自己体验了以后发现
surface shader
确实是隐藏了好多好多的内部实现呀,像我没有考虑的多光源,阴影,衰减等问题,在surface shader
中都是被隐藏实现好了的,而且还是多平台适配的,Vertex & Fragment Shader
则还要自己写,还好unity也给我们提供了一些现成方法可以调。_(:зゝ∠)_
所以总结一下,在写需要接收复杂光源信息的材质时,写
surface shader
应该会简单不少。
如果是不接收光或很少光的材质时,Vertex & Fragment Shader
应该代码更清晰易懂点,速度更快一点。
unity的surface shader
这项技术还是可以省我们不少事的,可以提高开发速度。
注意:以下2个shader,只考虑了只有一个平行光的情况,没有考虑多光源,阴影,衰减等问题。
surface shader
的实现可以去看官方的shader
资源资源去看,下面的代码是可以继续优化的,这里为了步骤清晰,就多分了一些变量
// 漫反射shader,对应 Legacy Shaders/Diffuse
Shader "Z_TestShader/TestDiffuse"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// 前向渲染编译指令
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
uniform float4 _Color;
struct v2f
{
float4 pos : SV_POSITION;
float3 normal : NORMAL;
float3 lightDir : TEXCOORD0;
};
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
// 把模型空间的模型法线转到世界空间,以下是等效的2种方法
//o.normal = normalize(mul(v.normal, _World2Object));
o.normal = UnityObjectToWorldNormal(v.normal);
// 取光线的方向,以下是等效的2种方法
//o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
o.lightDir = ObjSpaceLightDir(v.vertex);
o.lightDir = normalize(mul(_Object2World, o.lightDir));
return o;
}
fixed4 frag (v2f i) : COLOR
{
// 环境光
float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;
// 漫反射
fixed diff = max (0, dot(i.normal, i.lightDir));
float3 diffCol = _LightColor0.rgb * diff;
// 最终颜色合成
float4 col;
col = float4(ambientLight + diffCol, 1) * _Color;
return col;
}
ENDCG
}
}
}
// 贴花(就是把一张纹理贴在另一张的上面)shader,对应Legacy Shaders/Decal
Shader "Z_TestShader/TestDecal"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_DecalTex ("Decal (RGBA)", 2D) = "black" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 250
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _DecalTex;
uniform fixed4 _Color;
float4 _MainTex_ST;
float4 _DecalTex_ST;
struct v2f
{
float4 pos : SV_POSITION;
float2 uv_MainTex : TEXCOORD0;
float2 uv_DecalTex : TEXCOORD1;
float3 normal : NORMAL;
float3 lightDir : TEXCOORD3;
};
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
// 用材质的 Tiling, Offset 2个参数计算纹理uv
o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv_DecalTex = TRANSFORM_TEX(v.texcoord, _DecalTex);
//o.normal = normalize(mul(v.normal, _World2Object));
o.normal = UnityObjectToWorldNormal(v.normal);
//o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
o.lightDir = ObjSpaceLightDir(v.vertex);
o.lightDir = normalize(mul(_Object2World, o.lightDir));
return o;
}
fixed4 frag (v2f i) : COLOR
{
// 贴花算法
float4 tex = tex2D(_MainTex, i.uv_MainTex);
float4 decal = tex2D(_DecalTex, i.uv_DecalTex);
tex.rgb = lerp (tex.rgb, decal.rgb, decal.a);
tex *= _Color;
float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed diff = max (0, dot(i.normal, i.lightDir));
float3 diffCol = _LightColor0.rgb * diff;
// 最终颜色合成
float4 col;
col = float4(ambientLight + diffCol, 1) * tex;
return col;
}
ENDCG
}
}
}
注意:还有一点,如果你要在一个场景里跟 surface shader 放在一起做效果对比的话,记得在 Window->Lighting 界面里,把环境光的 Ambient Source 选项,从SkyBox 换成 Color,因为我发现在 SkyBox 模式下(也就是环境光来自于天空盒),UNITY_LIGHTMODEL_AMBIENT 这个宏得到的环境光是不准确的,至少是跟 surface shader 获得的环境光是有差别的。可能如果想获得一样的环境光还需要经过一些处理才行吧。所以你要想让2种 shader 效果一样,记得要改一下这里。