理解Stencil Buffer可不是件容易的事,正确灵活的运用就更有技巧性了。Stencil主要是在Reflection、Refraction、Shodow creation应用很广。网上也有一些示例,就Planar Reflection有固定的模式,但知其然不知其所以然就无法创建更复杂多变的场景了。下面就最简单的Planar Reflection说说吧。
Beware that reflection will reverse vertex ordering for the faces in the object, so the face culling state for the reflection scene must be set to the reverse of the state for the original scene。必须注意的是,反射将反转物体表面的顶点顺序,所以反射场景中物体表面的culling state必须置为原始场景中culling state的相反值。
如何得到相对reflector的virtual object呢(这是相对视点独立的操作,只考虑物体和反射面的相对位置)?这个过程可以分解成三个步骤:
1、 把物体转变到反射面(reflector)的局部坐标系中,再把反射面平移至原点,并旋转使得反射面和XY平面重合。
2、 glScalef(1.0f, 1.0f, -1.0f);
3、 第一步变换的逆操作,使反射面回到原来位置。
相关代码列与下面。
通过反射矩阵(Reflector Matrix)也可求得virtual object:
![](http://blog.csdn.net/images/blog_csdn_net/xukunn1226/img248.gif)
P为反射平面上的一点,V是垂直反射面的向量。物体顶点乘以R得到反射物体。
我们可以做个小的事例。假设,反射面就是XY平面,则V=(0,0,1);取反射面上的任一点为P=(2,1,0),则P·V=0;可以求得反射矩阵R为:
| 1 0 0 0 |
| 0 1 0 0 |
| 0 0 -1 0 |
| 0 0 0 1 |,当物体顶点乘以R时只是改变了其Z方向,和上面方法的第二步glScalef(1.0f, 1.0f, -1.0f);效果一样。由此可以知上面两种求virtual object效果一样。
在得到反射物体的虚顶点(virtual vertex)后,就该对场景中的反射物体进行渲染了。主要的工作是对反射物体的剪切,使其只能在反射面内可见。方法一般有两种:对模板缓存(Stencil Buffer)的使用,或使用纹理图投影到Reflector上,用Reflector的边界对反射图象进行剪切。
使用模板缓存对反射物体进行剪切比较好理解。
//先把reflector区域的stencil buffer设置为1,其他区域为0,且不显示出来
glColorMask(0,0,0,0);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glMultMatrixd(reflector->matrix());
DrawFloor();
DrawFloor2();
glPopMatrix();
//渲染virtual object,使物体转变至反射面的局部坐标系中
virtualObject->SetParentFrame(dynamic_cast<Frame*>(reflector));
virtualObject2->SetParentFrame(dynamic_cast<Frame*>(reflector));
virtualObject->SetPositionAndOrientation(object->position(), object->orientation());
virtualObject2->SetPositionAndOrientation(object->position(), object->orientation());
// glScalef(1.f, 1.f, -1.f)实现反射的过程
// glPushMatrix()/glPopMatrix()实现步骤三
glPushMatrix();
glMultMatrixd(reflector->matrix());
glScalef(1.f, 1.f, -1.f);
glMultMatrixd(virtualObject->matrix());
DrawObject();
glPopMatrix();
glPushMatrix();
glMultMatrixd(reflector->matrix());
glScalef(1.f, -1.f, 1.f); //注意:DrawFloor2()绘制在XZ平面,所以在Y方向反射
glMultMatrixd(virtualObject2->matrix());
DrawObject();
glPopMatrix();
glDisable(GL_STENCIL_TEST);
//渲染场景的其他部分
glEnable(GL_BLEND);
glColor4f(1.f, 1.f, 1.f, 0.8f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPushMatrix();
glMultMatrixd(reflector->matrix());
DrawFloor();
DrawFloor2();
glPopMatrix();
glDisable(GL_BLEND);
glPushMatrix();
glMultMatrixd(object->matrix());
DrawObject();
glPopMatrix();
实现后效果图如下:
![](http://blog.csdn.net/images/blog_csdn_net/xukunn1226/reflection.jpg)
![](http://blog.csdn.net/images/blog_csdn_net/xukunn1226/REFLECT.jpg)
当物体穿越反射面时,对应的virtual object也会从反射面穿出。NEHE解决这种问题的方法是定义个Clip Plane(平面由方程Ax+By+Cz+D=0的系数确定),若反射面为非特殊平面,则A、B、C、D系数难以确定。可以考虑增加碰撞检测,使其不能穿过反射面。
see also: