Введение
Техника компьютерной графики
Теневой объем — это техника, используемая в 3D-компьютерной графике для добавления теней в отрендеренную сцену. Впервые она была предложена Фрэнком Кроу в 1977 году как геометрия, описывающая трехмерную форму области, затененной от источника света. Теневой объем разделяет виртуальный мир на две части: области, находящиеся в тени, и области, не находящиеся в тени. Реализация теневых объемов с использованием буфера трафарета обычно считается одним из наиболее практичных универсальных методов затенения в реальном времени для использования на современном 3D-оборудовании. Она получила широкое распространение благодаря видеоигре Doom 3, а конкретный вариант этой техники, использованный в этой игре, стал известен как Carmack's Reverse. Теневые объемы стали популярным инструментом для затенения в реальном времени, наряду с более традиционным методом — картами теней. Основное преимущество теневых объемов заключается в их точности до пикселя (хотя многие реализации имеют небольшую проблему самозатенения вдоль края силуэта, см. описание ниже), в то время как точность карты теней зависит от объема выделенной текстурной памяти, а также от угла падения теней (под некоторыми углами точность карты теней неизбежно снижается). Однако эта техника требует создания геометрии теней, что может быть ресурсоемким для центрального процессора (в зависимости от реализации). Преимущество карт теней заключается в том, что они часто работают быстрее, поскольку полигоны теневого объема часто занимают большую площадь на экране и требуют значительного времени заполнения (особенно для выпуклых объектов), в то время как карты теней не имеют этого ограничения.
Глубинный проход
Хайдманн предложил, что если передние и задние поверхности теней отрисовывать в отдельных проходах, то количество передних и задних граней перед объектом можно подсчитать с помощью буфера трафарета. Если поверхность объекта находится в тени, между ним и камерой будет больше теневых граней, обращенных вперед, чем граней, обращенных назад. Однако, если их количество равно, поверхность объекта не находится в тени. Генерация маски трафарета происходит следующим образом: отключить запись в буфер глубины и буфер цвета. Использовать отсечение задних граней. Установить операцию трафарета на инкремент при прохождении проверки глубины (учитывать только тени перед объектом). Отрисовать объемы теней (из-за отсечения отрисовываются только их передние грани). Использовать отсечение передних граней. Установить операцию трафарета на декремент при прохождении проверки глубины. Отрисовать объемы теней (отрисовываются только их задние грани). После этого все освещенные поверхности будут соответствовать значению 0 в буфере трафарета, где число передних и задних граней всех объемов теней между камерой и этой поверхностью равно. Этот подход имеет проблемы, когда сама камера находится внутри объема тени (например, когда источник света перемещается за объект). С этой точки зрения, камера видит заднюю грань этого объема тени в первую очередь, и это добавляет смещение −1 ко всему буферу трафаретов, эффективно инвертируя тени. Это можно исправить, добавив "крышку" (cap) на переднюю часть объема тени, обращенную к камере, например, на передней плоскости отсечения. Существует и другая ситуация, когда камера может находиться в тени объекта, расположенного за камерой, который также необходимо как-то закрыть, чтобы предотвратить аналогичную проблему. В большинстве распространенных реализаций, поскольку корректное закрытие при проверке глубины может быть сложным, метод проверки прохождения глубины (см. ниже) может быть использован для этих особых случаев. Альтернативно, можно добавить смещение +1 к буферу трафарета для каждого объема тени, внутри которого находится камера, хотя обнаружение может быть медленным. Существует еще одна потенциальная проблема, если в буфере трафарета недостаточно битов для хранения количества теней, видимых между камерой и поверхностью объекта, поскольку он использует арифметику насыщения. (Если бы вместо этого использовалось арифметическое переполнение, проблема была бы незначительной.) Проверка прохождения глубины также известна как z-проверка, поскольку буфер глубины часто называют z-буфером.
Disable writes to the depth and color buffers. Use back face culling. Set the stencil operation to increment on depth pass (only count shadows in front of the object). Render the shadow volumes (because of culling, only their front faces are rendered). Use front face culling. Set the stencil operation to decrement on depth pass. Render the shadow volumes (only their back faces are rendered). After this is accomplished, all lit surfaces will correspond to a 0 in the stencil buffer, where the numbers of front and back surfaces of all shadow volumes between the eye and that surface are equal. This approach has problems when the eye itself is inside a shadow volume (for example, when the light source moves behind an object). From this point of view, the eye sees the back face of this shadow volume before anything else, and this adds a −1 bias to the entire stencil buffer, effectively inverting the shadows. This can be remedied by adding a "cap" surface to the front of the shadow volume facing the eye, such as at the front clipping plane. There is another situation where the eye may be in the shadow of a volume cast by an object behind the camera, which also has to be capped somehow to prevent a similar problem. In most common implementations, because properly capping for depth pass can be difficult to accomplish, the depth fail method (see below) may be licensed for these special situations. Alternatively one can give the stencil buffer a +1 bias for every shadow volume the camera is inside, though doing the detection can be slow. There is another potential problem if the stencil buffer does not have enough bits to accommodate the number of shadows visible between the eye and the object surface, because it uses saturation arithmetic. (If they used arithmetic overflow instead, the problem would be insignificant.) Depth pass testing is also known as z pass testing, as the depth buffer is often referred to as the z buffer.
Глубина не работает
Около 2000 года несколько человек обнаружили, что метод Хайдмана можно заставить работать для всех положений камеры, инвертировав глубину. Вместо подсчета теневых поверхностей перед поверхностью объекта, поверхности за ним можно считать столь же легко, с тем же конечным результатом. Это решает проблему, когда глаз оказывается в тени, поскольку объемы теней между глазом и объектом не учитываются, но вводит требование, что задняя часть объема тени должна быть отсечена, иначе тени будут отсутствовать там, где объем направлен назад к бесконечности. Отключите запись в буфер глубины и буфер цвета. Используйте отсечение передних граней. Установите операцию с буфером трафарета на увеличение при провале проверки глубины (считайте только тени позади объекта). Визуализируйте объемы теней. Используйте отсечение задних граней. Установите операцию с буфером трафарета на уменьшение при провале проверки глубины. Визуализируйте объемы теней. Метод проверки глубины имеет те же соображения относительно точности буфера трафарета, что и метод прохода по глубине. Также, как и метод прохода по глубине, его иногда называют методом z-fail. Уильям Билодо и Майкл Сонги открыли эту технику в октябре 1998 года и представили ее на конференции разработчиков Creative Labs под названием Creativity в 1999 году. Сим Дитрих представил эту технику как на GDC в марте 1999 года, так и на Creativity в конце 1999 года. Через несколько месяцев Уильям Билодо и Майкл Сонги подали заявку на патент США на эту технику в том же году под названием "Метод рендеринга теней с использованием объема тени и буфера трафарета". Джон Кармак из id Software независимо открыл этот алгоритм в 2000 году во время разработки Doom 3.
Исключительная-или
Любой из вышеуказанных типов может быть аппроксимирован с помощью реализации "исключающее ИЛИ", которая некорректно обрабатывает пересекающиеся объемы теней, но позволяет сэкономить один проход рендеринга (если не время растеризации) и требует только 1-битный буфер трафарета. Следующие шаги относятся к версии прохода по глубине:
Отключите запись в буфер глубины и буфер цвета. Установите операцию трафарета на XOR при проходе по глубине (инвертировать на любой затененной поверхности). Отрисуйте объемы теней.
Оптимизация
Один из методов ускорения вычислений геометрии объемов теней заключается в использовании существующих этапов графического конвейера для выполнения части вычислений. Например, при использовании однородных координат координату w можно установить в ноль, чтобы расширить точку до бесконечности. Это должно сопровождаться использованием viewing frustum с дальней плоскостью отсечения, простирающейся до бесконечности, чтобы вместить эти точки, что достигается с помощью специализированной проекционной матрицы. Этот метод незначительно снижает точность буфера глубины, но разница обычно несущественна. Подробную реализацию можно найти в статье 2002 года "Практичные и надежные stencil-теневые объемы для аппаратного ускоренного рендеринга" (C. Everitt и M. Kilgard). Время растрирования объемов теней можно сократить, используя аппаратный scissor-тест для ограничения теней определенным прямоугольником на экране. NVIDIA реализовала аппаратную функцию, называемую depth bounds test, предназначенную для удаления частей объемов теней, которые не влияют на видимую сцену. (Эта функция доступна начиная с модели GeForce FX 5900.) Обсуждение этой функции и ее использования с объемами теней было представлено на Game Developers Conference в 2005 году. Поскольку метод depth fail имеет преимущество перед depth pass только в том случае, когда глаз находится внутри объема тени, предпочтительнее проверять это условие и использовать depth pass, когда это возможно. Это позволяет избежать ненужного back capping (и связанной с ним растризации) в случаях, когда depth fail не требуется, а также проблемы с правильным front capping для особых случаев depth pass. В более современных графических конвейерах для генерации объемов теней можно использовать geometry shaders. На системах, не поддерживающих geometry shaders, vertex shaders также можно использовать для создания объемов теней путем выборочного выдавливания вершин, которые уже находятся в памяти GPU.