Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Термины / Стенсил буфер (Stencil Buffer)

Стенсил буфер (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_NEVERD3DCMP_NEVERGL_NEVERif(false)
МеньшеD3D10_COMPARISON_LESSD3DCMP_LESSGL_LESSif((ref & mask) < (stencil & mask))
Меньше или равноD3D10_COMPARISON_LESS_EQUALD3DCMP_LESSEQUALGL_LEQUALif((ref & mask) <= (stencil & mask))
БольшеD3D10_COMPARISON_GREATERD3DCMP_GREATERGL_GREATERif((ref & mask) > (stencil & mask))
Больше или равноD3D10_COMPARISON_GREATER_EQUALD3DCMP_GREATEREQUALGL_GEQUALif((ref & mask) >= (stencil & mask))
РавноD3D10_COMPARISON_EQUALD3DCMP_EQUALGL_EQUALif((ref & mask) == (stencil & mask))
НеравноD3D10_COMPARISON_NOT_EQUALD3DCMP_NOTEQUALGL_NOTEQUALif((ref & mask) != (stencil & mask))
ВсегдаD3D10_COMPARISON_ALWAYSD3DCMP_ALWAYSGL_ALWAYSif(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_KEEPD3DSTENCILOP_KEEPGL_KEEPНе менять значение в буфере трафарета
0D3D10_STENCIL_OP_ZEROD3DSTENCILOP_ZEROGL_ZEROУстановить значение в 0
ЗаменитьD3D10_STENCIL_OP_REPLACED3DSTENCILOP_REPLACEGL_REPLACEУстановить значение в value (stencil ref)
Увеличить на 1D3D10_STENCIL_OP_INCR_SATD3DSTENCILOP_INCRSATGL_INCRУвеличить значение на 1. Значение ограничено максимальным (255)
*Увеличить на 1D3D10_STENCIL_OP_INCRD3DSTENCILOP_INCRGL_INCR_WRAPУвеличить значение на 1. При превышении максимума сбрасывается в 0
Уменьшить на 1D3D10_STENCIL_OP_DECR_SATD3DSTENCILOP_DECRSATGL_DECRУменьшить значение на 1. Значение ограничено минимумом (0)
*Уменьшить на 1D3D10_STENCIL_OP_DECRD3DSTENCILOP_DECRGL_DECR_WRAPУменьшить значение на 1. При уменьшении нуля, оно сбрасывается в 255
ИнвертироватьD3D10_STENCIL_OP_INVERTD3DSTENCILOP_INVERTGL_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)?

29 июля 2005

#буферы, #Direct3D, #OpenGL, #stencil


Обновление: 17 февраля 2011

2001—2017 © GameDev.ru — Разработка игр