Таро Теней — Значения карт. Аркан VIII «Вульгарная магия». UV чарты и отступы

Помимо классической колоды карт Таро существует еще множество других, ярких, красочных, современных и старинных, отличающихся точностью и легкостью восприятия, или же подходящих только для опытных тарологов.

Колода Теней карт Таро относится к категории оккультных. Она появилась на свет в 2003 году , благодаря талантливому сотрудничеству известного в России таролога Скляровой В.А. и художнику Васильченко О.

Эти карты уникальны и по своей характеристике и по стилистике. Гадание на картах Теней под силу знатокам карт Таро, начинать с этой колодой будет сложно.

Колода карт Теней Таро

Вера Склярова, автор колоды, опытный таролог, на счету которого несколько работ по технике гадания на картах Таро и изданных под ее авторством новых колод. Колода Теней позволяет точно ответить на волнующие вопросы, понять причины преследующих человека неудач, установить воздействие темных сил на судьбу человека.

Трактовка карт осуществляется несколько иначе чем в классическом варианте, поэтому не для каждой даже опытной гадалки эта колода подойдет в качестве советчика и друга. Среди современных колод Таро, карты Теней являются одной из самых популярных и интересных.

Автор вложил в карты весь свой опыт и знания в этой области. Колода состоит из 78 арканов и не делится по мастям, как это происходит в классическом варианте Таро. На самом деле, колоду карт Теней сложно сравнивать с классическим Таро.

Младшие арканы

По-большому счету карты Теней исполняют роль оракула. Это отличный инструмент для профессиональных гадалок при необходимости узнать о магическом воздействии. Деления на младшие арканы и старшие здесь не предусмотрено, так как колода состоит из 78 арканов , каждый из которых имеет свое значение и значимость.

Каждая карта в колоде имеет уникальное изображение и уникальное толкование. В колоде карт Таро многие арканов из колоды карт Теней вы найдете. Карты Таро Теней являются на сегодняшний день уникальной колодой, сразу же получившей популярность среди профессионалов.

Расклады на личность и ситуацию

Рассмотрим значение нескольких старших арканов, для того, чтобы понять, насколько похожи карты Теней на обычные карты Таро и в чем их очевидное отличие.

  • Карта “Сатана” – если расклад проводится на личность человека, то перед вами удачливый и привлекательный человек. Он может произвести приятное впечатление практически всегда. Он успешен и вызывает у окружающих уважение и зависть. Но это лишь внешнее проявление личности человека, на самом деле он вспыльчив и неуравновешен, может быть жестоким и проявлять приступы агрессии. Если карта выпадает в раскладе на ситуации, то она предупреждает вас об осторожности в отстаивании своей позиции. Возможно вы выступаете слишком жестко и вам стоит подумать о том, чтобы в чем-то уступить или согласиться на компромисс. Карта также может указывать на то, что над человеком был произведен обряд разрушения на что именно можно будет понять по другим картам в раскладе. Обряд может быть проведен на разрушение отношений, карьеры и даже здоровья. В раскладе на отношения карта говорит о том, что вы под властью более сильного партнера. Может также являться предвестником болезненного разрыва.
  • Аркан под номером два – Лилит. Эта карта обычно олицетворяет сильную женщину. Эта женщина умеет добиваться своего и в делах любовных и в делах материальных. Она красива, влиятельна, ее отличает сильный характер. Если карта выпадает мужчине, то она указывает на зависимость от женщины в любой возможной форме. Это может быть зависимость страстная от любимой женщины, может быть зависимость от материнской любви. В раскладе на отношения указывает на то, что доминирует в них женщина. В раскладе на дела и ситуацию карта обещает удачу и успех женщине, а вот для мужчины означает что ему пора стать жестче, его позиция слишком мягкая и не внятная.
  • Третья карта колоды Теней – это Геката . Эта карта также говорит о стремлении к независимости, но движет этим стремлением вовсе не сильный характер волевой женщины, а потворство своим желаниям и эгоизм. Часто карта может указывать на ветреность натуры. Олицетворяя женщину, карта указывает на ее привлекательность, но при этом говорит о ее несерьезности и стремлению к выгоде. Возможно, что алчность проявляется конкретно в данной ситуации. Если карта выпадает в раскладе мужчине, то это является предупреждением. В ближайшем будущем вам не стоит конкурировать или вступать в серьезный спор с особой женского пола. Вас ожидает проигрыш. Если карта выпадает женщине, то она предвещает победу и удачу. В раскладе на отношения карта говорит о потребительском отношении одного из партнеров к другому. Подчеркивает несерьезность связи и поверхностность чувств.
  • Четвертый аркан Люцифер предостерегает от поспешности в любых ситуациях. Если вы примете решение необдуманно, то рискуете иметь неприятные последствия. Лучше остановиться, потянуть время, подумать несколько раз, посоветоваться с близкими людьми и только затем сделать свой выбор. В спешке таится опасность. При раскладе на личность карта указывает на уравновешенного, логичного, твердого в своих убеждениях человека. Этот человек кажется сильным и надежным. Он способен контролировать свои эмоции, хотя по своей сути является человеком темпераментным. В раскладах на отношения может указывать на наличие серьезного покровителя, на то, что вторая половинка является для вас надежной опорой во всех делах и невзгодах.
  • Аркан под номером пять носит название “Ариман” . Аркан очень интересен в том плане, что носит магический отпечаток. Эта карта может выпадать в раскладе человеку, который в настоящий период своей жизни расплачивается за свое поведение. Иначе говоря, карта несет отпечаток возмездия, кары судьбы за неверные поступки или плохие черты характера. Но при этом, в сочетании с некоторыми картами может указывать на то, что на человека была наведена порча или был проведен обряд мести. В раскладах на личность эта карта выпадает тем людям, которые отличаются особенно низкими поступками или наделены низкими качествами характера. Карта указывает на подлость, лицемерие, агрессию, излишнюю подверженность страстям, азартным играм, эгоизм. В целом крайне негативный аркан. Он предвещает провал в любых начинаниях, проблемы в делах, появление болезни, финансовую несостоятельность. В раскладах на отношения говорит о болезненной любви или страсти, разрыве, потере любимого.
  • Шестой аркан тоже можно назвать удивительным и уникальным. Речь идет о карте “Ваалверит, Владыка соглашений” . Эта карта выпадает в раскладе на личность, указывая на крайнюю степень подавленности человека. Возможно, что человек подвержен суицидальным мыслям. Может говорить о недавней утрате близкого человека, крупной финансовой потере, близкой к банкротству. Карта выпадает в том случае, когда в сложившейся бедственной ситуации виновен сам человек.

