OpenG:Reflection-Stencil Buffer

OpenG:Reflection-Stencil Buffer
每个像素都有自己对应的 Buffer,其实就是一个 32bit 的数,如 Color Buffer, Depth Buffer, Stencil Buffer. Stencil Buffer 与 Depth Buffer 有点特别,因为他们共用同一个 Buffer, Depth Buffer 占用 Buffer 前面的 24Bit, Stencil Buffer 占用后面的 8Bit. Stencil Buffer 可以使用从 1Bit-8Bit. 如在绘制反射时,就像照镜子一样,因为只需要在反射平面上绘制物体的镜像,即要么在反射平面上绘制,要不就不绘制,所以只需要用到 1Bit 的 Stencil Buffer.
 
什么叫 Stencil Buffer ?
即是一个模板,也就是说,他可以是一个平面,也可以是一个立体几何图形,如一个四边形,一个Teapot. 在模板所占据的空间中,他的值为 1(values stored in the stencil buffer), 在启用 Stencil Buffer 时,我们所画的图形只有在这个空间中的部分才能显示出来,所以我们可以创建一个模板,他是一个字,然后以后画的图形最多只能把这个字给显示出来,这个图形有其他部分都没有被写进 Color Buffer.
 
Stencil Buffer 最简单的运用,用来生成镜面反射。
1. 先要使编程环境支持 Stencil Buffer
        glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
 
2. 设置清除 Stencil Buffer 使用的函数
        glClearStencil(0);
 
3. 在我们创建模板的时候,要先关掉 Depth Test und Color Mask,
         因为我们并不想把模板画到屏幕上
        glDisable(GL_DEPTH_TEST);
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
         // 为了把我们的模板图形不显示到屏幕上,但又要写入 Stencil Buffer 中。
 
        我们什么时候创建模板的?就是在启用模板缓存后进行的第一次进绘制的图形就是模板。
        OpenGL会根据我们所设定的 glStencilFunc 的值和 glStencilOp 来比较,
        然后在 plane 中(即视口所对应的那个二维数组)写入比较的结果值。
 
4. 开始创建模板
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0x1, 0x1);
         // 把模板图形所在的区域的 Buffer 值设置成 1, 其余的还是 0.
        // 这时用的就是给 glStencilFunc 指定的 ref 的值,现在是 1
         // 当然可以有其他的操作,如 GL_INCR, GL_INVERT(bitwise invert)
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
       
         // 开始创建模板的图形
        drawFloor();
       
         // 模板创建好后,我们就要设置下一次进行绘制时的模板函数
        // 只有通过条件的像素才能被显示到屏幕上,否则就被丢弃
        // 但要注意,现在我们要进行绘制的就是镜像了,所以是要被显示到屏幕上的,
        // 所以在绘制之前,要把颜色屏蔽关掉和启用深度测试
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
        glEnable(GL_DEPTH_TEST);
 
        // Stencil buffer 值等于 1 的地方才绘制到屏幕上       
        glStencilFunc(GL_EQUAL, 0x1, 0x1);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
 
5. 绘制镜像图像
        // 现在绘制我们的镜像图像
        // 镜像是跟原来的物体对称的, 所以用 glScalef 来进行反转,实现对称
        // 在绘制镜像物体的时候,灯光也要相应的反转
        glPushMatrix();
                glScalef(1, -1, 1);
                glutSolidTeapot(1.0f);
        glPopMatrix();
 
6. 在模板中显示的镜像图像已经创建好,不再需要模板了,所以我们关掉 stencil buffer
        glDisable(GL_STENCIL_TEST);
 
7. 绘制镜像所在的平面,就如镜子
        // 使用 Blend 与镜像图像混合起来
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawFloor();
        glDisable(GL_BLEND);
 
8. 绘制产生镜像的物体
        glutSolidTeapot(1.0f);
 
至此,真正的镜面反射已经创建完成。
非真正的反射可以如下实现:
先画对称物体,画出镜面(使用 Blend), 然后画出原物体,但这时如果旋转Camera,就会发现,那个对称的物体并不是平面的,还是原来的空间立体物体。但用 Stencil Buffer 实现的镜面反射是真正的镜面反射,镜像是只在镜面上显示,即是平面的。
 
下面的代码可以很好的工作
  //****************************************************************//
 if (useStencil) {
          glClearStencil(0);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 } else {
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }

 if (useStencil) {
          glDisable(GL_DEPTH_TEST);
          glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
          /* Draw 1 into the stencil buffer. */
          glEnable(GL_STENCIL_TEST);
          glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
          glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
          /* Now render floor; floor pixels just get their stencil set to 1. */
          drawFloor();
          /* Re-enable update of color and depth. */
          glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
          glEnable(GL_DEPTH_TEST);
          /* Now, only render where stencil is set to 1. */
          glStencilFunc(GL_EQUAL, 1, 0xffffffff); /* draw if ==1 */
          glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
 }
 glPushMatrix();
          glScalef(1, -1, 1);
          glTranslatef(0.0, 0.8, 0);
          glColor3f(0, 1, 0);
          glutSolidTeapot(1);
 glPopMatrix();
 
 if (useStencil) {
          glDisable(GL_STENCIL_TEST);
 }
 glEnable(GL_BLEND);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 glColor4f(0.7, 0.0, 0.0, 0.3);
 drawFloor();
 glDisable(GL_BLEND);
 
 glTranslatef(0, -0.0001, 0);
 glFrontFace(GL_CW);
 glColor3f(1, 1, 1);
 drawFloor();
 
 glTranslatef(0.0, 0.8, 0);
 glColor3f(0, 1, 0);
 glutSolidTeapot(1);
 //****************************************************************//
 

你可能感兴趣的:(OpenG:Reflection-Stencil Buffer)