这是我以前升级Ogre引擎,实现的一个方案,愿意拿出来分享下经验。
每个精通Ogre的程序员,都会对Ogre自带的阴影方案感到失望,不管是模板阴影,还是投影阴影,都是固定渲染管线的阴影,效果都不好。
畅游《天龙八部》都升级到3了,还采用以前的阴影方案,说明要找个理想的方案替换以前的真是很难。
天龙的方案是,人物用模板阴影,场景用静态烘培的阴影贴图贴地形上。效果一般般,这是以前很多游戏的做法。由于模板阴影是基于模型顶点的,没法支持alpha test。
那么树叶这种带有alpha test的模型的阴影用不了。而静态烘培的光照图,精度决定了用在树木这种模型上的效果很不好,而且都不支持自投影,
而投影阴影,本身有3个缺陷,比如反向投影,无自投影,和地形穿插的投影有穿帮等bug,也不太实用,很早就被天龙抛弃了。
固定管线的阴影方案迟早都要被淘汰,而基于shader实现的shadow map阴影技术是比较主流的高级阴影技术。
shadow map的技术分支,就太多了,几十种都有,但每种技术都是不完美的,也有各种各样的问题。国外次世代游戏多用VSM和CSM这种。
我要考虑的前提效率。 效率必须是第一位,在国内开发引擎,任何NB的效果,如果效率有问题,就没任何适用价值。
权衡一下,结合了自己2.5D锁视角游戏的实际情况,我选择了传统的depth shawow map。
传统的阴影很多问题,需要很多技术来改进才有实用价值。
比如阴影精度问题,边缘锯齿,某些视角阴影自走样等等。
改进质量的方法:
1)摄像机聚焦到可视区域,提高阴影的精度。
2)阴影纹理大小采用1024或2048来提高阴影的精度。
3)由于看不到很远的地方,可以控制阴影距离,再次提高阴影精度,如果是纯3D游戏大场景,能看很远,就需要换成PSSM技术。
4)4X4的PCF滤波来实现简单的软阴影,消除生硬的边缘。
5)采用基于梯度的深度偏移减少自投影走样
改进效率的方法:
1)只支持单光源的阴影,那么就不需要迭代每个光源,阴影贴花的时候采用modulate的方式,而不是add,这样只需增加一个纹理单元,而不会增加渲染pass
,由于一个pass,那么阴影和雾也可以完美结合。
其他要解决的问题
1)树木这种alpha test的模型的投射和接受阴影。
效果图
阴影质量令人满意,且支持自投影,alpha test的竹子,也支持阴影。以上效果都在我笔记本上用地形编辑器测试的
效率方面,和传统的投影阴影相比,也是渲染一张阴影图,在贴花方面并没有增加渲染批次,材质脚本也简单
// 2012.12.5 luoyinan // 阴影投射材质 浮点纹理 material Ogre/DepthShadowmap/Caster/Float { technique { scheme shadow_map pass { fog_override true none vertex_program_ref Ogre/DepthShadowmap/CasterVP { } fragment_program_ref Ogre/DepthShadowmap/CasterFP { } } } } // 基本的自投影材质 material Ogre/DepthShadowmap/BasicTemplateMaterial { technique { scheme shadow_map pass { // Vertex program reference vertex_program_ref Ogre/DepthShadowmap/ReceiverVP_Entity { } // Fragment program fragment_program_ref Ogre/DepthShadowmap/ReceiverFP_Entity { } texture_unit { content_type shadow tex_address_mode border tex_border_colour 1 1 1 } texture_unit { texture_alias <baseTexture> } } } technique { pass { diffuse vertexcolour texture_unit { texture_alias <baseTexture> } } } } // 支持alpha test和双面的 阴影自投影材质, material Ogre/DepthShadowmap/AlphaTestTemplateMaterial { technique { scheme shadow_map pass { alpha_rejection greater 128 cull_hardware none // Vertex program reference vertex_program_ref Ogre/DepthShadowmap/ReceiverVP_Entity { } // Fragment program fragment_program_ref Ogre/DepthShadowmap/ReceiverFP_Entity { } texture_unit { content_type shadow tex_address_mode border tex_border_colour 1 1 1 } texture_unit { texture_alias <baseTexture> } } } technique { pass { diffuse vertexcolour alpha_rejection greater 128 cull_hardware none texture_unit { texture_alias <baseTexture> } } } }