Расклады для карт Теней Таро

Для карт Теней предусмотрены совершенно иные расклады нежели для классических Таро. Один из простых раскладов всего лишь на четыре карты при гадании на личность человека представляет собой следующее: первая карта говорит о хороших качествах человека, вторая карта указывает на отрицательные стороны его личности, третья карта раскрывает самую большую его тайну, а четвертая карта говорит о его страхах.

Расклад можно усложнить, например, добавить пятую карту, которая расскажет о том, что человек любит больше всего в жизни. Шестая карта может говорить о мечтах человека, а седьмая укажет на источник раздражения.

Можно заранее подготовить список вопросов , ответы на которые вам необходимо узнать и таким образом создать собственный расклад.

Оригинальный алгоритм теневых карт (shadow mapping) придумали уже достаточно давно. Принцип работы его заключается в следующем:
  1. Рисуем сцену в текстуру (теневую карту) из позиции источника света. Здесь важно отметить, что для разных типов источников света всё происходит немного по-разному.
    Направленные источники света (к таким в определённом приближении можно отнести солнечный свет) не имеют позиции в пространстве, однако для формирования карты теней эту позицию приходиться выбирать. Обычно её привязывают к положению наблюдателя, так чтобы в карту теней попадали объекты, находящиеся непосредственно в поле зрения наблюдателя. При рендеринге используют ортографическую проекцию .
    Проекционные источники света (лампы с непрозрачным абажуром, прожекторы) имеют определённое положение в пространстве и ограничивают распространение света определенными направлениями. При рендеринге карты теней в этом случае используется обычная перспективная проекционная матрица .
    Всенаправленные источники света (лампа накаливания, например) хоть и имеют определенное положение в пространстве, распространяют свет во всех направлениях. Чтобы корректно построить тени от такого источника света необходимо использовать кубические текстуры (cube maps), что, как правило, означает рисование сцены в карту теней 6 раз. Не всякая игра может позволить себе динамические тени от такого рода источников света, да и не всякой игре это необходимо. Если вам интересны принципы работы этого подхода, есть на эту тему.
    Кроме того, существует подкласс алгоритмов shadow mapping (LiSPSM , TSM , PSM и пр.), в которых используются нестандартные матрицы вида-проекции для улучшения качества теней и устранения недостатков оригинального подхода.
    Каким бы способом не формировалась карта теней, она неизменно содержит в себе расстояние от источника света до ближайшей видимой (из позиции источника света) точки или функцию от этого расстояния в более сложных разновидностях алгоритма.
  2. Рисуем сцену из основной камеры. Для того чтобы понять находится ли точка какого-либо объекта в тени, достаточно перевести координаты этой точки в пространство карты теней и произвести сравнение. Пространство карты теней определяется матрицей вида-проекции, которая использовалась при формировании этой карты. Переведя координаты точки объекта в это пространство и произведя преобразование координат из диапазона [-1;-1] в , получим текстурные координаты. Если полученные координаты получились вне диапазона , то эта точка не попала в карту теней, и её можно считать незатененной. Сделав выборку из карты теней по полученным текстурным координатам, мы получим расстояние между источником света и ближайшей к нему точкой какого-либо объекта. Если сравнить это расстояние с расстоянием между текущей точкой и источником света, то точка оказывается в тени, если значение в карте теней меньше. Это достаточно просто с логической точки зрения, если значение из карты теней меньше, значит, в этой точке есть какой-то объект, который находится ближе к источнику света, и мы находимся в его тени.
Shadow mapping на сегодняшний день является, наверное, самым распространенным алгоритмом для рендеринга динамических теней. Реализацию той или иной модификации алгоритма можно найти практически в любом графическом движке. Главным достоинством этого алгоритма является то, что он обеспечивает быстрое формирование теней от сколь угодно геометрически сложных объектов. Вместе с тем, существование широкого спектра вариаций алгоритма объясняется во многом его недостатками, которые могут приводить к очень неприятным графическим артефактам. Проблемы, характерные для PPSM, и пути их преодоления будут рассмотрены ниже.

Parallel-Split Shadow Mapping

Рассмотрим следующую задачу: необходимо рисовать динамические тени от объектов, находящихся на значительном удалении от игрока без ущерба для теней от близко расположенных объектов. Ограничимся направленным солнечным светом.
Задача такого рода может быть особенно актуальна в outdoor-играх, где в некоторых ситуациях игрок может видеть ландшафт на сотни метров перед собой. При этом, чем дальше мы хотим видеть тень, тем большее пространство должно попадать в теневую карту. Чтобы сохранить должное разрешение объектов в теневой карте, мы вынуждены увеличивать разрешение самой карты, что сначала приводит к снижению производительности, затем мы упираемся в ограничение на максимальный размер render target’а. В итоге, балансируя между производительностью и качеством тени, мы получим тени с хорошо заметным эффектом алиасинга, который плохо маскируется даже размытием. Понятно, что такое решение нас не может удовлетворить.
Для решения данной проблемы мы можем придумать такую матрицу проекции, чтобы близко расположенные к игроку объекты получали в карте теней площадь больше, чем объекты, которые расположены далеко. В этом заключается основная идея алгоритма Perspective Shadow Mapping (PSM) и ряда других алгоритмов. Главным преимуществом такого подхода является тот факт, что мы практически не изменили процесс рендеринга сцены, изменился лишь способ расчёта матрицы вида-проекции. Такой подход может быть легко встроен в существующую игру или движок без необходимости серьезных доработок последних. Главный недостаток такого рода подходов – граничные условия. Представим себе ситуацию, что мы рисуем тени от Солнца на закате. Когда Солнце приближается к горизонту, объекты в теневой карте начинают сильно перекрывать друг друга. В этом случае нетипичная проекционная матрица может усугубить ситуацию. Иными словами, алгоритмы класса PSM неплохо работают в определённых ситуациях, например, когда в игре рисуются тени от «неподвижного Солнца» близкого к зениту.
Принципиально другой подход предлагается в алгоритме PSSM. Некоторым данный алгоритм может быть известен под названием Cascaded Shadow Mapping (CSM). Формально, это разные алгоритмы, я бы даже сказал, что PSSM является частным случаем CSM. В этом алгоритме предлагается разделить пирамиду видимости (frustum) основной камеры на сегменты. В случае PSSM – с границами параллельными ближней и дальней плоскостям отсечения, в случае CSM – вид разделения жестко не регламентирован. Для каждого сегмента (split в терминологии алгоритма) строится своя теневая карта. Пример разделения приведен на рисунке ниже.


