Шаг 47 - Буффер трафарета

Итак, все пиво выпито, пора работать.

Делаем стандартный проект. Убираем из него всякие функции генерации списков и прочую ерунду. Функция Draw теперь будет выглядеть так:

GLvoid Draw()
{
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	glPushMatrix();
	DrawPrim();
	glPopMatrix();
	SwapBuffers(hDC);
}

Здесь, как вы видите, к функции glClear добавился еще один флаг - очистка буффера трафарета. Кстати буфер трафарета надо включить. Сделать это удобно в StartRC():

glEnable(GL_STENCIL_TEST);

И там же установить значение, которым будет заполняться буффер трафарета при очистке:

glClearStencil(0);
glStencilMask(1);

Еще пару слов о том что же такое этот буффер трафарета. По сути дела этот буффер представляет собой двумерный массив чисел (запомните!!! ДВУМЕРНЫЙ, а не трехмерный), который мы будем заполнять при рисовании. Информация в буффер кадра попадает после успешного прохождения буффера трафарета, поэтому если буффер трафарета не найден, то на экране вы ничего не увидите. По умолчанию буффер трафарета выключен, поэтому вся информация сразу же попадает в буффер кадра.

Ну вот, теперь переходим к рисованию. Самое интересно будет происходить в функции DrawPrim(), перейдем туда:

GLvoid DrawPrim()
{
	glStencilFunc(GL_ALWAYS, 2, 1);
	glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POLYGON);
		glVertex3f(1.0f, 1.0f, 0.0f);
		glVertex3f(-1.0f, 1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, 0.0f);
	glEnd();

	glStencilFunc(GL_NEVER, 1, 1);
	glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
	glColor3f(0.0f, 1.0f, 0.0f);
	glBegin(GL_POLYGON);
		glVertex3f(0.0f, 1.5f, 0.0f);
		glVertex3f(-1.5f, -1.5f, 0.0f);
		glVertex3f(1.5f, -1.5f, 0.0f);
	glEnd();

	glStencilFunc(GL_EQUAL, 2, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	glColor3f(0.0f, 0.0f, 1.0f);
	glBegin(GL_POLYGON);
		glVertex3f(0.0f, 2.0f, 0.0f);
		glVertex3f(-2.0f, -2.0f, 0.0f);
		glVertex3f(2.0f, -2.0f, 0.0f);
	glEnd();
}

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

Давайте пройдем по шагам каждую команду.

glStencilFunc(GL_ALWAYS, 2, 1);

Означает, что тест трафарета всегда заканчивается удачно и во все места, соответствующие пикселам квадрата в буффер трафарета записывается двоечка.

glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);

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

Далее рисуем квадрат:

glStencilFunc(GL_NEVER, 1, 1);

Команда говорит о том, что тест трафарета всегда заканчивается неудачно.

glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);

В случае, если тест трафарета прошел с ошибкой - заменить текущее (первый аргумент команды glStencilOp) значение на то, что указано 2-м аргументом в glStencilFunc, т.е. на 1.

Далее рисуем треугольник.

glStencilFunc(GL_EQUAL, 2, 1);

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

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

Говорит о том, что никакие изменения в буффере трафарета делать не надо.

Рисуем еще один большой треугольник. Теперь соберите проект и посмотрите что получилось. Треугольник с дыркой, а посередине квадрат. Попробуйте теперь поменять в строке glStencilFunc(GL_EQUAL, 2, 1) 2-ку на 1-ку и проанализировать почему получилось именно так, а не иначе.

Очень рекоммендую попробовать нарисовать свои примитивы и повырезать в них дырки. Буффер трафарета достаточно часто используется в OpenGL, например, при постороении теней (для этого есть правда и менее энергоёмкие способы, но о них потом). Поэтому хорошее понимание логики его работы позволит легко работать с ним в 3D.


Загрузить проект | Предыдущий Шаг | Оглавление
Автор Kirill V. Ratkin - 14.06.2000