Constructive Solid Geometry with the Stencil Buffer

CSG是从简单的实体的逻辑组合来构建复杂的实体的过程。基本的逻辑操作(如合并、相交和补集)用来作为三维实体之间的关系操作。在这些算法中,假设实体由多边形组成的封闭表面来定义。组成体元的多边形以一致的方式定义,这样每个多边形表面的正面均向外。CSG是计算机辅助机械设计过程的一部分,用来创建复杂的实体。
CSG可由模板缓存来创建,它可以被认为是一种光线追踪(ray-casting)技术,计数一实体表面的像素当实体表面投影到屏幕上时与其他表面相交的次数。这里讲的技术是图像空间技术,且限于可见的CSG实体,它们不产生可用于进一步处理的顶点列表,它们也往往给深度缓存赋予不与可见表面匹配的值。因此,CSG实体方法通常不能用于复杂的有深度缓存的场景。
创建CSG实体是一个多通道(multi-pass)技术。选择2个物体,发现两者之间的想要的布尔关系。基本的CSG模板操作是找出一实体表面在另一实体的体积的部分,逐渐绘制每个实体表面的片断,直到整个实体绘制完成。由CSG创建的实体作为组成实体的表面面片来渲染,不生成新的表面。
组件实体被渲染多次。实体的前和后表面在各自的通道中渲染,该过程要求将一表面渲染进深度缓存中,然后用其他实体(结合模板平面操作)来决定表面在其他实体内的部分,然后表面的内部或外部部分渲染到颜色缓存中由模板缓存中的值来控制。
这里是确定一实体表面的哪部分在另一个内部的步骤。注意这只是阴影体技术的变形。2个实体A和B,在这个例子中,我们演示如何找出A的正表面中在B的体积之内和之外的部分:
1. 用glColorMask()关闭颜色缓存;
2. 清空模板缓存;
3. 打开深度测试;
4. 用glEnable(GL_CULL_FACE)激活面的剔除;
5. 用glCullFace(GL_BACK)设置背面剔除;
6. 渲染A;
现在深度缓存已用A的正面更新。
7. 设置模板缓存为如果深度测试通过则递增:glStencilFunc(GL_ALWAYS,0,0);glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
8. 渲染B;
9. 设置模板缓存为若深度测试通过则递减:glStencilOp(GL_KEPP,GL_KEEP,GL_DECR);
10. 现在剔除B的正面:glCullFace(GL_FRONT);
11. 再次渲染B;
现在,在A的正面在B的体积内的地方,模板缓存包含一非0值。现在你可以用模板缓存来渲染A的正表面在B内的部分,或在B外的部分。
下面是如何处理内部的部分:
12. 设置模板缓存为若模板值不为0时通过:glStencilTest(GL_NOTEQUAL,0,~0);
13. 禁止模板缓存更新:glStencilMask();
14. 打开背面剔除:glCullFace(GL_BACK);
15. 关闭深度测试,因为它不需要;
16. 渲染A。
要绘制A在B外的部分,将模板测试从GL_NOTEQUAL改为GL_EQUAL。
注意尽管只有A的正面的一部分用于更新颜色缓存,A的所有正面都在深度缓存中。如果你试图将CSG实体与场景中的其他深度缓存的物体结合起来,将会出问题。
下面是使用上面所述的机制的3个基本的CSG操作:合并(Union)、相交(Intersection)和减(Subtraction)。
·合并(Union)
A和B的并集,意味着渲染A不在B中的部分和B不在A中的部分,要渲染2实体的合并,只需简单地用深度测试来排序渲染,不需模板缓存。
·相交(Intersection)
渲染A和B的交集,意味着渲染同属于A和B的部分。找出这种相交的算法为:找出B在A中的部分并绘制出来,然后找出A在B中的部分并绘制。它包括上面提到的基本的内部算法的2个通道(pass)。
最终结果是原始图元的已有表面的结合,不产生新的表面。因为结果只包含图元的正面,所以只有一实体在其他实体内的正面需要渲染。
步骤:
1. 关闭向颜色缓存的写入;
2. 清空模板缓存为0;
3. 打开深度测试;
4. 绘制A的正面;
5. 用glDepthMask(GL_FALSE)禁止写入到深度缓存;
6. 设置模板缓存为若深度测试通过则递增;
7. 渲染B的正面;
8. 设置模板缓存为若深度测试通过则递减;
9. 渲染B的背面;
10. 设置模板缓存为若模板值不为0则通过;
11. 关闭向模板缓存的写入;
12. 打开向颜色缓存的写入;
13. 关闭深度测试;
14. 再次渲染A的正面;
现在,A在B中的正面已被渲染,要完成相交,还需渲染B在A中的正面。但这里有一个问题,只有A的在B内的部分才被渲染到颜色缓存,但A的所有部分均在深度缓存中。A在B外的部分可能遮蔽掉B本来可见的部分。有许多方法可解决这个问题。我们的方法是重新渲染被B的正面覆盖的深度缓存的像素,设深度测试为总是通过。
15. 打开向深度缓存的写入;
16. 打开深度测试;
17. 改变深度测试为GL_ALWAYS;
18. 渲染B的正面;
现在,在B下的深度缓存值是正确的,需做的是找到B的正面在A中的部分并渲染。
19. 再次清空模板缓存为0;
20. 禁止写入深度缓存;
21. 设置模板缓存为若深度测试通过则递增;
22. 渲染A的正面;
23. 设置模板缓存为若深度测试通过则递减;
24. 渲染A的背面;
25. 设置模板缓存为若模板值不为0则通过;
26. 关闭向模板缓存的写入;
27. 打开向颜色缓存的写入;
28. 关闭深度测试;
29. 再次渲染B的正面。
当向颜色缓存写入时,禁止深度测试,以避免由A和B共享的共面多边形产生的问题。
·差(Difference)
实体A和B间的差(A-B)可认为是A与B的相反部分的交集。也就是:只绘制A中不是B的部分。根据模板算法,这意味着绘制A在B外的正面的部分,但比这还要复杂,为了创建看上去封闭的实体,我们也必须绘制B的部分,尤其是B在A中的背面部分。
算法:
1. 关闭向颜色缓存的写入;
2. 清空模板缓存为0;
3. 打开深度测试;
4. 绘制A的背面;
5. 禁止写入深度缓存:glDepthMask(GL_FALSE);
6. 设置模板缓存为若深度测试通过则递增;
7. 渲染B的正面;
8. 设置模板缓存为若深度测试通过则递减;
9. 渲染B的背面;
10. 设置模板缓存为若模板值不为0则通过;
11. 关闭写入模板缓存;
12. 打开写入颜色缓存;
13. 关闭深度测试;
14. 再次渲染A的背面;
在这个阶段,你已绘制A的背面在B中的部分,你已经标出了A将切除B的任何部分所形成的“洞”。现在必须绘制B的正面,用相同的技术来修正深度缓存问题:
15. 打开写入深度缓存;
16. 打开深度测试;
17. 改变深度测试为GL_ALWAYS;
18. 渲染B的正面;
现在,在B下的深度缓存值是正确的,所需的是找到B的正面在A的外部的部分并渲染:
19. 再次清空模板缓存为0;
20. 禁止写入深度缓存;
21. 设置模板缓存为若深度测试通过则递增;
22. 渲染A的正面;
23. 设置模板缓存为若深度测试通过则递减;
24. 渲染A的背面;
25. 设置模板缓存为若模板值为0则通过;
26. 关闭写入模板缓存;
27. 打开写入颜色缓存;
28. 关闭深度测试;
29. 再次渲染B的正面。
翻译自《Programming with OpenGL : Advanced Rendering 96》
--完――

你可能感兴趣的:(buffer)