На рисунке можно видеть разбиение пирамиды видимости на 3 сегмента. Каждый из сегментов выделен ограничивающим прямоугольником (в трёхмерном пространстве будет параллелепипед, bounding box). Для каждой из этих ограниченных частей пространства будет строиться своя теневая карта. Внимательный читатель обратит внимание, что здесь я использовал выравненные по осям ограничивающие параллелепипеды. Можно использовать и невыравненные, это добавит дополнительную сложность в алгоритм отсечения объектов и несколько изменит способ формирования матрицы вида из позиции источника света. Так как пирамида видимости расширяется, площадь сегментов более близких к камере может быть существенно меньше площади более дальних. При одинаковом разрешении теневых карт это означает большее разрешение для тени от близко расположенных объектов. В упомянутой выше статье в GPU Gems 3 предложена следующая схема для вычисления расстояний разбиения пирамиды видимости:



где i – индекс разбиения, m – количество разбиений, n – расстояние до ближней плоскости отсечения, f – расстояние до дальней плоскости отсечения, λ – коэффициент, определяющий интерполяцию между логарифмической и равномерной шкалой разбиения.

Общее в реализации
Алгоритм PSSM в реализации на Direct3D 11 и OpenGL имеет много общего. Для реализации алгоритма необходимо подготовить следующее:
  1. Несколько теневых карт (по числу разбиений). На первый взгляд, кажется, что для получения нескольких теневых карт необходимо нарисовать объекты несколько раз. На самом деле, делать это явным образом не обязательно, мы воспользуемся механизмом аппаратного инстансинга. Для этого нам потребуется так называемый массив текстур для рендеринга и простой геометрический шейдер.
  2. Механизм отсечения объектов. Объекты игрового мира могут быть разной геометрической формы и иметь разное положение в пространстве. Протяженные объекты могут быть видны в нескольких теневых картах, небольшие объекты – только в одной. Объект может оказаться прямо на границе соседних сегментов и должен быть нарисован минимум в 2 теневые карты. Таким образом, необходим механизм для определения, в какое подмножество теневых карт попадает тот или иной объект.
  3. Механизм для определения оптимального числа разбиений. Рендеринг теневых карт для каждого сегмента на каждый кадр может быть излишней тратой вычислительных ресурсов. Во многих ситуациях игрок видит перед собой лишь небольшой участок игрового мира (например, он смотрит себе под ноги, или его взгляд уперся в стену перед собой). Понятно, что это сильно зависит от вида обзора в игре, но иметь такую оптимизацию было бы неплохо.
