Библиотека Direct3D располагает средствами работы с так называемым буфером трафарета. Буфер трафарета представляет собой двумерный массив с размерами как у буфера кадра и z-буфера, причем пиксель с координатами (x,y) в буфере трафарета будет соответствовать пикселю с такими же координатами в буфере кадра. Буфер трафарета работает как маска (трафарет), позволяя нам блокировать вывод некоторых пикселей на экран по заданному правилу. Принцип работы с буфером трафарета – это, как правило, двухпроходный алгоритм. Сначала мы разбиваем область вывода на зоны и каждой зоне присваиваем номер, при этом ничего не выводится на экран. Затем на основании некоторого установленного правила, мы производим непосредственный вывод сцены на экран, причем одни зоны могут быть выведены, другие нет.
По умолчанию тест трафарета выключен (заблокирован). Чтобы его включить необходимо установить значение переменной D3DRS_STENCILENABLE в true. Программно это выглядит следующим образом:
| C++ |
device->SetRenderState( D3DRS_STENCILENABLE, true ); // включение device->SetRenderState( D3DRS_STENCILENABLE, false ); // выключение |
| Pascal |
device.SetRenderState( D3DRS_STENCILENABLE, 1 ); // включение device.SetRenderState( D3DRS_STENCILENABLE, 0 ); // выключение |
Очистка буфера трафарета осуществляется с помощью вызова метода Clear интерфейса IDirect3DDevice9, который использовался нами и для очистки буфера кадра.
| C++ |
device->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 0.0f, 0 ); |
| Pascal |
device.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 0.0, 0 ); |
Добавленная константа (D3DCLEAR_STENCIL) в третий аргумент метода указывает, что мы собираемся очищать еще и буфер трафарета. А последний параметр метода (в нашем случае он ноль) определяет значение, которым будет заполнен буфер трафарета.
В силу того, что буфер трафарета может работать только совместно с буфером глубины, нам придется сказать несколько слов и о нем. Буфер глубины или z-буфер – это вспомогательный "экран" необходимый для удаления невидимых наблюдателю граней и поверхностей.
Буфер глубины представляет собой двумерный массив, хранящий z-координаты каждого пикселя. Программно буфер глубины может быть инициализирован с помощью заполнения двух полей структуры D3DPRESENT_PARAMETERS:
| C++ | D3DPRESENT_PARAMETERS params; … ZeroMemory( ¶ms, sizeof(params) ); … params.EnableAutoDepthStencil = true; params.AutoDepthStencilFormat = D3DFMT_D16; … |
| Pascal | var params: TD3DPresentParameters; … ZeroMemory( @params, SizeOf(params) ); … params.EnableAutoDepthStencil := true; params.AutoDepthStencilFormat := D3DFMT_D16; … |
| D3DFMT_D24S8 | буфер глубины определяется 24 битами на пиксель; трафаретный буфер задан 8-ю битами на пиксель. |
| D3DFMT_D15S1 | буфер глубины определяется 15 битами на пиксель; трафаретный буфер задан одним битом на пиксель. |
| D3DFMT_D24X4S4 | буфер глубины определяется 24 битами на пиксель; трафаретный буфер задан 4-мя битами на пиксель и 4 бита не используются |
| C++ | device->SetRenderState( D3DRS_STENCILMASK, 0xff00ffff ); |
| Pascal | device.SetRenderState( D3DRS_STENCILMASK, $ff00ffff ); |
| C++ | device->SetRenderState( D3DRS_STENCILREF, 1 ); |
| Pascal | device.SetRenderState( D3DRS_STENCILREF, 1 ); |
| D3DCMP_NEVER | Никогда |
| D3DCMP_LESS | Если (ref & mask) < (value & mask) |
| D3DCMP_EQUAL | Если (ref & mask) = (value & mask) |
| D3DCMP_LESSEQUAL | Если (ref & mask) <= (value & mask) |
| D3DCMP_GREATER | Если (ref & mask) > (value & mask) |
| D3DCMP_NOTEQUAL | Если (ref & mask) <> (value & mask) |
| D3DCMP_GREATEREQUAL | Если (ref & mask) >= (value & mask) |
| D3DCMP_ALWAYS | Всегда |
| C++ | device->SetRenderState( D3DRS_STENCILFUNC, <операция сравнения> ); |
| Pascal | device.SetRenderState( D3DRS_STENCILFUNC, <операция сравнения> ); |
| D3DSTENCILOP_KEEP | Не изменять значение в буфере трафарета |
| D3DSTENCILOP_ZERO | Установить значение в буфере трафарета в ноль |
| D3DSTENCILOP_REPLACE | Заменить значение в буфере трафарета на значение ref, определенное константой D3DRS_STENCILREF |
| D3DSTENCILOP_INCRSAT | Увеличить значение буфера трафарета на единицу |
| D3DSTENCILOP_DECRSAT | Уменьшить значение буфера трафарета на единицу |
| D3DSTENCILOP_INVERT | Произвести операцию побитового инвертирования |
| D3DSTENCILOP_INCR | Увеличить значение буфера трафарета на единицу |
| D3DSTENCILOP_DECR | Уменьшить значение буфера трафарета на единицу |

Шаг 1. Очистка буфера кадра и буфера трафарета (значение для фона в буфере трафарета - ноль) device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 0.0f, 0); |
Шаг 2. Вывод квадрата, покрытого текстурой (тест трафарета при этом отключен) … device->SetTexture(0, tex); device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2); … |
Шаг 3. 3.1 Включаем (разрешаем) тест трафарета. 3.2. В качестве параметра прохождения теста трафарета установить D3DCMP_NEVER (тест никогда не проходит и, следовательно, примитив не будет записан в буфер кадра). 3.3. В качестве значения записать в буфер трафарета, например, число два. 3.4. В качестве действия отрицательного прохождения теста трафарета установить D3DSTENCILOP_REPLACE (в буфере трафарета будет записана "двойка" на месте вырезки) 3.5. Вызвать функцию отрисовки желтого треугольника (сам треугольник не будет нарисован, но буфер трафарета обновит свое содержимое) … device->SetRenderState(D3DRS_STENCILENABLE, true); device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_NEVER); device->SetRenderState(D3DRS_STENCILREF, 0x2); device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_REPLACE); device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); … |
Шаг 4. 4.1. В качестве параметра прохождения теста трафарета установить D3DCMP_GREATER 4.2. В качестве значения, с которым будет сравниваться содержимое буфера трафарета, установить, например, число один. 4.3. Вывести разноцветный треугольник … device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ GREATER); device->SetRenderState(D3DRS_STENCILREF, 0x1); device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_REPLACE); device->DrawPrimitive(D3DPT_TRIANGLELIST, 3, 1); … |

