7 Shadows阴影
阴影渲染是很重要且很活跃的领域。渲染阴影的技术有很多,各有优点和缺点。为此Ogre提供了多种阴影实现,并提供大量的配置选项,以便根据场景选择最合适的方式。
根据阴影形状产生方式阴影可分为两类:模板和纹理。另外还由多种将阴影渲染到场景中的方法:
1 调制阴影(Modulative),将阴影区域变暗(将没有灯光照射的部分变暗,多个光源时,共同阴影会更暗;这样如果阴影调制色固定,灯光越多,阴影越暗。这种情况下,开灯的同时,需要同时变亮调制色)
2 灯光叠加(Additive),根据实际灯光计算阴影(比较真实的模拟,但每个灯光时需要一个渲染通道,相对费时)
使用基于纹理的阴影时还可以使用集成纹理阴影(Integrated)选项以通过单通道着色器实现完全的控制
开启阴影
1、 开启阴影应放在建立场景的第一步,因为阴影技术会影响mesh的加载方式。例:
mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE); //模板叠加阴影
2、灯光默认产生阴影,可以通过.setCastShadows(false)设置为不产生阴影
3、物体默认产生阴影,可以通过.setCastShadows(false)设置为不产生阴影
4、设置阴影最远投影距离,SceneManager/Light::setShadowFarDistance,限制距离是为了提高性能
5、设置材质的receive_shadows属性(默认on),或者Material::setReceiveShadows关闭阴影接收,比如:一些自发光物体;(不同于MovableObject::setCastShadows,仅针对单个物体)。透明物体默认不投射和接收阴影,但可通过 transparency_casts_shadows on设置为投射阴影[透明指背景色参与计算]。
有选择的关闭阴影
默认情况下,ogre将所有不透明物体作为阴影投射和接收体(根据所采用的技术,可能不会同时投射和接收)。可以通过以下方法关闭阴影:
1 关闭灯光投影, Light::setCastsShadows(false); 该灯光将不产生阴影
2 关闭材质阴影接收:Material::setReceiveShadows(false); 使用改材质的物体将不接收阴影
3 关闭单个物体投影:MovableObject::setCastsShadows(false); 该物体将不产生阴影
4 关闭整个渲染组:RenderQueueGroup::setShadowsEnable(false), 将关闭整个渲染组的投影和接收。因为 Ogre为了维持各组的渲染顺序,需要为每个组进行灯光设置,关闭阴影可以省去灯光设置消耗。Ogre默认自动关闭一些渲染组的阴影,包括:RENDER_QUEUE_BACKGROUND\OVERLAY\SKEIS_EARLY\SKIES_LATE。
7.1 模板阴影
模板阴影是一种基于模板缓冲区创建的遮罩的方法。模板遮罩可用于从屏幕上剔除后续的渲染,因此也可用于包含或者剔除阴影区域。因为模板标记只有有效和无效两种状态,所以模板阴影有清晰的边界,不可能软化边界。
为了产生模板,需要渲染从灯光拉伸引用阴影投影体的轮廓形成的阴影体(shadow volumes),与阴影体相交的地方,模板被更新,以便后续操作区分光照区和阴影。具体的操作依赖采用的技术:调制、叠加。物体既可以投射右可以接受阴影,模板阴影天生支持自阴影。
模板阴影的优点是可以在低端硬件上实现自阴影,如果需要渲染的多边形不是很多的话。采用纹理阴影实现自阴影则需要比较先进的硬件。基于这个原因,模板阴影比较适合需要精确投影并且需要支持低端硬件的场合。
模板阴影不利之处很多,特别是在先进的硬件上。模板阴影是基于几何体的技术,因此使用的多边形越多,代价约大,同时渲染阴影体的填充率也会相应增大,这不利于使用高细节的模型。因为先进的显卡倾向于使用大量的多边形,模板阴影因此会成为瓶颈。另外,视觉上模板阴影的硬边界特别原始,因为模板不支持篡改,你不大可能在其上做一些聪明的处理。所以,如果目标是高端硬件,你可能需要采用纹理阴影。
使用模板阴影需要考虑以下问题:
CPU负荷
计算网格的阴影体代价高昂,且不符合硬件加速特征,只能使用CPU,如果过度使用,将会产生CPU瓶颈。虽然Ogre可以有效的消除不能投影到视锥的物体,但这所能做的有限,而且巨大的、拉长的阴影(比如很低的阳光)也很难有些剔除。你需要避免同时渲染过多的投影物体和长阴影。可通过设置SceneManager.ShadowFarDistance消除远处阴影投射体的构造以节省时间,代价是只有近距离物体才有影子。最后,使用Ogre多层细节(LOD)特性,你可以使用代码自动生成LOD,也可以用网格工具如OgreXMLConverter\OgreMeshUpgrader生成。另外你可以通过指定另外的文件指定手工LOD。这些都可以节省巨大的阴影体计算时间。
延伸距离
顶点程序不可用时,Ogre只能将阴影体延伸有限的距离。如果一个物体很靠近光源,有限的延伸距离可能不大适合保证所有物体被正确的阴影。所以如果可能,你应该避免让物体过于靠近光源,除非目标硬件支持顶点程序(此时Ogre可以使用顶点程序拉伸无限的阴影体)
远截面位置
模板阴影不可被远截面裁剪,否则会出现顶部被裁剪的伪造效果。当你开启模板阴影时,Ogre内部将设置相机远截面为无穷大 (Camera::setFarClipDistance(0))。这可以避免阴影体被裁剪,代价是很小的深度精度损失。
网格边列表
模板阴影只有在网格存在边列表时才能计算。Ogre的导出工具可自动创建边列表。可以使用OgreMeshUpgrade \ OgreXmlConverter或者调用Mesh::buildEdgeList。如果没有边列表Ogre认为其不支持投影
轮廓边界
模板阴影通过计算网格轮廓边界,然后通过映射形成阴影体。这意味着网格体上,在光照和阴影区之间存在明确的边界,即一组边,边一侧的三角面都朝向灯光,另一侧的三角面背向灯光。在变换时这会在网格体上产生明显的边界。如果只有少量的光,而且网格体有平滑的法线以产生渐变的光照,轮廓边界将会不可见,而且网格体分割越高效果也越好。如果场景中含有环境光,差别将会很显著,特别是采用调制阴影的时侯。因为这种简化的方式并没有考虑各阴影区的光照贡献,所以采用两个或以上灯光时不建议使用调制模板阴影,不然外边框会很显著。灯光叠加不会这么糟,因为每个灯光单独屏蔽,意味着只有环境光能暴露轮廓边界
现实考虑(be realistic)
不要期望任何场景在使用任何硬件的时候都能直接利用模板阴影算法得到完美的速度。阴影是复杂昂贵的技术,你可以对灯光和物体的摆放强加一些适当的限制。虽然这些都限制不是必须的,但也不是完全没有代价:
l 避免让物体离光源太近甚至穿过光源,这看上去可能很酷,但在不支持顶点程序的硬件上会产生伪造的效果。
l 阴影并不遵守其中的物体的“固体性”,如果阴影中的物体不投射阴影,你仍能从物体的另一侧看到阴影(不受阻挡)
l 使用SceneManager::setShadowFarDistance限制构造阴影体的数量
l 使用LOD减少远距离阴影体的构造复杂度
l 避免长阴影(黄昏黎明),他们会激化阴影体裁剪、填充率问题并导致很远的距离也需要构造阴影体
Ogre对模板阴影的优化
尽管存在这些问题,模板阴影仍可以产生很好的效果(特别是灯光叠加方式),而且如果你遵守以上规则的话也会速度很快。另外Ogre提供了很多优化以使其尽可能的快。这一部分额外为对Ogre内部实现感兴趣的开发人员提供:
l 顶点程序拉伸
Ogre在支持顶点程序的硬件上通过硬件拉伸阴影体。这里有两个优点:最明显的一个是速度,第二是顶点程序可以无限拉伸顶点;固定管线无法做到这一点,至少也需要软件做一些计算。这提供了健全的阴影体,而且在方向光下也消除了近一半的三角形,因为在无穷远,所有顶点都映射为同一个点
l 裁剪测试优化
Ogre使用裁剪矩形限制了不能覆盖整个视口的点/聚光灯光源的影响 。这可以节省渲染模板体的填充率,特别是在远离光源的时候
l Z_Pass和Z-Fail算法
Z-Fail算法通常认为归功于John Carmack,用于相机穿过阴影体时也能得到正确的结果。Z-fail算法相比传统的z-pass算法代价昂贵,因此Ogre仅在需要的时候使用Z-Fail算法,其它时候则使用Z-pass算法
l 双面模板和模板环绕(wrapping)
Ogre支持双面模板\模板环绕扩展,这可以通过一个通道实现模板体绘制,而不是背面和正面各一个通道。这并不能节省填充率,因为产生同样的模板更新次数,但可以节省图元装配和每次渲染调用的负荷
l 有效的阴影体裁剪
Ogre可以很漂亮的检测到影响视锥的光源,以及哪些物体可以投射阴影到视锥。这意味着不需要构造不需要的阴影几何体。设置shadow far distance可有效的提出远距离阴影体,这在实际中是很有用的,因为你一般只对近处物体的阴影感兴趣
7.2 基于纹理的阴影
纹理阴影包括从灯光位置渲染阴影投射到纹理,然后将该纹理映射到阴影接收体。相比模板阴影,纹理阴影不需要三角面计算,纹理渲染工作大部分由GPU完成,从而可利用先进的图形卡带来的好处。另外,纹理阴影更易于定制,因为可以在着色器对其进行处理(特别是集成纹理阴影,你可以执行过滤实现软阴影或其它特效)。因为功能非常强大,而且不断增长的GPU性能快速分摊了填充率和纹理采样的消耗,因此最新的引擎大都采用纹理阴影作为主要的阴影渲染技术。
纹理阴影的主要缺点是纹理存在固定的分辨率,当拉伸时,纹理的像素现象会非常明显,这可以用以下方式改善:
选择合适的投影
最简单的是采用普通的基于灯光视角渲染阴影体,不过这样效果会比较差,有很多依据主渲染相机视角提高渲染质量的方法。Ogre通过ShadowCameraSetup接口提供插件式投影支持,并提供了四种实现: Uniform(最简单的,仅基于灯光视角), Uniform Focussed(仍然是普通的投影,不过聚焦在主相机观察方向), LiSPSM(Light Space Perspetive Shadow Mapping 灯光空间投射阴影图,聚焦并依据主相机扭曲投影矩阵,Plan Optimal(依据阴影平面优化阴影), PSSM(Parallel Split Shadow Map平行分割阴影图,在LiSPSM基础上多次渲染)
过滤
你可以通过多次采样的软阴影以优化质量。PCF(Percentage Closest Filteing相邻百分比过滤)是最流行的做法,根据采用的个数和模式存在多个变种。Ogre提供了5-tap PCF深度阴影图例子。
采用更大的纹理
因为GPU越来越快,显存越来越大,你可以采用更大的纹理
混合采用以上3种技术,你可以得到很高的阴影解决方案
还有个问题是点光源。因为纹理阴影需要从灯光方向渲染阴影到纹理,全方向灯(点光源)需要6次才能渲染所有可能的方向。因为这个原因,Ogre主要支持方向灯和聚光灯。如果使用点光源,必须在其位于相机视锥之外(off-camera)因而可以转换为聚光灯时才能正常工作。
方向灯
方向灯理论上是从无穷远距离渲染整个场景阴影。Ogre默认纹理投影中心为主相机正前方。阴影图的范围(Light::setShadowFarDistance)和阴影中心点距离主相机的偏移量都可以配置(偏移量用于计算投影中心点位置:cam.pos + cam.dir * SceneManager::setShadowDirLightTextureOffset * Light::setShadowFarDistanc)。在阴影的边缘,Ogre基于可配置的参数淡出阴影(SceneManager::setShadowTextureFadeStart/End)实现软阴影。
聚光灯
聚光灯很容易渲染纹理阴影,因为其本身就是一个视截体。Ogre直接用聚光灯的方向、位置渲染纹理,视角则采用聚光灯falloff角。因为渲染的阴影纹理是个矩形,Ogre采用第二个圆形阴影纹理混合实现阴影的淡出和圆形的阴影效果。
点光源
因为点光源阴影需要多次渲染(立方体的6面,或者低精度的两个抛物面映射),因此Ogre对点光源近似为聚光灯处理:默认为从点光源位置照射到纹理中心(参考方向灯)的120度聚光灯。
阴影投射和接收
开启纹理阴影需要使用阴影技术纹理调制(SHADOWTYPE_TEXTURE_MODULATIVE)或者纹理叠加(SHADOWTYPE_TEXTURE_ADDITIVE)。最廉价最简单的阴影技术不使用深度信息,仅仅渲染投影体到纹理然后作为平常的颜色渲染到接收体(这意味着不能实现自阴影)。这是固定管线渲染兼容的纹理阴影技术(可用于低端硬件)。可以通过基于着色器的自定义投射和接收材质实现更复杂的阴影算法,比如深度阴影图(可以实现自阴影)。Ogre在阴影示例中提供了一个这样的演示,不过只能用于Shader Model2或更新的显卡。虽然opengl支持固定管线的深度阴影图,但Direct3d并不支持,因此为了跨平台需要使用基于着色器的投射和接收材质。如果采用这种办法,需要调用SceneManager::setShadowTextureSelfShadow(true)设置纹理投射体同时为阴影接收体。
如果你不使用深度阴影图,Ogre将阴影投射和接收分为两个组。关闭物体的阴影投射将自动将其变为阴影接受体(也可以通过将材质的receive_shadows选项设为false),同样,一个物体作为引用投射体将不能作为引用接受体。
注1:你可以在不使用深度阴影图时开启支持自身渲染,但因为没有深度信息,投影将会同时向光源反向渲染,从而导致封闭物体(大部分物体)都会处在自身的阴影之中,不自身投影虽不能完全解决反向渲染问题,但会好很多(特别是仅地面接收阴影的时候)
注2:Renderable::getCastsShadow默认为false(应该为true);而仅Entity::SubEntity\ StaticGeomotry::GeometryBucket\ InstancedGeometryBucket重载了实现,因而能保持与Movalbe::setCastShadow()一致;其它类都没有实现,意味着Renderable::getCastsShadow==false,从而会导致投射阴影物体在不投射自身时也不会被放到mSolidsNoShadowReceive组,从而会在阴影接收阶段作为阴影接收体进行阴影渲染 ;比如常见的ManualObject总是接收阴影。
纹理阴影配置
最大阴影纹理个数
Ogre在每帧渲染的时候为每个投射阴影的灯光使用单独的纹理,你可以关闭灯光的投射阴影属性。通过SceneManager::setShasowTextureCount可设置最大的纹理个数,当采用多个灯光时,超过数量的灯光将不投射阴影。你可以使用SceneManager::setShadowTextureSettings同时设置纹理个数和尺寸,因为单个调用都可能导致潜在的纹理资源创建和销毁
纹理大小
调用SceneManager::setShadowTextureSize可设置阴影纹理大小,纹理假定为正方形,大小应为2的指数倍。每个调制纹理消耗内存为:size*size*3
注:如果你使用Opengl渲染,只有硬件支持FBO(Frame Buffer Object帧缓冲区)或者PBO(Pixel Buffer Object像素缓冲区)时纹理大小才可以超过主渲染窗口大小。你可以通过Root->getRenderSystem()-> getCapabilities()->hasCapability(RSC_HWRENDER_TO_TEXTURE)检查是否支持,如果不支持,超过主渲染窗口的部分将为空白。
纹理最远距离
Light::setShadowFarDistance; 设置灯光阴影渲染最大范围,未设置时默认为主相机近截面的300倍,用于方向灯(默认也用于计算点光源退化为聚光灯时的光照方向)
纹理距离偏移比例
相对于纹理最远距离系数,SceneManager::setShadowDirLightTextureOffset 默认0.6,用于方向灯计算纹理中心位置(默认也用于计算点光源退化为聚光灯时的光照方向)
纹理渐变
相对于纹理最远距离系数,SceneManager::setShadowTextureFadeStart/End ,默认0.7/0.9,用于实现阴影的淡出,避免突兀的效果
纹理阴影相关顶点、片段着色器
当渲染阴影投射到调制纹理阴影时,Ogre关闭所有纹理和灯光,并将环境光设置为阴影色。对于叠加阴影,则改为渲染到黑色纹理。这对于固定渲染管线材质已经足够了,但如果材质使用了顶点着色器,Ogre就做不到那么多的控制。如果在第一个通道(Pass)使用了顶点着色器,你需要告诉Ogre在渲染阴影投射时使用哪个顶点着色程序。
自定义投影相机
Ogre默认采用DefaultShadowCamerSetuo投影,另外还提供PlaneOptimalShadowCameraSetup等实现,可以通过继承ShadowCamerSetup实现自定义投影。投影设置可以用于单个相机,也可以用于整个SceneManager
阴影纹理深度缓冲区共享
从1.8开始,深度缓冲区的使用和共享可以通过poolId指定。相同poolId的渲染目标尽可能共享深度缓冲区。阴影纹理默认的poolId为1,与渲染窗口和渲染目标相同。共享深度缓冲区可提高效率和内存使用,但有些情况下需要采用独立的pool,特别是shadow pass需要依赖之前的深度缓冲区内容,而不是进行清空的时候:
在Direct3D9中,当渲染窗口分辨率大于阴影纹理时会尽可能共享,小于的时候则不共享。阴影纹理还有可能和最大的(most) 其它渲染目标 (RTT)共享深度缓冲区(比如合成器)
在Opengl2.1 阴影纹理不能和主渲染窗口共享,而且不大可能和其它RTT 共享(比如合成器),因为opengl2.1要求纹理大小必须精确匹配,而D3d9只要分辨率小于等于就可以共享。
你可以指定不同的poolId避免与其它渲染纹理共享:
mSceneMgr->setShadowTextureSettings( size, count, format, PoolId );
mSceneMgr->setShadowTextureConfig( 0, 512, 512, PF_FLOAT16_R, 50 );
poolId为0表示阴影纹理不使用深度缓冲区,一般情况下不是期望的行为
集成纹理阴影
纹理阴影的主要优点是可以在着色器中引用。Ogre默认的纹理阴影模式(纹理调整、叠加)可自动渲染阴影,其缺点是采用附加到材质的方式会导致采用更多的通道。另外,也不能对阴影合成进行更多的控制。
通过集成纹理阴影可以对这些进行处理。默认的两种纹理阴影都对应相应的集成版本:集成纹理调制、集成纹理叠加。这两种方式不自动渲染阴影,而只是创建阴影纹理,然后要求你自己选择合适的方式渲染场景中的阴影接收体。这样做的不利之处是,你需要在接收体的材质中考虑阴影接收,有利之处是你可以完全控制阴影纹理的使用。最大的好处是你可以利用阴影实现复杂的着色,而且你可以混合通道从而使用更少的通道。对于集成阴影,调制和叠加唯一的区别是阴影区的颜色(调制为阴影色,叠加为黑色)。集成阴影不再有独立的调制通道,同时不再化分为环境光、各灯光最后贴花(decal)等,所有都由你提供的材质决定(可能需要调制、各灯光迭代、贴花等,但不必须)
通过在材质的texture_unit使用content_type shadow指令引用阴影纹理。所引用的纹理由同一通道中该指令的使用次数以及light_start选项或者基于灯光的通道迭代(light_based pass iterator)隐含决定,灯光的起始索引可能大于0
7.3调制阴影
调制阴影采用固定的颜色将已渲染的场景变暗。首先,场景渲染所有可以被加阴影的物体,然后对每个灯光执行调制通道将阴影中的的区域变暗,最后渲染所有不接收阴影的物体。
有两种调整阴影技术:模板调制、纹理调制。调制阴影是不精确的光照模型,因为其对阴影区域做相同的暗化,而不考虑灯光的衰减。不过调制阴影仍可以得到比较好的结果,另外相比“正确的”叠加阴影消耗更少,而且可以很好的与预烘焙的静态光照兼容,叠加阴影则不可以。需要考虑的是多个光源会导致阴影区过黑(阴影叠加的区域,直觉上可能是对的,物理上是错的),另外在采用模板阴影时边界过于税利。
阴影色
用于将阴影区变暗的颜色,用SceneManager::setShadowColour设置,默认暗灰(多灯光会过黑)
注:使用纹理阴影时,还可以选择集成纹理阴影,可以避免将阴影渲染作为单独的通道。这种情况下,“调制”仅影响纹理的颜色。
7.4灯光叠加
灯光叠加需要多次渲染场景,每次渲染一个灯光时屏蔽阴影中的区域。每个通道与之前渲染结果叠加,最终所有灯光精确反映了场景的光照,可以产生很现实的光照效果,但代价是更多的渲染通道
像许多技术所说的那样,渲染现实的光照需要多个通道。作为一个友好的引擎,Ogre帮助做了很过工作,让你可以用类似的材质定义光照。为了实现这个技术,Ogre自动将材质中的通道划分为三类:
1) 环境光通道(ambient),无任何光照。环境光始终作为第一个渲染的通道,初始化深度和环境光/自发光的成份。纹理只有在影响环境光的时候才渲染(比如环境光吸收图ambient occlusion maps)
2) 漫反射\镜面光通道(diffuse/specular),逐个灯光渲染,阴影区域不进行更新,渲染结果叠加(add)到已有场景。同样所有纹理不进行渲染,除非用于光照(比如法线图normal maps)
3) 贴花通道(decal),最终将纹理色与之前的环境光、漫反射\镜面光结果混合(modulate)
实际上,通道很少能划分到单个分类,为了维护分类,Ogre需要将用户定义的通道划分为多个照明(illumination)通道。
比如,下面一个简单的通道:
material TestIllumination
{ technique { pass {
ambient 0.5 0.2 0.2
diffuse 1 0 0
specular 1 0.8 0.8 15
texture_unit { texture grass.png }
} } }
Ogre将会将其划分为3个照明通道:
material TestIlluminationSplitIllumination
{ technique {
// Ambient pass
pass
{
ambient 0.5 0.2 0.2
diffuse 0 0 0
specular 0 0 0
}
// Diffuse / specular pass
pass
{
scene_blend add
iteration once_per_light
ambient 0 0 0
diffuse 1 0 0
specular 1 0.8 0.8 15
}
// Decal pass
pass
{
scene_blend modulate
lighting off
texture_unit { texture grass.png }
}
} }
可以看出,如果使用阴影技术,即便以上的简单材质也需要(num_light+2)个通道。对每个通道Ogre都自动会做环境光、漫反射\镜面光、纹理划分,因此无论采用什么光照方式都可以采用和以前一样的材质定义。
手工分类照明通道
如果很清楚材质如果在叠加灯光下工作,可以使用照明阶段(illumination_stage)选项指定通道直接对应一个照明通道: illumination_stage <ambient|per_light|decal>
通道分类和顶点程序
在叠加灯光时,Ogre可以很好的将你的通道划分为多通道渲染技术,而不需要你改变材质定义。但是在使用顶点程序时,正常的光照属性并没有使用(环境光、漫反射光、镜面光等),因为其完全由顶点程序内部决定,Ogre无法知道,因此需要明确指定。
方法也很简单,虽然顶点程序可能做了很多复杂的处理,但仍然可以划分为上面所说的三类。你需要的是像没有使用顶点程序一样使用通道属性(环境光、漫反射/镜面光、发射光)告诉Ogre处理的内容。这些信息只是供Ogre用来对通道分类,没有其它用途(渲染的时候才用到这些)。顶点程序将会保持原样(因此划分后的通道仍保持原有的顶点变换)
当Ogre划分diffuse/specular可编程通道时,会检查是或否需要逐个灯光运行(iteration onece_per_light)。如果是,通道将保持不变。如果没有指明这个属性,Ogre将分离逐灯光部分(per_light),并关闭片段着色器,因为没指定“iteration onece_per_light”,Ogre只能假定片段着色器用于处理贴花(decal),因此不能逐个灯光运行。
所以,当你采用加成灯光阴影技术时,你需要确保可编程通道已正确配置以便正确分类。另外,你还需要注意为了保证分类正确所做的修改不影响不使用叠加灯光技术时的渲染,原则上你应该保证同样的材质在各种光照下均可用。
一个可以正确分类的可编程材质实例:
// Per-pixel normal mapping Any number of lights, diffuse and specular
material Examples/BumpMapping/MultiLightSpecular
{ technique{
// Base ambient pass
pass
{
// ambient only, not needed for rendering, but as information
// to lighting pass categorisation routine
ambient 1 1 1
diffuse 0 0 0
specular 0 0 0 0
// Really basic vertex program
vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture
{
param_named_auto worldViewProj worldviewproj_matrix
param_named_auto ambient ambient_light_colour
}
}
// Now do the lighting pass
// NB we don‘t do decal texture here because this is repeated per light
pass
{
// set ambient off, not needed for rendering, but as information
// to lighting pass categorisation routine
ambient 0 0 0
// do this for each light
iteration once_per_light
scene_blend add
// Vertex program reference
vertex_program_ref Examples/BumpMapVPSpecular
{
param_named_auto lightPosition light_position_object_space 0
param_named_auto eyePosition camera_position_object_space
param_named_auto worldViewProj worldviewproj_matrix
}
// Fragment program
fragment_program_ref Examples/BumpMapFPSpecular
{
param_named_auto lightDiffuse light_diffuse_colour 0
param_named_auto lightSpecular light_specular_colour 0
}
// Base bump map
texture_unit
{
texture NMBumpsOut.png
colour_op replace
}
// Normalisation cube map
texture_unit
{
cubic_texture nm.png combinedUVW
tex_coord_set 1
tex_address_mode clamp
}
// Normalisation cube map #2
texture_unit
{
cubic_texture nm.png combinedUVW
tex_coord_set 1
tex_address_mode clamp
}
}
// Decal pass
pass
{
lighting off
// Really basic vertex program
vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture
{
param_named_auto worldViewProj worldviewproj_matrix
param_named ambient float4 1 1 1 1
}
scene_blend dest_colour zero
texture_unit
{
texture RustedMetal.jpg
}
}
}
}
注:如果你使用纹理阴影,可选择”集成纹理阴影”,避免被迫采用这种显示的顺序,从而可以将通道数压缩的很小,代价是需要详细说明最大数量的阴影投射灯光。这种情况下,“叠加”阴影仅影响阴影纹理色,你可以自由选择阴影的混合方式
静态照明
叠加光照技术有个格外的限制,不能很好的和预计算静态光照结合。叠加阴影基于没有光照的区域就是阴影的原则,但静态光照包含了光照和阴影区,叠加阴影不能移除(静态图中的)照明以创建新的阴影。所以使用叠加阴影时不能和其它光照混用(你可以与逐像素光照结合实现动态阴影),或者你可以通过集成阴影技术实现与静态光照混合。