在Unity的Surface Shader中,自定义光照模型需要按一定格式命名和设置光照模型函数。
一、准备
首先我们来分别新建一个表面着色器和材质,都命名为BasicDiffuse,把着色器赋给材质。
精简shader代码如下,只留一个主贴图功能:
Shader "Custom/BasicDiffuse" {
Properties {
_MainTex("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Standard
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
现在shader代码中与光照模型相关的只有两处地方:
1、指定光照模型Standard
#pragma surface surf Standard
2、专门给Standard光照模型用的数据传输结构SurfaceOutputStandard
void surf (Input IN, inout SurfaceOutputStandard o) {
Standard是Unity内置的一个很强大的光照模型,我们要做的就是把它替换成我们自定义的光照模型。
二、光照函数定义规则
自定义光照函数说是自定义其实也不完全是,它必须按一定的格式来自定义,它的返回值必须是颜色值类型,如fixed4,函数名必须带有前缀Lighting,比如LightingCustomLight,参数也是固定的参数:数据传输结构体SurfaceOutput、光照方向lightDir、光强的衰减度atten,结合起来就如下:
inline fixed4 LightingCustomLight(SurfaceOutput s, fixed3 lightDir, fixed atten)
{
//...
}
然后我们就可以在函数里利用已有的数据进行各种计算,最后返回一个颜色值就行。
1、SurfaceOutput结构体里存的就是表面着色函数surf的输出值。
2、lightDir通常称为光照方向,但具体点说lightDir其实是光照相对于当前正在计算的像素的方向(特别注意这个向量的箭头是指向光源的而不是指向物体的),对于平行光,它是一个固定的值,而对于其他类型光源如点光源,不同像素它的值是不一样的。
3、atten是光照的衰减,就是离光源越远光照越弱,但对于平行光它固定是1。
写完自定义的光照函数之后,我们必须指定它为我们的光照模型它才能起作用,如前面指定Standard光照模型那样,我们只要把Standard换成我们自定义的光照函数名去掉前缀Lighting即可。
就像这样:
#pragma surface surf CustomLight
因为我们要实现的是基础漫反射光照模型,所以我们把自定义的光照模型命名为BasicDiffuse。
最后实际的shader的代码如下:
Shader "Custom/BasicDiffuse" {
Properties {
_MainTex("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf BasicDiffuse
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
inline fixed4 LightingBasicDiffuse(SurfaceOutput s, fixed3 lightDir, fixed atten)
{
fixed diffuseLight = max(0.0, (dot(s.Normal, lightDir)));
fixed4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (diffuseLight * atten);
col.a = s.Alpha;
return col;
}
ENDCG
}
FallBack "Diffuse"
}
首先光照函数的第一行代码就是计算物体表面对光照的漫反射的强度。很简单的道理,当你直面太阳的时候,受光是最强的,当你背面太阳的时候,受光是最弱的。而法线Normal表示的就是像素的面向。
lightDir.png
当像素的法线方向与光照方向相同,即表示它是直面光源,受光最强,计算出来的点积是1。
当像素的法线方向与光照方向相反,即表示它是背面光源,不受光,计算出来的点积是负数,即0到-1。
但是无论你是0还是-1,意义都是一样的,都是不受光,所以我们可以给计算出来的点积加一个max修正,将小于0的都统一为0。
所以最终第一句代码得出来的值表示的就是当前像素反射光的强度(或者是物体表面反射光的强度)。
然后我们将计算得到的反射光强度乘以主贴图颜色、光照颜色和光强衰减度得到一个新的像素颜色值(就是第三行代码),透明度不变(第四行代码),最后返回这个新的颜色值即可。
自定义光照函数写完之后,需要指定这个函数为我们的光照模型,同时不要忘记把surf函数的SurfaceOutputStandard参数类型改为SurfaceOutput。
好了代码已经写完了,如果没有出错的话我们应该可以得到这样的效果:
BasicDiffuse.png
我们也可以给材质赋上一张贴图看看:
BasicDiffuse1.png
三、总结
在编写shader代码的时候,很多关键的空间几何数据的表示都是以当前正在计算的像素为中心原点的,比如光照方向lightDir,观察视角方向viewDir,切空间等。有一个统一的参考系,各种几何计算会变得比较容易。
网友评论