1、将场景的深度值预先渲染到 以光源位置为原点、光线发射方向为观察方向的投影坐标系中,形成深度纹理。
2、再次渲染场景的过程中,将每个片断(像素)变换到前述眼坐标系中,并缩放到[0,1]的范围内以便查询纹理。
3、以较暗的光照绘制场景
4、以当前片断在眼坐标中的S、T坐标查询深度纹理获得深度值,将此深度值与当前片断的R坐标进行比较,若R坐标大于深度值,则当前片断在阴影中;否则当前片断受光照。
下面讲述如何使用OpenGL实现该算法;
首先我们先介绍如下几个数学类,提供对矩阵的一些基本运算,用起来很方便:
Matrix4x4-----------4x4矩阵类,提供对4x4矩阵的基本运算支持,并重载了与float*之间的转换,可以被OpenGL的函数直接调用
Vector2d,vector3d,vector4d-------提供了对二维,三维,四维向量的基本运算支持,同样重载了float*转换
Glee库,提供对显卡所支持的OpenGL扩展的方便查询.
好,现在我们开始我们的shawmap之路:
1, 定义如下全局变量,用来保存相应的状态数据:
如下变量分别为聚光灯的发散度矩阵,光源位置矩阵,相机的视景体矩阵,模型视图矩阵,由于接下来要将相机在光源与眼坐标中来回转换,因此需要保存这些数据
好现在我们回顾shadowmap的基本算法,它需要首先将镜头切换到光源所在位置,得出看到的场景每个点的深度值,然后回到视点,将视点状态下的所有片段与光源的距离和刚才保存的对应像素点进行对比,大了就涂黑,小了就接受.那么,我们如何实现呢.这里我们采用深度纹理映射的方法,将光源处渲染的场景拍张照,并以深度纹理的方式保存,深度纹理保存了场景中每个片段的深度信息,当然与之对应的,就是光源到片段的距离.然后,在回到视点进行渲染,我们找到视点中每个顶点与纹理图中对应点的变换关系,然后将纹理图映射到模型中,进而采用适当的映射函数和方法,就可以将阴影部分扣除或者涂黑了.
好,我们发现,每次渲染时,相机要在光源和视点之前 来回的切换,变来变去很麻烦,我们知道,调整光源位置和调整相机位置的方法都是将一个特定的变换矩阵与对应的当前矩阵左乘,由此,我们先将定义光源处相机转换的矩阵和视点处相机的矩阵求出,在每次切换的时候,应用OpenGL的glLoadMatrix函数,将对应的矩阵加载进去,就可以啦~
下面介绍如何求出这两组矩阵(包括模型视图矩阵和投影矩阵)
先把代码copy下来:
上面这几行代码所依据的就是,在我们每次使用gluLookAt,glRoatef等函数是,OpenGL内部的实现机理都是根据传入的参数生成对应的变换矩阵,然后将之与当前矩阵相乘.这样,我们在计算这两组矩阵的时候就可以告诉OpenGL我们怎么变换然后将OpenGL所计算出的变换结果使用函数glGetFloatv读出来就可以啦.经过以上分析,我们很容易就可以理解,为什么上述的每个函数中的代码都在glPushMatrix和glPopMatrix对中,因为我们只是要求OpenGL帮我们计算,并不吧相应的变换结果应用到绘画中而且只是把OpenGL作为一个专业计算器使用~
接下来,我们在init函数中进行一些必要的初始化设置,这里我们要开启深度测试,并给出必要的纹理映射最初的设置,并且暂时不开启光照并且前述的俩个矩阵也需要在这里执行一次用来获取最初的矩阵.
紧接着,我们就开始介绍最核心的部分啦~
首先我们在光源处进行第一次绘制:
这里我们分别在投影矩阵和视图矩阵中两次使用glLoadMatrix来加载之前所计算出的矩阵:此时,相机变被设置到了光源位置,这里,我们剔除前表面,这样可以保证用一个物体不会被扣除后面的部分.继而我们采用单调着色并且关闭颜色绘制,来绘制场景,这样可以节省些许性能,继而,我们恢复着色模式,来时渲染更加的逼真并载入视点矩阵,
为了使阴影更加逼真,不至于黑乎乎的什么都看不见我们先使用较暗淡的环境光绘制一遍场景,这样,阴影部分就会看起来只是暗一些.而不会被完全涂黑:
接下来,最关键的部分,找到视点处场景中片段与纹理图中对应片段的关系,我们模仿OpenGL的绘图顺序进行转换.注意到,纹理空间中所有的点坐标都是在[0,1]之间的,而视觉坐标下它的范围在[-1,1]之间,因此我们应该进行移动和缩放这个矩阵比较简单,直接写下就可以了:
进而,我们计算出相应的变换矩阵:
MATRIX4X4 textureMatrix=biasMatrix*lightProjectionMatrix*lightViewMatrix;
然后使用OpenGL自动计算纹理坐标的函数来制定纹理坐标:
并制定纹理映射函数和方法并绘制:
到此为止,阴影图中绘制完毕,我们要注意的是,由于相机在光源和视点之间切换时依赖的是前述的两组矩阵,因此我们要保证在绘画时投影矩阵与模型视图矩阵与上述俩个矩阵是一致的,所以在绘画时在应用变换后应当将变换都放在glPushMatrix和glPopMatrix对之间.
效果如下图:
这篇文章是按照我老是的要求写的文档,之前老师说写文档有很多好处,我开始还不是很相信,现在发现确实是那样,有些东西在代码里面好像是动了,但是当用自己的话组织的时候才发现,我还是有很多模棱两可,辗转大一大二和半个大三,除了java,什么语言就转了一遍,做了两年的信息系统,默然回首,突然发现,c++才是真正的王道,算法精通才是真正的实力.悔不该,当初急功近利跑出去做哪些貌似很高级的东西,而把数学这门一生都要重要的神器落下,如果有大一大二的学弟,记住了,落什么课都可以,千万要把数学学好!