所谓planar shadow就是用一个投影矩阵(不是opengl流水线所谓之projection matrix,而是根据光源和投影面位置将一个点投射到投影面上的矩阵),将模型的所有顶点投射到投影面(比如地面)上。一般或者直接使用模型经过skin动画计算好的mesh进行投影并绘制,或者使用一个低模独立计算mesh然后投影绘制。无论哪种方法,都有一个问题,由于影子是很多三角形重叠在一起的,如果使用半透明材质绘制,众多的三角形会重叠在一起非常难看,且会有z-fighting现象闪烁不止。。当然如果有stencil buffer,可以使用stencil buffer进行mask,如果没有呢?确实在某些平台上没有stencil buffer,往往只能通过depth buffer做一些有限的模拟,本文所提的方法就是一例。
------------------------------------------
绘制过程:
1)首先绘制场景里所有其他东西
2)用0清除depth buffer:
glClearDepth(0);
glClear(GL_DEPTH_BUFFER_BIT);
3)确保深度测试开启,z write允许,并且设置depth func为always
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_ALWAYS);
4)禁止color buffer写入:
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
5)绘制影子(solid材质,无纹理,无blend)
经过上面的设置,实际上只会更新depth buffer, 写入z buffer时,默认的depth range是0~1,所以没有影子的地方,z buffer中是0,有影子的地方是一个大于0的值,因为影子一般不可能离near plane很近很近,所以这个值应该>0。
6)将model-view和projection矩阵设为单位阵
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
7)重新开启color buffer写入,depth func为LEQUAL, 并关闭depth buffer的写入
8)绘制一个全屏的矩形,开启blend,设置矩形顶点颜色为半透明黑色
矩阵顶点坐标为:
glVertex3i (-1, -1, -0.99);
glVertex3i (1, -1, -0.99);
glVertex3i (1, 1, -0.99);
glVertex3i (-1, 1, -0.99);
绘制这个矩形时进行深度测试LEQUAL,z值-0.99是clip space中的值,通过depth range的映射,最终的z值是>0但很接近0的值,而影子应该是离near plane比这个面更远的(即z值比这个面的z值要大),所以经过LEQUAL的测试,画这个矩形的时候,z buffer上有影子的部分通过深度测试,而z buffer为0的地方不通过所以不绘制。而且由于启用了blend,矩形的像素还要和color buffer上的已有颜色进行混合,达到半透明效果。这样半透明的矩形被影子mask出来了,就得到了半透明的影子。
另外,为啥是-0.99而不是-1呢?因为clip space, -1就是在near clip plane上了,窗口坐标映射后的z值是0,0==0就能通过LEQUAL的测试了就会全部画出来了。。但是如果使用LESS测试,-1也就可以用了。
注:以上代码仅作参考,实际调用的都是引擎的接口
当然这个方法不完美,毛病也很多,比如影子会遮住人的脚,平台边缘无法裁掉影子等
ps:复习一下向z buffer写入z值的整个过程:此Z非x,y,z的z,而是eye space, clip space, NDC, windows space的z。通过modle-view变换,相机被变换到默认位置,即view为-z,此为eye space。我们在世界坐标系下,往往采用z up,所以此z非彼z,无论世界坐标z表示什么,到eye space之后,z值就表示深度了,所谓深度就是指离camera的距离。从eye space经过投影变换,到达clip space, clip space中(x,y,z)都被变换到[-w,w]的范围,再经过透视除法,x,y,z都除以w,使得x,y,z都在[-1,1]之间这就是NDC了(归一化坐标)。然后再经过viewport变换,变换到窗口坐标,此时z值会根据depth range进行映射,默认的是映射为[0,1]。clip,透视除法和视口变换都属于图元装配过程,之后是光栅化阶段,图元被光栅化为片段(fragment),在光栅化阶段,为了降低zfighting现象,可以使用polygon offset, polygon offset是在进行深度测试和写入zbuffer之前,将计算好的z值加上一个偏移量,如果深度测试通过,会将原始深度值(而不是加上offset之后的z值)写入zbuffer。也就是说无论是否使用polygon offset,最终写入zbuffer的z值是视口变换之后通过depth range映射的windows coordinate z值,但polygon offset会对depth test产生影响。
分享到:
相关推荐
OpenGL3.3_StencilBuffer_Outlines.rar
用Stencil Buffer实现在UGUI上嵌入3D模型
Optimized Stencil Shadow Volumes Optimized Stencil Shadow Volumes Optimized Stencil Shadow Volumes
D3D111_StencilBuffer
该示例程序用于学习D3D11中模板缓冲区的使用,通过使用它来实现一个平面镜面反射的效果。 操作说明:鼠标左键按下旋转镜头,右键按下调整镜头远近。 文件中包含整个示例程序及框架,以及可执行程序。如果exe不能正常...
Stencil是Swift的一种简单而强大的模板语言。 它提供类似于Django和Mustache的语法。 如果您熟悉这些内容,您将对Stencil感到宾至如归。 例子 There are {{ articles.count }} articles. {% for article in ...
octin stencil
Spacedock stencil
Viking Stencil
VMware Visio stencil 20.11
directx9.0利用模板绘制镜子反射,和墙壁不反射
VMware VISIO 專用 Stencil2 強烈推薦~~
Graff Stencil iPhone_WebApp_WireFrame,非常不错的原型设计插件和模版。
必做部分: 学习了一种建模软件:3DS Max的基本功能,并采用其对三维场景进行建模,对物体进行贴图。... 利用模板缓存(stencil buffer)技术实现镜面效果,并且要在镜子中显示物体。 实现全局光照。 等
良心推荐我平时用到的几个非常有用的visio几个有用的模板 stencil包括: 流程图、ui控件、电气、机械制造
尤其是在数值方法中 [1] ,有限差分法是一种常用的数值解法,它是在微分方程中用差商代替偏导数,得到相应的差分方程,通过解差分方程得到微分方程解的近似值。地球物理正演模拟时常应用这种方法。
AG-Stencil字体我个人觉得还不错,跟大家分享下!
常见印刷机对stencil要求一览表 对于常用的锡膏印刷机,对钢网的设计的一些注意的细节。
这是一个用于使用Stencil构建独立Web组件的入门项目。 模板也非常适合构建整个应用程序。 为此,请改用 。 模版 Stencil是用于使用Web组件构建快速Web应用程序的编译器。 Stencil将最受欢迎的前端框架的最佳概念...