<p>有时我们不需要物体的许多细节,而只需要其外围的轮廓来描绘物体大概的形状比如物体的阴影。</p> <p>如果我们简单的使用线框模式绘制一个立方体如下:</p> <p><a href="http://static.oschina.net/uploads/img/201312/21173609_p6of.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://static.oschina.net/uploads/img/201312/21173610_xWul.png" width="313" height="246"></a></p> <p>下面介绍OpenGL生成轮廓的两种方式。</p> <h4>多边形偏移</h4> <p>多边形偏移是一个挺有用的技巧,有时会用来解决z-fighting。z-fighting在开启深度测试时,如果两个重叠物体的深度值非常接近,那么就有可能会产生z-fighting(因为在绘制物体时采用插值的方式,而插值会有一定的误差)。</p> <p>如下图</p> <p><a href="http://static.oschina.net/uploads/img/201312/21173610_K9Nz.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://static.oschina.net/uploads/img/201312/21173610_KF5T.png" width="419" height="196"></a></p> <p>遇到这种情况,我们就可以使用多变形偏移,对一个多边形的z值进行偏移。</p> <p>多边形偏移有三种方法,分别对应于不同的光栅化模式:GL_LINE, GL_FILL和GL_POINT.通过 glEnable传参数GL_POLYGON_OFFSET_LINE、GL_POLYGN_OFFSET_FILL和GL_POLYGON_OFFSET_POINT来启用。</p> <p>通过glPolygonOffset(GLfloat factor, GLfloat units);如果启用了多边形偏移,那么在进行深度测试之前,每个片段的深度值都会加上一个计算出来的偏移值。计算公式如下:</p> <p>offset = m * factor + r * units;</p> <p>其中m是多边形的最大深度斜率(在光栅化过程中计算的),r是能够保证产生可解析区别的窗口坐标深度值的最小值。r因OpenGL的实现而异。通过设置factor和units来调整偏移值,来达到你想要的效果。</p> <p>下面是使用多边形偏移的代码:</p><pre class="prettyprint"> //保存之前的矩阵和属性 glPushMatrix(); glPushAttrib(GL_ALL_ATTRIB_BITS);
//往里移动并旋转 glTranslatef(0.0f, 0.0f, -5.0f); glRotatef(angle, 1.0f, 1.0f, 0.0f);
//画线框立方体,制造轮廓 glColor3f(1.0f, 0.0f, 0.0f); //设置线的宽度为3 glLineWidth(3.0f); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glutSolidCube(1);
//画实心立方体 glColor3f(0.0f, 0.0f, 0.0f); //设置多边形偏移,往屏幕外靠近观察点进行偏移 glPolygonOffset(-1.5, -1.0f); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glutSolidCube(1);
glPopAttrib(); glPopMatrix();</pre>
<p>得到的效果:</p> <p><a href="http://static.oschina.net/uploads/img/201312/21173612_8OJe.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://static.oschina.net/uploads/img/201312/21173612_JMoS.png" width="465" height="365"></a></p> <h4>使用模板缓冲区</h4> <p>模板缓冲区类似于深度缓冲区,图元在绘制到颜色缓冲区前,需要先经过模板测试。首先我们可以通过绘制一个实心的立方体来创建一个模板(把模板缓冲区对应的值设置为1)。然后再绘制线框立方体,设置模板测试函数为不等于1的才通过测试,这样就能得到一个立方体的轮廓(去除掉了中间的线,因为中间的线不能通过模板测试)。</p> <p>代码如下:</p> <div> glPushMatrix();<br> glPushAttrib(GL_ALL_ATTRIB_BITS);<br><br> //清除模板缓冲区<br> glClearStencil(0);<br> glClear(GL_STENCIL_BUFFER_BIT);<br><br> //一开始设置为总是通过模板测试,建立模板<br><br> glStencilFunc(GL_ALWAYS, 1, 0xFFFF);<br><br> //模板测试失败时与模板测试通过但深度测试失败时,模板的值保持不修改.<br> //通过模板测试与深度测试时,使用上面glStencilFunc指定的<span style="color: blue">ref</span>(1)替换掉<br><br> glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );<br> glEnable( GL_STENCIL_TEST );<br><br><br> glTranslatef(0.0f, 0.0f, -5.0f);<br> glRotatef(angle, 1.0f, 1.0f, 0.0f);<br><br> //绘制实体的立方体<br> glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);<br><br> glColor3f(0.0f, 0.0f, 0.0f);<br> glutSolidCube(1);<br><br> //设置只有模板缓冲区值不为1的,地方才通过模板测试<br> glStencilFunc(GL_NOTEQUAL, 1, 0xFFFF);<br><br> //绘制线框立方体<br> glLineWidth(3.0f);<br> glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);<br> glColor3f(1.0f, 0.0f, 0.0f);<br> glutSolidCube(1);<br> <br> glPopAttrib();<br> glPopMatrix(); </div> <p>效果:</p> <p><a href="http://static.oschina.net/uploads/img/201312/21173613_ceEP.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://static.oschina.net/uploads/img/201312/21173613_zus9.png" width="244" height="192"></a></p>
<p>参考: <a href="http://www.codeproject.com/Articles/8499/Generating-Outlines-in-OpenGL">http://www.codeproject.com/Articles/8499/Generating-Outlines-in-OpenGL</a></p>