В итоге, получим следующий алгоритм формирования матриц вида-проекции для рендеринга карт теней:
  1. Вычисляем расстояния для разбиения пирамиды видимости для наихудшего случая. Наихудший случай здесь – мы видим тени до дальней плоскости отсечения камеры.

    Код

    void calculateMaxSplitDistances() { float nearPlane = m_camera.getInternalCamera().GetNearPlane(); float farPlane = m_camera.getInternalCamera().GetFarPlane(); for (int i = 1; i < m_splitCount; i++) { float f = (float)i / (float)m_splitCount; float l = nearPlane * pow(farPlane / nearPlane, f); float u = nearPlane + (farPlane - nearPlane) * f; m_maxSplitDistances = l * m_splitLambda + u * (1.0f - m_splitLambda); } m_farPlane = farPlane + m_splitShift; }

  2. Определяем расстояние между камерой и наиболее удаленной видимой точкой объекта, отбрасывающего тень. Здесь важно отметить, что объекты могут отбрасывать и не отбрасывать тени. Например, равнинно-холмистый ландшафт можно сделать не отбрасывающим тени, за затенение в этом случае может отвечать алгоритм освещения. В карту теней будут рисоваться только отбрасывающие тени объекты.

    Код

    float calculateFurthestPointInCamera(const matrix44& cameraView) { bbox3 scenebox; scenebox.begin_extend(); for (size_t i = 0; i < m_entitiesData.size(); i++) { if (m_entitiesData[i].isShadowCaster) { bbox3 b = m_entitiesData[i].geometry.lock()->getBoundingBox(); b.transform(m_entitiesData[i].model); scenebox.extend(b); } } scenebox.end_extend(); float maxZ = m_camera.getInternalCamera().GetNearPlane(); for (int i = 0; i < 8; i++) { vector3 corner = scenebox.corner_point(i); float z = -cameraView.transform_coord(corner).z; if (z > maxZ) maxZ = z; } return std::min(maxZ, m_farPlane); }

  3. На основе полученных на шагах 1 и 2 значений определяем количество сегментов, которое нам действительно необходимо и расстояния разбиения для них.

    Код

    void calculateSplitDistances() { // calculate how many shadow maps do we really need m_currentSplitCount = 1; if (!m_maxSplitDistances.empty()) { for (size_t i = 0; i < m_maxSplitDistances.size(); i++) { float d = m_maxSplitDistances[i] - m_splitShift; if (m_furthestPointInCamera >= d) m_currentSplitCount++; } } float nearPlane = m_camera.getInternalCamera().GetNearPlane(); for (int i = 0; i < m_currentSplitCount; i++) { float f = (float)i / (float)m_currentSplitCount; float l = nearPlane * pow(m_furthestPointInCamera / nearPlane, f); float u = nearPlane + (m_furthestPointInCamera - nearPlane) * f; m_splitDistances[i] = l * m_splitLambda + u * (1.0f - m_splitLambda); } m_splitDistances = nearPlane; m_splitDistances = m_furthestPointInCamera; }

  4. Для каждого сегмента (границы сегмента определяются ближним и дальним расстояниями) вычисляем ограничивающий параллелепипед.

    Код

    bbox3 calculateFrustumBox(float nearPlane, float farPlane) { vector3 eye = m_camera.getPosition(); vector3 vZ = m_camera.getOrientation().z_direction(); vector3 vX = m_camera.getOrientation().x_direction(); vector3 vY = m_camera.getOrientation().y_direction(); float fov = n_deg2rad(m_camera.getInternalCamera().GetAngleOfView()); float aspect = m_camera.getInternalCamera().GetAspectRatio(); float nearPlaneHeight = n_tan(fov * 0.5f) * nearPlane; float nearPlaneWidth = nearPlaneHeight * aspect; float farPlaneHeight = n_tan(fov * 0.5f) * farPlane; float farPlaneWidth = farPlaneHeight * aspect; vector3 nearPlaneCenter = eye + vZ * nearPlane; vector3 farPlaneCenter = eye + vZ * farPlane; bbox3 box; box.begin_extend(); box.extend(vector3(nearPlaneCenter - vX * nearPlaneWidth - vY * nearPlaneHeight)); box.extend(vector3(nearPlaneCenter - vX * nearPlaneWidth + vY * nearPlaneHeight)); box.extend(vector3(nearPlaneCenter + vX * nearPlaneWidth + vY * nearPlaneHeight)); box.extend(vector3(nearPlaneCenter + vX * nearPlaneWidth - vY * nearPlaneHeight)); box.extend(vector3(farPlaneCenter - vX * farPlaneWidth - vY * farPlaneHeight)); box.extend(vector3(farPlaneCenter - vX * farPlaneWidth + vY * farPlaneHeight)); box.extend(vector3(farPlaneCenter + vX * farPlaneWidth + vY * farPlaneHeight)); box.extend(vector3(farPlaneCenter + vX * farPlaneWidth - vY * farPlaneHeight)); box.end_extend(); return box; }

  5. Вычисляем теневую матрицу вида-проекции для каждого сегмента.

    Код

    matrix44 calculateShadowViewProjection(const bbox3& frustumBox) { const float LIGHT_SOURCE_HEIGHT = 500.0f; vector3 viewDir = m_camera.getOrientation().z_direction(); vector3 size = frustumBox.size(); vector3 center = frustumBox.center() - viewDir * m_splitShift; center.y = 0; auto lightSource = m_lightManager.getLightSource(0); vector3 lightDir = lightSource.orientation.z_direction(); matrix44 shadowView; shadowView.pos_component() = center - lightDir * LIGHT_SOURCE_HEIGHT; shadowView.lookatRh(shadowView.pos_component() + lightDir, lightSource.orientation.y_direction()); shadowView.invert_simple(); matrix44 shadowProj; float d = std::max(size.x, size.z); shadowProj.orthoRh(d, d, 0.1f, 2000.0f); return shadowView * shadowProj; }

Отсечение объектов реализуем при помощи простого теста на пересечение двух ограничивающих параллелепипедов (объекта и сегмента пирамиды видимости). Здесь есть одна особенность, которую важно учесть. Мы можем не видеть объект, но видеть тень от него. Нетрудно догадаться, что при описанном выше подходе мы отсечём все объекты, которые не видны в основной камере, и теней от них не будет. Чтобы этого не происходило, я использовал довольно распространенный приём – вытянул ограничивающий параллелепипед объекта вдоль направления распространения света, что дало грубое приближение области пространства, в которой видна тень от объекта. В итоге, для каждого объекта был сформирован массив индексов теневых карт, в которые необходимо рисовать этот объект.

Код

void updateShadowVisibilityMask(const bbox3& frustumBox, const std::shared_ptr& entity, EntityData& entityData, int splitIndex) { bbox3 b = entity->getBoundingBox(); b.transform(entityData.model); // shadow box computation auto lightSource = m_lightManager.getLightSource(0); vector3 lightDir = lightSource.orientation.z_direction(); float shadowBoxL = fabs(lightDir.z) < 1e-5 ? 1000.0f: (b.size().y / -lightDir.z); bbox3 shadowBox; shadowBox.begin_extend(); for (int i = 0; i < 8; i++) { shadowBox.extend(b.corner_point(i)); shadowBox.extend(b.corner_point(i) + lightDir * shadowBoxL); } shadowBox.end_extend(); if (frustumBox.clipstatus(shadowBox) != bbox3::Outside) { int i = entityData.shadowInstancesCount; entityData.shadowIndices[i] = splitIndex; entityData.shadowInstancesCount++; } }


Теперь рассмотрим процесс рендеринга и специфичные для Direct3D 11 и OpenGL 4.3 части.
Реализация на Direct3D 11
Для реализации алгоритма на Direct3D 11 нам понадобятся:
  1. Массив текстур для рендеринга теневых карт. Для создания такого рода объекта в структуре D3D11_TEXTURE2D_DESC есть поле ArraySize . Таким образом, в коде на C++ у нас не будет ничего похожего на ID3D11Texture2D* array[N] . С точки зрения Direct3D API массив текстур слабо отличается от единственной текстуры. Важной особенностью при использовании такого массива в шейдере является то, что мы можем определить, в какую именно текстуру в массиве будем рисовать тот или иной объект (семантика SV_RenderTargetArrayIndex в HLSL). В этом заключается и главное отличие этого подхода от MRT (multiple render targets), при котором один объект рисуется сразу во все заданные текстуры. Для объектов, которые необходимо нарисовать сразу в несколько теневых карт, мы воспользуемся аппаратным инстансингом, позволяющим клонировать объекты на уровне GPU. При этом объект может быть нарисован в одну текстуру в массиве, а его клоны в другие. В картах теней мы будем хранить только значение глубины, поэтому воспользуемся текстурным форматом DXGI_FORMAT_R32_FLOAT .
  2. Специальный текстурный сэмплер. В Direct3D API можно задать специальные параметры для выборки из текстуры, которые позволят производить сравнение значения в текстуре с заданным числом. Результатом в этом случае будет 0 или 1, а переход между этими значениями может быть сглажен линейным или анизотропным фильтром. Для создания сэмплера в структуре D3D11_SAMPLER_DESC зададим следующие параметры:

    SamplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT; samplerDesc.ComparisonFunc = D3D11_COMPARISON_LESS; samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; samplerDesc.BorderColor = 1.0f; samplerDesc.BorderColor = 1.0f; samplerDesc.BorderColor = 1.0f; samplerDesc.BorderColor = 1.0f;
    Таким образом, у нас будет билинейная фильтрация, сравнение функцией «меньше», а выборка из текстуры по координатам вне диапазона вернет 1 (т.е. отсутствие тени).

Рендеринг будем осуществлять по следующей схеме:

Реализация на OpenGL 4.3
Для реализации алгоритма на OpenGL 4.3 нам понадобится все то же самое, что и для Direct3D 11, однако есть тонкости. В OpenGL мы можем делать совмещённую со сравнением выборку только для текстур, содержащих значение глубины (например, в формате GL_DEPTH_COMPONENT32F). Следовательно, рендеринг мы будем осуществлять только в буфер глубины, а запись в цвет уберём (точнее, привяжем к framebuffer’у только массив текстур для хранения буфера глубины). Это, с одной стороны, немного сэкономит нам видеопамять и облегчит графический конвейер, с другой, вынудит работать с нормализованными значениями глубины.
Параметры выборки в OpenGL можно привязать прямо к текстуре. Они будут идентичны тем, что были рассмотрены ранее для Direct3D 11.

Const float BORDER_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; glBindTexture(m_shadowMap->getTargetType(), m_shadowMap->getDepthBuffer()); glTexParameteri(m_shadowMap->getTargetType(), GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(m_shadowMap->getTargetType(), GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(m_shadowMap->getTargetType(), GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(m_shadowMap->getTargetType(), GL_TEXTURE_COMPARE_FUNC, GL_LESS); glTexParameteri(m_shadowMap->getTargetType(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(m_shadowMap->getTargetType(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameterfv(m_shadowMap->getTargetType(), GL_TEXTURE_BORDER_COLOR, BORDER_COLOR); glBindTexture(m_shadowMap->getTargetType(), 0);
Интересен процесс создания массива текстур, который внутри OpenGL представлен трёхмерной текстурой. Для его создания не сделали специальной функции, и то и другое создается при помощи glTexStorage3D . Аналогом SV_RenderTargetArrayIndex в GLSL является встроенная переменная gl_Layer .
Схема рендеринга также осталась прежней:

Проблемы

Проблем у алгоритма shadow mapping и его модификаций много. Зачастую алгоритм приходится тщательно настраивать под конкретную игру или даже конкретную сцену. Список наиболее частых проблем и путей их решения можно найти . При реализации PSSM я столкнулся со следующим:

Производительность

Замеры производительности велись на компьютере следующей конфигурации: AMD Phenom II X4 970 3.79GHz, 16Gb RAM, AMD Radeon HD 7700 Series, под управлением ОС Windows 8.1.

Среднее время кадра. Direct3D 11 / 1920x1080 / MSAA 8x / полный экран / маленькая сцена (~12к полигонов в кадре, ~20 объектов)

Среднее время кадра. OpenGL 4.3 / 1920x1080 / MSAA 8x / полный экран / маленькая сцена (~12к полигонов в кадре, ~20 объектов)

Среднее время кадра. 4 разбиения / 1920x1080 / MSAA 8x / полный экран / большая сцена (~1000к полигонов в кадре, ~1000 объектов, ~500 инстансов объектов)

Результаты показали, что на больших и малых сценах реализация на OpenGL 4.3 работает, в целом, быстрее. С увеличением нагрузки на графический конвейер (увеличение количества объектов и их инстансов, увеличение размера карт теней) разница по скорости работы между реализациями сокращается. Преимущество реализации на OpenGL я связываю с отличным от Direct3D 11 способом формирования карты теней (мы использовали только буфер глубины без записи в цвет). Ничего нам не мешает сделать то же самое на Direct3D 11, смирившись при этом с использованием нормализованных значений глубины. Однако такой подход будет работать только до тех пор, пока мы не захотим хранить в карте теней какие дополнительные данные или функцию от значения глубины вместо значения глубины. И некоторые улучшения алгоритма (например, Variance Shadow Mapping) окажутся для нас сложно реализуемыми.

Выводы

Алгоритм PSSM является одним их самых удачных способов для создания теней на больших открытых пространствах. В его основе лежит простой и понятный принцип разбиения, который можно легко масштабировать, увеличивая или уменьшая качество теней. Данный алгоритм можно объединять с другими алгоритмами shadow mapping для получения более красивых мягких теней или физически более правильных. Вместе с тем, алгоритмы класса shadow mapping часто приводят к появлению неприятных графических артефактов, которые необходимо устранять путем тонкой настройки алгоритма под конкретную игру.

Теги:

  • Shadow mapping
  • PSSM
  • Direct3D 11
  • OpenGL 4
Добавить метки

Карта теней вероятно, самая трудная часть в создания визуального представления объекта. Мы используем их, чтобы получить запеченные свет и тень.
Они должны быть однозначно развернуты, так что каждая часть модели имеет свое собственное место в UV пространстве, чтобы в итоге получать корректную информацию о свете и тени.
Важно помнить, что разрешение карты теней крошечное по сравнению с размером UV пространства.
Важно также понимать, что чем больше уровень должен быть оптимизирован тем ниже дизайнер уровней должен использовать разрешение на лайтмапах, иногда доходя до 8 на 8 или 16 на 16 в случае с более мелкими объектами.
Эта тенденция требует, чтобы мы оставили много дополнительного пространства вокруг каждой секции из развертки объекта так, чтобы области, которые являются темными,
не воздействовали на светлые и не уничтожали иллюзию визуальной корректности теней в игре.

Есть 3 основных способа создания такой развертки:

BOX UNWRAP

Часто это самый надежный метод создания развертки объекта, поскольку большинство моделей окружающей среды близки по форме к блокам, которые объединены в некую структуру.
Непрерывный меш (меш в которой нет отстоящих от основной частей) часто бывает очень полезным решением при построении развертки,
это решение поможет обеспечить более эффективное распределение сетки геометрии в UV пространстве.
Это так же будет хорошо работать даже при низком разрешении карты теней, поскольку тогда она образует единый градиент от темного к светлому.
В отличии от фрагментированной развертки где результат покажется более неоднозначным и может потребоваться повысить разрешение карты теней для парирования эффекта резких переходов.
Мы должны стараться избегать этого там где мы можем. К сожалению иногда нет возможности использовать пониженное разрешение или единую развертку геометрии.

PLANAR UNWRAP

Этот способ особенно полезен для плоских конструкций, таких как стены с несколькими фасками или выдавливаниями. Это также очень полезно для больших частей фасадов зданий, таких как, например, многоквартирные дома.
Planar будет разворачивать значительно лучше если использовать неразрывную геометрию, потому что здесь вопрос будет стоять лишь в “расслаблении” сетки развертки.
Иногда так же хорошее правило заключается в том, чтобы убедиться, что на подобной развертке больше места по горизонтали, чем по вертикали, так как отбрасывание теней, как правило, происходит со стороны при слегка повышенном угле,
а не прямо вниз. Так, большее горизонтальное пространство даёт более широкие возможности для построения более резкой тени, из-за тенденции выбора дизайнерами освещения под углом,
чтобы создать более интересные тени, чем при освещении сверху вниз.

CYLINDRICAL UNWRAP

Большинство других форм можно представить как вариации цилиндрической формы, если конечно они не близки к параллелепипедам или плоскостям.
Цилиндрическую развертку хорошо использовать для многих конструкций которые имеют переднюю и боковые части, но не имеют задней, иначе мы бы использовали метод BOX UNWRAP .

Примеры

Это был неразрывный меш так что его легко было развернуть при помощи BOX UNWRAP и просто разложить его горизонтально, чтобы использовать как можно больше пространства карты теней.
Нижние поверхности, которые могли бы быть видны на среднем изображении, удалены, так как они почти всегда будут черными,
и если бы они были связаны с остальной разверткой, тени от них просто бы просачивались темными пятнами на стены там, где этого не должно быть. Это же верно и для верхних граней.
За исключением того что они всегда были бы светлыми.


Этот метод развертки позволяет нам иметь почти идеальную карту теней в игре с разрешением 32 на 32. У геометрии нет никаких швов. Там где должна быть тень мы видим тонкие черные линии, а где не должна, там её и нет.


Здесь мы видим что необходимо использовать максимально возможное пространство, так как карта теней в любом случае покроет всю развертку.
По этому между соотношением сторон объекта 1 к 1 и 1 к 7 вы увидите сещественную разницу. Так же вы видите что здесь отделены некоторые части развертки и отодвинуты от основной сетки.
Это сделано потому как эти части всегда останутся в тени. Они не должны воздействовать на остальную карту теней.


Даже на больших фасадах как этот, Planar показывает хороший результат. Этот меш является неразрывным, что помогает нашей работе,
но в данном случае всё работало бы так же даже если бы развертка была разделена на несколько вертикальных или горизонтальных полос, хотя и потребовалось сделать небольшие отступы между ними.


Вы можете видеть, что здесь, плотно прилегающая геометрия позволила легко уложить UV. Так же вы видите что здесь сделаны отступы между частями развертки что бы темные области не воздействовали на светлые.
Чем меньше разрешение карты, тем больше необходимо брать отступ.


Вы можете увидеть некоторые довольно агрессивные искажения на пересекающихся вертикальных частях которые удерживают перила вместе.
Вы видите что центральная опрора разделена на две части вместо трех, как если бы мы разрезали её по краям центральной части. Это сделано для того что бы сократить количество швов и обеспечить плавное освещение на большей площади.


У некоторых проектов не выходит следовать этим простым правилам, как на скриншоте ниже.


Когда имеется так много отдельных элементов у нас нет иного выбора, кроме как повысить разрешение текстур иначе мы бы тратили очень много места на отступах между элементами развертки, это бы выглядело ужасно в игре.
Так что разрешение карт теней было поднято до 128 на 128, однако это всё еще не выглядит безупречно, но всё же не на столько что бы полностью разрушить визуальный образ объекта в игре.


Иногда развернуть объект легко, достаточно разбить его на несколько обоснованных частей. А затем просто “расслабить” развертку. Отличный пример объект ниже.


Такая конструкция является по существу цилиндром с плоским основанием поэтому здесь используются эти два основных метода развертки объекта.
Planar разворачивает части геометрии вниз по оси Z, а затем применяется модификатор “расслабления” и немного регулируется положение вершин чтобы убедиться, что ничего не получает слишком мало покрытия.
В середине схожий с основанием случай, здесь центральная часть разделена и использован Planar вместо Cylindrical для того что бы обеспечить большую зону покрытия.
Как всегда, мы больше озабочены охватом, чем чем соотношением сторон 1 на 1. Большим преимуществом будет располагать швы в их реальных местах, это позволит выглядеть теням более естественно.
Если на вашем объекте есть глубокие вырезы, крайне резкие стыки геометрии то это отличное место что бы здесь проложить шов, если конечно он требуется.

Lightmap Coordinates Index

По умолчанию, первый набор UV (индекс 0) статик меша будет использоваться при создании карты теней для статического освещения.
Это означает, что один и тот же набор координат, который используется для нанесения материалов на меш, так же будет использован для статического освещения.
Этот метод довольно часто не является идеальным. Одной из причин этого является то, что UV, используемые для генерации карты теней должны быть уникальными,
а это означает, что каждая грань сетки не должна перекрывать любую другую поверхность в UV пространстве. Причина этого достаточно очевидна: если фейсы перекрывают друг друга на UV развертке,
часть карты теней, соответствующая этому пространству будет применяться к обеим граням. Это приведет к неправильному освещению, появлению теней там где их в принципе быть не должно.
Статик меши имеют свойство LightmapCoordinateIndex , которое позволяет использовать заданную UV развертку под карту теней. Установите это свойство, чтобы указать на набор UV, который правильно настроен для освещения.

UV чарты и отступы

Группы обособленных треугольников с сопредельными UVs называются UV чарты (charts).

Разделять развертку на чарты и располагать их отдельно следует если вы хотите исключить воздействие теней одного чарта на другой. Так же делая отступ следует помнить о простом правиле:
Размер отступа должен быть больше чем 4х4 текселя, так как DXT сжатие работает с блоками именно такого размера.

  1. Потраченный впустую отступ
  2. Необходимый отступ

Это означает, что для карты теней разрешением 32, отступы между частями UV развертки должны быть 12,5% от всего UV-пространства.
Однако, имейте в виду, что использование слишком больших отступов между частями UV развертки приведет к трате памяти, отведенной на карту теней, впустую на более высоких разрешениях.
Чем ближе ближе вы сможете расположить UV чарты, тем лучше. Это позволит уменьшить количество растраченной впустую памяти.


Это далеко не идеальная развертка.

Одним из примеров проблем с разверткой служит чрезмерная фрагментация. Вы видите как тени что должны оставаться на внутренних частях объекта дают затенение и на внешние грани.
Другой потенциальный ловушкой полагается на автоматический разворачивать, так как это тоже может привести к тем же проблемам.


Лучший способ создания развертки для карты теней является моделирование всего меша как одного непрерывного элемента или создание развертки вручную.


Это даст единую развертку, на которой почти нет швов и которая гораздо более эффективна.

Конечным результатом является меш, который освещается должным образом без каких — либо артефактов.


Дополнительным преимуществом этого метода является то, что он также обычно сокращает число вершин и треугольников, необходимых для данной модели.


Кроме метода теневых объемов (требующего предварительной обработки геометрии и работы в трехмерном пространстве) есть еще один довольно простой метод для получения теней - использование так называемых теневых карт ().

Данный метод работает не в исходном трехмерном пространстве, а в картинной плоскости (и поэтому ему свойственны ошибки дискретизации - aliasing artifacts ), но зато он крайне прост, не требует специальной обработки геометрии сцены и может использовать альфа-канал текстуры для создания "дырок".

В основе этого метода лежит крайне простая идея, что освещенные фрагменты - это те фрагменты, которые видны из положения источника света.

Поэтому, если мы расположим камеру в положении источника света и отрендерим сцену при помощи этой камеры, то мы получим все освещенные фрагменты.

Видимость фрагмента источнику света в терминах буфера глубины означает, что данный фрагмент успешно проходит тест глубины при рендеринге из положения источника света.

Рис 1. Рендеринг из положения источника света.

Если после рендеринга сцены из положения источника света взять получившийся при этом буфер глубины (все остальное можно просто отбросить) и поместить его в специальную текстуру, то такая текстура может использоваться для проверки видимости фрагментов из положения источника света.

Подобно тому, как это делалось при построении отражения в текстуру, можно подобрать такой способ вычисления текстурных координат (s,t,r) , при котором первые две компоненты (s,t) определяют проекцию соответствующего фрагмента на картинную плоскость при рендеринге из положения источника света (т.е. являются индексом в текстуру глубины), а третья компонента r является глубиной фрагмента относительно источника света.

Тогда для определения видимости источником света данного фрагмента достаточно просто сравнить значение глубины depthMap(s,t) и r . Если первая величина больше или равна второй, то данный фрагмент проходит тест глубины для источника света и, следовательно, освещен.

При этом текстурные координаты (s,t,r) фактически являются координатами фрагмента в системе координат камеры, использованной для рендеринга из положения источника света (с точностью до отображения куба [-1,1] 3 в 3).

Поэтому данные текстурные координаты могут быть получены путем умножения пространственных координат на произведение модельной и проектирующей матриц, использованных при рендеринге из положения источника света. Как и при рендеринге отражения в текстуру необходимо домножение этого произведения на матрицу преобразования, переводящую куб [-1,1] 3 в 3).

Рис 2. Координаты в относительно источника света.

Рассмотрим, что необходимо для практической реализации данного метода.

Во-первых, нужен специальный формат текстуры для хранения карты глубины (обладающий достаточной точностью).

Во-вторых, необходим быстрый способ вычисления depthMap(s,t)<=r (хотя это может быть легко реализовано при помощи фрагментной программы).

Первая возможность предоставляется расширением ARB_depth_texture (или SGIX_depth_texture, расширением которого оно является).

Данное расширение вводит новый формат текстуры (для использования в командах glGetTexImage, glTexImage1D, glTexImage2D, glTexSubImage1D и glTexSubImage2D) - GL_DEPTH_COMPONENT.

Также данное расширение вводит несколько внутренних форматов для функций glTexImage1D, glTexImage2D, glCopyTexImage1D и glCopyTexImage2D - GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT24_ARB и GL_DEPTH_COMPONENT32_ARB.

Таким образом, использование данного расширения легко позволяет переносить содержимое буфера глубины в текстуру (с достаточной точностью) и работать с ней в дальнейшем.

Если к такой текстуре происходит обращение, подразумевающее получение цветового значения, то текстура трактуется как текстура типа GL_LUMINANCE.

Вторая возможность предоставляется расширением ARB_shadow (или SGIX_shadow).

Данное расширение вводит новые значения для параметра pname функций glTexParamerteri, glTexParameterf, glTexParameteriv и glTexParameterfv - GL_TEXTURE_COMPARE_MODE_ARB и GL_TEXTURE_COMPARE_FUNC_ARB.

Также вводится константа GL_COMPARE_R_TO_TEXTURE_ARB, которое может быть использовано в качестве параметра param у функций glTexParamerteri и glTexParameterf при значении pname равном GL_TEXTURE_COMPARE_MODE_ARB.

Среди стандартных источников света три источника (а именно Spot, Direct и Omni) позволяют нам выбрать тип просчитываемых теней. Если мы используем штатный стандартный визуализатор Default Scanline Renderer (DSR), то интерес для нас будут представлять: Advanced ray-traced shadows, Area shadows, Ray-traced shadows, Shadow maps.

При выборе типа тени среди свитков параметров ИС появляется свиток параметров тени, название которого начинается с наименования типа.

Shadow Map

Самый простой и нетребовательный к расчетным ресурсам тип теней.

  1. Размер карты, на основе которой расчитывается тень. Чем больше карта, тем качественнее расчитываемая тень. Лучше использовать числа порядка 2 n
  2. Размытие кромки тени. Увеличение параметра позволяет избавиться от зубчатого края кромки при низком разрешении карты
  3. Параметр, отвечающий за управление значением Bias. По умолчанию отключено (наилучший результат в большинстве случаев). В случае с анимацией может помочь включение опции
  4. Если выключено, то свет проходит через поверхность, если натыкается на полигоны, обращенные от него нормалями. Включение опции позволяет получить корректные тени

На Рис.1 верхний ряд изображений наглядно показывает изменение качества тени при увеличении параметра Size. Даже значительное увеличение размера карты не решает проблемы зубцов на краях тени, хотя безусловно рисунок тени становится более проработанным.

Во втором ряду во всех трех случаях размер карты остается одинаковым, но меняется параметр Sample Range. Постепенно увеличивая его, мы избавились от зубчатости, размыв кромку тени.

Рис.1 Изменение качества тени типа Shadow Map при различных параметрах

Ray-Traced Shadows

Тени этого типа расчитываются на основе алгоритма трассировки. Имеют четкие края и практически не поддаются настройке.

Тень Ray-Traced Shadow более точная относительно Shadow Map. К тому же она способны учитывать прозрачность объекта, но тем временем «сухая» и четкая, что смотрится не очень естественно в большинстве случаев. Ray-Traced Shadow более требовательна к ресурсам компьютера, чем Shadow Map.

  1. Расстояние объекта от отбрасываемой тени
  2. Глубина трасировки – параметр, отвечающий за проработку тени. Увеличение этого значения может значительно увеличить время визуализации

Ray-Traced Shadows с ИС типа Omni займут больше времени на визуализацию чем связка Ray-Traced Shadows + Spot (или Directional)

Рис.2 Тени Ray-Traced Shadows от непрозрачного и прозрачного объектов

Advanced Ray-Traced Shadows

Тени этого типа очень похожи на тени Ray-Traced Shadows, но как понятно из названия имеют более продвинутые настройки, позволяющие получать более естественные и корректные расчеты.

  1. Метод генерации тени
    Simple – одиночный луч выходит из ИС. Тень не поддерживает какого-либо сглаживания и настройки качества
    1-Pass Antialias – иммитируется испускание пучка лучей. Причем такое же кол-во лучей отражается от каждой освещенной поверхности (Кол-во лучей регулируется Shadow Quality).
    2-Pass Antialias – Аналогично, но испускается два пучка лучей.
  2. Если выключено, то свет проходит через поверхность, если натыкается на полигоны обращенные от него нормалями. Включение опции позволяет получить корректные тени
  3. Количество лучей испускаемых освещенной поверхностью
  4. Количество вторичных лучей испускаемых освещенной поверхностью
  5. Радиус (в пикселях) размытия кромки тени. Увеличение параметра повышает качество размытия. Если приразмытии кромки потерялись мелкие детали, скорректируйте сей казус увеличением Shadow Integrity
  6. Расстояние объекта от отбрасываемой тени
  7. Параметр, управляющий хаотичностью лучей. Изначально лучи направляются по строгой сетке, что может вызвать неприятные артефакты. Внесение хаоса сделает изображение тени более естественным
    Рекомендуемые значения 0,5-1,0. Но более размытые тени потребуют большего значения Jitter Amount

Area Shadows

Этот тип теней позволяет учитывать габариты источника света, благодаря чему можно получать естественные протяженные тени, которые «расщепляются» и размываются при удалении от объекта. Такие тени 3dsMax получает путем смешения ряда «сэмплов» (образцов) теней. Чем больше «сэмплов» и лучше смешение, тем качественнее просчитанная тень.

  1. Форма мнимого источника света, позволяющая определить характер тени.
    Simple – одиночный луч выходит из ИС. Тень не поддерживает какого-либо сглаживания и настройки качества.
    Rectangle Ligh t – иммитируется испускание света с прямоугольной области.
    Disc Light – ИС ведет себя так, как если бы он приобрел форму диска.
    Box Light – имитация кубического ИС.
    Sphere Ligh t – имитация сферического ИС.
  2. Если выключено, то свет проходит через поверхность, если натыкается на полигоны, обращенные от него нормалями. Включение опции позволяет получить корректные тени.
  3. Управляет количеством испускаемых лучей (нелинейно). Чем выше число, тем больше лучей, тем выше качество тени.
  4. Параметр, отвечающий за качество тени. Для рационального расчета, всегда устанавливайте число выше, чем Shadow Integrity.
  5. Радиус (в пикселях) размытия кромки тени. Увеличение параметра повышает качество размытия. Если приразмытии кромки потерялись мелкие детали, скорректируйте сей казус увеличением Shadow Integrity.
  6. Расстояние объекта от отбрасываемой тени.
  7. Параметр, управляющий хаотичностью лучей. Изначально лучи направляются по строгой сетке, что может вызвать неприятные артефакты. Внесение хаоса сделает изображение тени более естественным.
  8. Габариты мнимого источника. Length - длина, Width – ширина, Height (активна только для Box Light и Sphere Light) - высота.

Взглянем на Рис.3. На первом фрагменте. Несколько «сэмплов» тени наложены друг на друга без какого-либо смешения. На втором фрагменте они уже смешаны (Jitter Amount изменен с 0,0 на 6,0). Смешанные «сэмплы» воспринимаются как более естественная тень, но качество ее оставляет желать лучшего. Третий фрагмент показывает тень с отличным качеством (Shadow Integrity и Shadow Quality изменены с единичных значений на 8 и 10 соответственно).

Второй ряд на Рис.3. иллюстрирует, как меняется характер тени, если мы увеличиваем размеры мнимого источника. В данном случае у нас мнимый источник типа Rectangle Light (плоский прямоугольный). При увеличении площади источника размытие тени увеличивается.

Рис.3 Изменение качества тени типа Area Shadow при различных параметрах

Некоторые значения параметров имеют рекомендательный характер, но все ограничивается лишь вашей фантазией. Самый лучший способ разобраться – это эксперимент. Не бойтесь проводить опыты со светом. Уловите настроение будущей картинки и сдаитесь за настройки.

На Рис.4. шахматный конь с материалом на основе простой процедурной текстуры Wood. Три источника света подкрашенные в разные цвета. Простая постановка, тем не менее, фигурка смотрится неплохо.

Рис.4 Шахматная фигура «Конь». Предметная визуализация

Резюме

Освещение – один из важнейших этапов в работе над трехмерной сценой. С первого взгляда может показаться, что сухую информацию урока невозможно приложить к творческой работе. Однако же при должной смекалке и трудолюбии можно добиться невероятных результатов. В конце концов все цифровые изображения всего лишь наборы нулей и единиц, а 3dsMax всего лишь ваш очередной инструмент, ровно как карандаш или кисть.