在3D图形领域,经常需要在屏幕上突出显示对象的轮廓。这可以用于多种目的,例如,在CAD系统中,可能会用红色轮廓来表示当前选中的对象。此外,密集三角化的物体在线条模式下往往渲染效果不佳,轮廓模式可以作为一种向用户展示物体形状的方法,而不会过于复杂。另一个应用场景是在工程绘图中,3D模型的轮廓可以用来自动生成模型的特定黑白投影。
轮廓的生成需要对场景几何体进行多次遍历来实现其目标。多通道渲染技术越来越普遍,通常用于游戏软件中生成阴影等效果。传统的Z缓冲区技术无法创建阴影,但多通道技术可以实现。文章最后将简要介绍如何实现阴影,尽管不会展示阴影的代码。
第一种技术是快速而简单的。它涉及使用非常粗的线条绘制对象的线框图,然后使用背景色重新绘制图像。这使得突出显示对象的粗线框边缘可见,从而得到所需的轮廓。其中一个渲染通道必须进行偏移。选择将表面向前拉出屏幕,以确保多边形覆盖在顶部。
第二种技术使用了一种称为模板缓冲区的图形卡特性。模板缓冲区类似于Z缓冲区,但允许在渲染通道中进行每个像素的测试。在这种情况下,使用背景色绘制对象,每次成功将像素写入屏幕时,都会向模板缓冲区写入一个值。然后重新绘制线框图,使用粗线条,但只有在模板缓冲区为空时才写入。
为了快速获取一些OpenGL代码,使用了另一个Code Project文章中的库和演示应用程序。
OGLTools by Jonathan de Halleux提供了一个面向对象的框架,用于创建GL渲染上下文并处理所有Windows特定的OpenGL内容。请注意,要使用模板缓冲区,必须确保在像素格式中获得模板缓冲区。通过稍微修改Jonathan的项目来实现这一点。
演示应用程序基于Jonathan de Halleux的应用程序。编辑了绘制循环,以便它可以以三种方式之一进行绘制。第一种使用多边形偏移,第二种使用模板缓冲区,第三种使用模板缓冲区和显示列表。在第三种算法中,将对象绘制成深蓝色而不是背景色,以便可以看到效果。可以从应用程序的标准菜单中选择模式。
glPushAttrib(GL_ALL_ATTRIB_BITS);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-2.5f, -2.5f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glLineWidth(3.0f);
glColor3f(1.0f, 1.0f, 1.0f);
RenderMesh3();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_LIGHTING);
glColor3f(0.0f, 0.0f, 0.0f);
RenderMesh3();
glPopAttrib();
glPushAttrib(GL_ALL_ATTRIB_BITS);
glEnable(GL_LIGHTING);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor3f(0.0f, 0.0f, 0.0f);
RenderMesh3();
glDisable(GL_LIGHTING);
glStencilFunc(GL_NOTEQUAL, 1, 0xFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glLineWidth(3.0f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(1.0f, 1.0f, 1.0f);
RenderMesh3();
glPopAttrib();
glClearStencil用于设置glClear(GL_STENCIL_BUFFER_BIT)的清除值。这允许将模板缓冲区初始化为想要的任何值。
glEnable(GL_STENCIL_TEST)非常直观,它打开了模板缓冲区的使用。关键函数是glStencilFunc和glStencilOp。
glStencilFunc允许设置测试函数,指定参考值,并在进行任何测试之前将掩码应用于模板缓冲区和参考值。测试函数可以包含多种比较运算符,或者是通用的GL_NEVER和GL_ALWAYS。
glStencilOp解释了在模板缓冲区测试失败、模板缓冲区测试通过但Z缓冲区测试失败以及模板缓冲区测试和Z缓冲区测试都通过的情况下应该做什么。选项包括保留现有值、将缓冲区归零或替换、反转、递增或递减值。
最后,想解释使用模板缓冲区和多通道渲染产生阴影的多种方法之一。以下过程将从简单对象产生地面平面上的阴影:
将视图变换加载到模型视图矩阵中。这将系统设置为从视点查看。
绘制对象和地面平面,启用光照。
使用glPushMatrix()推送模型视图矩阵。
构建一个灯光视图矩阵并将其乘以模型视图矩阵。灯光视图矩阵是使用地面平面的方程和灯光位置以特定方式生成的。
void shadowMatrix(GLfloat m[4][4], GLfloat plane[4], GLfloat light[4]) {
GLfloat dot = plane[0]*light[0] + plane[1]*light[1] +
plane[2]*light[2] + plane[3]*light[3];
m[0][0] = dot - light[0]*plane[0];
m[1][0] = - light[0]*plane[1];
m[2][0] = - light[0]*plane[2];
m[3][0] = - light[0]*plane[3];
m[0][1] = - light[1]*plane[0];
m[1][1] = dot - light[1]*plane[1];
m[2][1] = - light[1]*plane[2];
m[3][1] = - light[1]*plane[3];
m[0][2] = - light[2]*plane[0];
m[1][2] = - light[2]*plane[1];
m[2][2] = dot - light[2]*plane[2];
m[3][2] = - light[2]*plane[3];
m[0][3] = - light[3]*plane[0];
m[1][3] = - light[3]*plane[1];
m[2][3] = - light[3]*plane[2];
m[3][3] = dot - light[3]*plane[3];
}
设置多边形偏移,以便渲染的阴影多边形不与地面平面交错。
将地面平面渲染到模板缓冲区 - 每次写入地面平面时设置一个值。
禁用光照,将颜色设置为深灰色,并再次渲染对象(不是地面平面)。只有在模板缓冲区设置时才渲染对象点,并且在每次渲染时将模板缓冲区的内容归零。这个稍微复杂的过程将确保阴影只投射在地面平面上,并且阴影多边形只渲染一次。
弹出矩阵并禁用多边形偏移以整理事物。