Стенсил буфер (Stencil Buffer)
Стенсил буфер (Stencil Buffer — буфер шаблона или буфер трафарета) — это дополнительный буфер, соответствующий размеру выводимого кадра, то есть каждому пикселю изображения на экране соответствует свое значение в стенсил буфере. Каждый раз когда точка рисуется на экран, то кроме тестов, вроде сравнения с глубиной в Z-буфере, она проходит еще и стенсил тест. То есть, например, можно сказать — точка рисуется, только если в стенсиле значение больше единицы. С другой стороны, можно сказать, как изменить значение стенсила после того как пиксель в этом месте отрисуется.
Описание
Стенсил буфер используется при создании таких спецэффектов, как тени, отражения, плавные переходы из одной картинки в другую, создания конструктивной стереометрии (CSG) и др.
Для включения работы буфера трафарета требуется выделить для него некоторое количество бит в формате бэк-буфера.
- В OpenGL количество бит можно задать в структуре PIXELFORMATDESCRIPTOR, поменяв значание cStencilBits (идёт сразу после cDepthBits).
- В DirectX для этого следует выбрать один из Depth\Stencil форматов с некоторым кол-вом бит, помеченных буквой S (D24S8, D15S1, D24X4S4, S8). Формат S8 поддерживается только в Direct3D 9Ex.
Имейте в виду, что видеокарты ATI и NVidia поддерживают только 8-ми битный стенсил (D3DFMT_D24S8), поэтому стоит использовать его.
Теперь можно будет работать с буфером трафарета, и первое что нам захочется сделать - очистить его:
- В OpenGL мы устанавливаем значение: glClearStencil(val); и затем очищаем glClear(GL_STENCIL_BUFFER_BIT); не забывайте, что можно очищать несколько буферов одновременно!
- В DirectX мы устанавливаем последний DWORD параметр функции IDirect3DDevice9::Clear(...);.
Далее для работы предоставляются функции для установки условия stencil теста, значение для сравнений, маски значения трафарета (перед сравнением значения будут обрабатываться побитовым оператором AND с использованием этой маски) и трёх операций, которые будут проводиться со значением в буфере трафарета (при провале теста трафарета, при провале теста глубины - zfail, и прохода теста глубины и теста трафарета - pass).
Но перед этим нужно включить тест трафарета вызовом функций:
glEnable(GL_STENCIL_TEST);
SetRenderState(D3DRS_STENCILENABLE, true);
Теперь можем установить условие, значение и маску:
В OpenGL это делается одной командой
glStencilFunc(func, value, mask);
Но для смены маски есть и дополнительная функция
glStencilMask(mask);
В DirectX9 мы можем устанавливать эти параметры по отдельности вызовом функций
SetRenderState(D3DRS_STENCILFUNC, func);
SetRenderState(D3DRS_STENCILREF, value);
SetRenderState(D3DRS_STENCILMASK, mask);
Значения подставляемые на место func:
Параметр | Название в Direct3D10 | Название в Direct3D9 | Название в OpenGL | Описание |
Никогда | D3D10_COMPARISON_NEVER | D3DCMP_NEVER | GL_NEVER | if(false) |
Меньше | D3D10_COMPARISON_LESS | D3DCMP_LESS | GL_LESS | if((ref & mask) < (stencil & mask)) |
Меньше или равно | D3D10_COMPARISON_LESS_EQUAL | D3DCMP_LESSEQUAL | GL_LEQUAL | if((ref & mask) <= (stencil & mask)) |
Больше | D3D10_COMPARISON_GREATER | D3DCMP_GREATER | GL_GREATER | if((ref & mask) > (stencil & mask)) |
Больше или равно | D3D10_COMPARISON_GREATER_EQUAL | D3DCMP_GREATEREQUAL | GL_GEQUAL | if((ref & mask) >= (stencil & mask)) |
Равно | D3D10_COMPARISON_EQUAL | D3DCMP_EQUAL | GL_EQUAL | if((ref & mask) == (stencil & mask)) |
Неравно | D3D10_COMPARISON_NOT_EQUAL | D3DCMP_NOTEQUAL | GL_NOTEQUAL | if((ref & mask) != (stencil & mask)) |
Всегда | D3D10_COMPARISON_ALWAYS | D3DCMP_ALWAYS | GL_ALWAYS | if(true) |
По умолчанию стоит значение «всегда».
Для установки трёх операций в OpenGL опять же используется одна функция
glStencilOp(fail, zfail, pass);
В DirectX9 устанавливаем по отдельности:
SetRenderState(D3DRS_STENCILFAIL, fail);
SetRenderState(D3DRS_STENCILZFAIL, zfail);
SetRenderState(D3DRS_STENCILPASS, pass);
Параметры fail, zfail, pass могут принимать следующие значения:
Параметр | Название в Direct3D10 | Название в Direct3D9 | Название в OpenGL | Описание |
Не менять | D3D10_STENCIL_OP_KEEP | D3DSTENCILOP_KEEP | GL_KEEP | Не менять значение в буфере трафарета |
0 | D3D10_STENCIL_OP_ZERO | D3DSTENCILOP_ZERO | GL_ZERO | Установить значение в 0 |
Заменить | D3D10_STENCIL_OP_REPLACE | D3DSTENCILOP_REPLACE | GL_REPLACE | Установить значение в value (stencil ref) |
Увеличить на 1 | D3D10_STENCIL_OP_INCR_SAT | D3DSTENCILOP_INCRSAT | GL_INCR | Увеличить значение на 1. Значение ограничено максимальным (255) |
*Увеличить на 1 | D3D10_STENCIL_OP_INCR | D3DSTENCILOP_INCR | GL_INCR_WRAP | Увеличить значение на 1. При превышении максимума сбрасывается в 0 |
Уменьшить на 1 | D3D10_STENCIL_OP_DECR_SAT | D3DSTENCILOP_DECRSAT | GL_DECR | Уменьшить значение на 1. Значение ограничено минимумом (0) |
*Уменьшить на 1 | D3D10_STENCIL_OP_DECR | D3DSTENCILOP_DECR | GL_DECR_WRAP | Уменьшить значение на 1. При уменьшении нуля, оно сбрасывается в 255 |
Инвертировать | D3D10_STENCIL_OP_INVERT | D3DSTENCILOP_INVERT | GL_INVERT | Побитовое инвертирование значения (bitwise NOT) |
Одно из самых популярных и часто используемых применений буфера трафарета — создание теней. Этот метод теней называется Shadow Volume. Он стал так популярен, что производители видеокарт начали вводить дополнительные расширения для ускорения этого метода.
Одно из таких расширений — двусторонний тест трафарета. Так как все грани разделяются на лицевые и не-лицевые, для каждого типа из них можно установить своё условие прохождения теста трафарета, и свои операции fail, zfail и zpass.
В OpenGL имеются следующие расширения: GL_EXT_stencil_two_side и GL_ATI_separate_stencil. Первое мульти-вендор расширения держат карты NVidia начиная с GeForce FX5200, а второе - карты ATI начиная с ATI Radeon 9500.
По этой причине, нам придётся описать работу с обоими.
GL_EXT_stencil_two_side:
Включаем: glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
Выбираем ориентацию треугольника: glActiveStencilFaceEXT(face); face - GL_FRONT, GL_BACK
Далее настраиваем параметры для выбранной стороны.
GL_ATI_separate_stencil:
Включаем: glEnable(GL_STENCIL_TEST);
Выбираем одновременно условие для передних и задних граней: glStencilFuncSeparateATI(frontfunc, backfunc, value, mask);
Устанавливаем операции для каждой стороны: glStencilOpSeparateATI(face, fail, zfail, pass);
Как мы видим, АТИ просто не поддерживает различное значение для сравнения и маску у передних и задних сторон.
В DirectX9 мы делаем следующее:
Включаем: SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, true);
Устанавливаем условие для граней с clock-wise порядком следования вершин: SetRenderState(D3DRS_STENCILFUNC, cwfunc);
И для граней с conter clock-wise порядком следования вершин: SetRenderState(D3DRS_CCW_STENCILFUNC, ccwfunc);
Далее устанавливаем операции функциями (Допустим что у нас передние грани с clock-wise порядком следования вершин):
SetRenderState(D3DRS_STENCILFAIL, frontfail); SetRenderState( D3DRS_STENCILZFAIL, frontzfail); SetRenderState( D3DRS_STENCILPASS, frontpass); SetRenderState( D3DRS_CCW_STENCILFAIL, backfail); SetRenderState( D3DRS_CCW_STENCILZFAIL, backzfail); SetRenderState( D3DRS_CCW_STENCILPASS, backpass);
Что такое Стенсил буфер (Stencil Buffer)?
#буферы, #Direct3D, #OpenGL, #stencil
29 июля 2005 (Обновление: 17 фев 2011)
Комментарии [4]