使用多个光源渲染
支持多光源类型
使用光照信息
计算顶点光照
了解球谐函数
上部分介绍了Unity的基本单个光源,现在学习多个光源参与渲染物体,使用Unity5.6.6f2
1 Include Files
为了给Shader增加支持多个光源,我们需要增加更多Pass通道。但是这些Pass最终包含了几乎完全相似的代码,为了避免代码的重复性,我们可以通过把着色器代码移动到一个CG文件,然后在Shader代码中引用该文件
在文件目录中手动创建一个MyLighting.cginc文件,再把FirstLighting.shader内从#pragma以下到ENDCG以上区间内代码拷贝进.cginc文件。这样我们不直接在shader中写这些重复的代码,通过include引用使用它。
注意,.cginc文件也提供了类似的避免重复定义,#define XXX_INCLUDED,再把整个文件内容放置在预处理文件块中。
#if !defined(MY_LIGHTING_INCLUDED) #define MY_LIGHTING_INCLUDED //…
#endif
2 第二光源-Drection
新建两个方向光对象,参数设置如下图:
2-1. 两个光源参数
现在场景中有两个光,但是每个物体看起来没有什么区别。现在我们一次只激活一个光源,看看有什么变化。
2-2. 左main光源,右minor光源
2.1 增加第二个Pass
当前场景内只能看见一个光源效果,这是由于MyMultiLightShader只有一个Pass且只计算了一个光源。Pass光照标签ForwardBase只计算主光源, 为了渲染额外的光源,需要增加一个Pass且指定光照标签为ForwardAdd方可计算额外的光源。
SubShader { Pass { Tags { "LightMode" = "ForwardBase" } CGPROGRAM #pragma target 3.0 #pragma vertex MyVertexProgram #pragma fragment MyFragmentProgram #include "MyLighting.cginc" ENDCG } Pass { Tags { "LightMode" = "ForwardAdd" } CGPROGRAM #pragma target 3.0 #pragma vertex MyVertexProgram #pragma fragment MyFragmentProgram #include "MyLighting.cginc" ENDCG } }
现在虽然计算了两个光源,但是ForwardAdd计算结果会直接覆盖ForwardBase的结果。我们需要把这两个光照效果结合起来,需要在ForwardAdd Pass内使用混合。
UnityShader的Blend函数:如何通过定义两个因子来合并新旧数据? 新旧数据分别与Blend函数的因子相乘然后相加,得到最终结果。如果Pass内没有Blend默认不混合=Blend One Zero。每个Pass计算后的数据会写入帧缓冲区中,也就会替换之前任何写入该缓冲区的内容。为了把新旧数据都能加到帧缓冲区,我们可以需要指示GPU使用Blend one one模式。
Pass { Tags { "LightMode" = "ForwardAdd" } Blend One One //... }
2-3. 左无混合, 右one one混合
Z-buffer\GPU`s depth buffer:一个物体第一次被渲染,GPU就会检查该片元是否会渲染在其他已经渲染过的像素的前面,这些距离信息就存储在该缓冲区中。因此每个像素都有颜色和深度信息,该深度表示从相机到最近表面的每个像素的距离。
ForwardBase中,如果要渲染的片元前面没有任何内容(深度值最小),它就是最靠近摄像机的表面。GPU也会继续运行fragment程序,生成新的颜色和记录新的深度。如果要渲染的片元的深度值最终比已经存在的大,说明它前面有东西,它就不会被渲染也不能看见。在forward add中重复计算minor光时,要添加到已经存在的灯光,再次运行fragment程序时,因为针对的是同一个对象,最终记录了完全相同的深度值。因此两次写入相同的深度信息是没必要的,用ZWrite off关闭它。
Blend One One ZWrite Off
2.2 Draw Call Batches
在Game视图右上角打开Stats窗口,可以更好地了解运行时发生的事情。查看Batches、Saved by batching数据。先只激活main光源。
2-4. Batches数据6,总共7
2-5. 实际Batches
通过FrameDebugger分析,实际是5个draw mesh加上3个内置阴影render函数,一共8个Batches。但是由于启用了动态批处理dynamic batching,所以有一个Saved by batching统计。
那现在来消除这3个阴影渲染函数调用,打开Edit/Project Settings/Quality。Shadows选择Disable Shadows. Clear先无视它这个系统清屏函数。
2-6. 去掉了阴影渲染函数
激活minor光源,如下图:
2-7. 10 + 1 = 11
10个Batches? 因为这5个对象被渲染了两次,最终为10个批次,而不是上面的4个。 动态批处理失效了!Unity规定动态批处理最多只支持一个方向光作用的物体对象。
2.3 Frame Debugger
通过Window / Frame Debugger打开可以清楚了解屏幕画面是如何被渲染出来的,5.6版本。
2-8 Frame Debugger调试
通过选择光条可单步调试渲染,窗口会自动显示每一步的细节。按照上面的顺序,优先画出了靠近相机的不透明物体,这个front-to-back从前到后的渲染顺序是有效的,得益于depth-buffer深度缓冲,隐藏的片元就会被跳过不渲染。如果使用back-to-front从后到前的顺序,就会覆写远处的像素,发生overdraw。
Unity渲染顺序是front-to-back,同时Unity喜欢把相似的物体分组。例如,sphere和cube分开,可避免在不同mesh网格间切换;或者把使用相同的material分组。
3 Point Lights
来源:https://www.cnblogs.com/baolong-chen/p/12245910.html