第九章 更复杂的光照
渲染路径是指什么?什么作用?
- 一种能让Unity知道设定哪些预制变量数据的参数。有前向渲染路径、延迟渲染路径和顶点照明渲染路径
通过在每个Pass中设置Tags{“LightMode” = “ForwardBase”}来实现
参考UntiyPassTags设置 - 前向渲染:深度检测,对各片元进行依次进行多个光照计算,每个光源都要计算一次Pass。(缺点:多光源计算量大)
- 延迟渲染:深度检测,并将计算需要的数据存储到G缓冲,然后执行依次光照计算的Pass。主要是多光源的时候应用不同。固定两个Pass。(缺点:不支持真正的抗锯齿;不能处理半透明;显卡需要支持MRT) 参考GBuff实现细节
- 顶点照明渲染:前向渲染的特化方式,在一个Pass内可以访问所有的光照,计算出所有的结果。
参考Unity中的Multi预定义
参考UnityShader中常用变量
- 一种能让Unity知道设定哪些预制变量数据的参数。有前向渲染路径、延迟渲染路径和顶点照明渲染路径
Unity中的光源有哪些?有什么特点?
- 平行光:只有方向,所有点看到的方向都一样。不会衰减。
- 点光源:有位置,有范围,有衰减
- 聚光灯:有位置,有方向,锥形范围,有衰减。计算范围和衰减相对来说最复杂。
多光源的光照如何计算?
多Pass,第一个Pass设置Tags{“LightMode”=”ForwardBase”},另一个Pass设置{“LightMode”=”ForwardAdd”}。
BasePass中计算平行光(最强的一个平行光)、环境光。Additional Pass对每个光照调用一次,依次计算所有的光照。一般Additional Pass混合为颜色叠加即Blend One One
如果worldNormal传给frag,那么在frag函数中需要再normalize一次,因为自动差值之后,可能已经不是单位向量了。如果光照设置为不重要,只能在BasePass中处理了吗?但BasePass中只能访问4个非重要光照的信息,其他的怎么解决?AdditionalPass应该能在顶点函数中处理光照吧?
光照衰减怎么计算?
Unity使用一个_LightTexture0纹理(_LightTextureB0如果开启了cookie)来作为查表数据,减少计算。减少了一定灵活性。
点光源的衰减计算:float3 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)).xyz; fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
spot衰减计算:
float4 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)); fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
什么是阴影,生成阴影的原理是什么?有没有什么限制?透明物体的阴影有什么不一样的地方?
假设从灯光所在的地方有个摄像机,那么该这相机看不到的地方,实际就是阴影产生的区域。这就是阴影贴图的思想。当然不同的光照实际计算的方式不太一样,在Unity开启阴影之后,直接得到结果阴影纹理。
那么又如何使用这个阴影纹理呢?
传统方法:对于一个需要绘制的像素坐标,转换到对应的光照空间之后,计算深度,如果该深度大于阴影纹理中对应的该点的深度值,那说明这点就在阴影中。为了减少这个空间转换的计算,Unity现在采用屏幕空间的阴影映射技术(并不是所有都支持,不支持的话采用传统方式),阴影纹理在存储的时候已经转到屏幕坐标空间下,当前摄像机也同时产生一个屏幕空间下的深度纹理。那么在计算投影的时候,就只需要比较两个纹理中同一点的深度值就可以了。相机深度纹理中的值较大的话,就说明在阴影中。
看一下TRANSFER_SHADOW宏源码的实现可以验证上面的说法:
不支持屏幕映射的情况,可以看到是做了两次坐标空间转换,就是上面说的传统方法将坐标转换到对应的光照空间下。
#define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );
支持屏幕映射的情况,直接计算屏幕坐标。
#define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);
- 三个重要的宏:
SHADOW_COORDS(n)
TRANSFER_SHADOW(o);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); - 透明物体的阴影使用不同fallback,可以需要再设置双面投影。
链接汇总:
UntiyPassTags设置
GBuff实现细节
Unity中的Multi预定义
UnityShader中常用变量