在计算光照模型的时候,我们往往需要得到光源方向、视角方向这两个基本信息。在之前的例子中,我们都是自行在代码里计算的,例如使用normalize(_WorldSpaceLightPos0.xyz)来得到光源方向(这种方法实际只适用于平行光),使用normalize(_WorldSpaceCameraPos.xyz-i.worldPosition.xyz)来得到视角方向。但如果需要处理更复杂的光照类型,如点光源和聚光灯,我们计算光源方向的方法就是错误的。这需要我们在代码中先判断光源类型,再计算它的光源信息。
手动计算这些光源信息的过程相对比较麻烦(但并不意味着你不需要了解它们的原理)。幸运的是,Unity提供了一些内置函数来帮助我们计算这些信息。
函数名 | 描述 |
float3 WorldSpaceViewDir(float4 v) | 输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向。内部实现使用了UnityWorldSpaceViewDir函数 |
float3 UnityWorldSpaceViewDir(float4 v) | 输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向 |
float3 ObjSpaceViewDir(float4 v) | 输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向 |
float3 WorldSpaceLightDir(float4 v) | 仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。内部实现使用了UnityWorldSpaceLightDir函数 |
float3 UnityWorldSpaceLightDir(float4 v) | 仅可用于前向渲染中。输入一个世界空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有被归一化 |
float3 ObjSpaceLightDir(float4 v) | 仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有被归一化 |
float3 UnityObjectToWorldNormal(float3 norm) | 把法线方向从模型空间转换到世界空间中 |
float3 UnityObjectToWorldDir(float3 dir) | 把方向矢量从模型空间变换到世界空间中 |
float3 UnityWorldToObjectDir(float3 dir) | 把方向矢量从世界空间变换到模型空间中 |
inline float3 UnityWorldSpaceViewDir(in float3 worldPos)
{
return _WorldSpaceCameraPos.xyz - worldPos;
}
可以看出,这与之前计算视角方向的方法一致。需要注意的是,这些函数都没有保证得到的方向矢量是单位矢量,因此,我们需要在使用前把它们归一化。
而计算光源方向的3个函数:WorldSpaceLightDir、UnityWorldSpaceLightDir和ObjSpaceLightDir,稍微复杂一些,这是因为,Unity帮我们处理了不同种类光源的情况。需要注意的是,这3个函数仅可用于前向渲染。这是因为只有在前向渲染时,这3个函数里使用的内置变量_WorldSpaceLightPos0等才会被正确赋值。
下面介绍使用内置函数改写Unity Shader。
v2f vert(a2v v) { v2f o; ... o.worldNormal = UnityObjectToWorldNormal(v.normal); ... return o; } fixed4 frag(v2f i) : SV_Target { ... fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); ... fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); ... }
需要注意的是,由内置函数得到的方向是没有归一化的,因此我们需要使用normalize函数来对结果进行归一化,再进行光照模型的计算。