2006年10月3日

Shadow Volume详解(1)

利用每天夜里的一部分时间写一下,这也是热爱吧。主要是翻译reference(1)里的,也讲一下Nehe的实现。现在使用的还是z-pass方法,以后会把z-fail,Nvidia 的改进z-pass乃至soft shadow加进来。
感谢两个人的话:
DancingWind:
如果我能让读者在读我的书的时候节省一分钟,那么这个班上有60个同学,我就节约了一个小时。好的教程往往可以事办功倍。
Emilmatthew:让更多人受益。
虽然我能力所限做不出好的教程,但是会尽力的。
Reference:
(1).Volume Shadows Tutorial by John Tsiombikas
(2).Nehe OpenGL tutorial:lesson 27 by Nehe
Steps:

[1]原理

[原理]首先,讲一下阴影体原理

我会尽我所能的讲得清楚一些,但是我仍然希望读者队模板缓存(stencil buffer),深度(depth)有基本得了解。我自己得学习过程也遇到很多困难,所得到得经验就是要先把原理弄明白,而不是急于看别人写好的code。













这个图是很理想的,足够简单又能说明原理。黄色的点代表光源,红色的平面代表一个三角形的物体,紫色,蓝色,棕色的平面就是构成阴影体的面(实际上这三个面应该在被离光源的方向上继续延长的,现在还是继续使用它吧)。灰色的代表阴影投射到的平面。

我们一边渲染一边讲:
[首先把要绘制阴影之外的物体绘制],这也是一个原则阴影物体要最后被绘制,这样做的目的是为了获取深度信息。深度:depth也就是物体离视点的距离,深度信息通常用做遮挡判断。比如在三维空间里绘制了一前一后两个平面,就要通过深度信息来判断两个平面的前后位置,进而其中一个平面判断那些位置被遮挡了。绘制完成之后屏幕上每点的深度值都已经确定下来,然后使深度缓存不可写
比如:glDepthMask(GL_FALSE);(DX下有类似的做法)
但是允许深度测试:glEnable(GL_DEPTH_TEST);
测试函数为:glDepthFunc(GL_LEQUAL);
也就是说只有深度小于原象素的象素才会被接受。


[然后绘制阴影体]
先清空模板缓存:glClearStencil(0);
开启模板缓存测试:glEnable(GL_STENCIL_TEST);

设置测试比较函数为: glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
然后设置片元通过或者未通过模板测试时如何队模板缓存中的数据进行更改: glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);也就是通过测试时增1。
然后绘制阴影体的所有朝向你的面,阴影体是这样形成的,以光源为一个顶点,物体边缘的一条边为边的三角形无限延伸所形成的四边形(理论上讲应该是一个无限的,但实际上长用一个足够长的来模拟),本例中就是紫色和蓝色的面。容易发现,这两个面上的所有片元都可以通过测试
相对应位置的模板缓存的值都会加1。
但是相应位置的深度信息不变,因为深度缓存被设置为不可写。
结果如右图所示:淡蓝色的区域模板缓存值增1。

3 条评论:

goker 说...

什么叫"做学问",我大概知道了.

goker 说...

什么叫"做学问",我大概知道了.

yaker 说...

嗯,这个还是图形学里比较简单的算法。
可惜我可能不做researcher了,但是research还是要做的。