Shader "Unlit/ForwardRendering" { Properties { } SubShader { Tags { "RenderType"="Opaque" } LOD 100 //第一个重要的平行光,计算环境光和自发光 Pass { Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float3 normal:NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 worldNormal : COLOR0; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = mul(unity_ObjectToWorld, v.normal); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//环境光,Base这里计算一次 float3 worldLightDir = normalize(_WorldSpaceLightPos0); fixed3 diffuse = _LightColor0.rgb * max(0, dot(i.worldNormal, worldLightDir)); return fixed4(ambient + diffuse,1); } ENDCG } //计算其他逐像素光照 Pass { Tags{ "LightMode" = "ForwardAdd" } Blend One One //混合 CGPROGRAM #pragma multi_compile_fwdadd #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" //unity_WorldToLight struct appdata { float4 vertex : POSITION; float3 normal:NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 worldNormal : COLOR0; float4 worldPos :COLOR1; }; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = mul(unity_ObjectToWorld, v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag(v2f i) : SV_Target { //光照方向 #ifdef USING_DIRECTIONAL_LIGHT fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); #else fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif //计算光照衰减 #ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0; #else float3 lightCoord = mul(unity_WorldToLight , float4(i.worldPos.xyz, 1)).xyz; fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; #endif fixed3 diffuse = _LightColor0.rgb * max(0, dot(i.worldNormal, worldLightDir)); return fixed4(diffuse,1)* atten ; } ENDCG } } }
Pass { Tags { "LightMode" = "ForwardBase" } }
当场景有很多实时光照,前向渲染的性能就会极速下降。所以更古老的延迟渲染就有用了。延迟渲染除了颜色缓冲和深度缓冲外,还会利用额外的缓冲区叫做G缓冲。G缓冲存储了我们关心的表面(离相机近的表面)的其他信息,表面的法线、位置、用于光照计算的材质属性等。延迟渲染主要有两个Pass,第一个Pass不进行任何光照计算,仅仅计算哪些片元是可见的,然后将信息存储到G缓冲区中。然后第二个Pass,就会利用G缓冲中的片元信息,进行真正的光照计算。注意,使用延迟渲染不支持真正的抗锯齿、不能出来半透明物体。
前向渲染是一种传统的常用的渲染路径,在Unity中通过Quality Setting设置逐像素关照的个数,然后关照们根据光照的重要和距离排序决定是否是逐像素,直射光一定是逐像素的。前向渲染有两种Pass,一种是Base Pass,用来计算一个逐像素的平行光和所有逐顶点和SH光源,这个Pass只执行一次,所以环境光和自发光也是这里计算,这些是计算一次就行了。还有一种是Additional Pass,用来计算其他影响该物体的逐像素光源,每个光源执行一次Pass,这个Pass会设置混合模式,然后计算到该光源影响的颜色和帧缓存混合起来。默认的AP是没有阴影效果的,但是可以使用#pragma multi_compile_fwdadd_fullshadows来替换掉#pragma multi_compile_fwdadd编译指令。
要实现双面物体其实就是关闭剔除,物体相对摄像机来说都有正面和反面的说法,我们可以关闭剔除,从而达到正反面都渲染的目的。
Cull Front | Back | Off
透明测试的只需要在Pass通道中加上 Cull Off 就可以实现透明的双面渲染,就是透过自己看到自己后面面的背面。
混合模式的透明需要加多一个Pass通道,两个通道的内容是一样的,但是一个 Cull Front (先渲染背面),一个 Cull Back(再渲染正面)。使用双通道会消耗性能,但是不用的话,混合中没有深度写入,就会有可能的背面渲染在正面前面,所以使用两个通道,先背面再正面。
注意具有深度写入的透明物体,在第一个Pass的时候,深度值就已经把背面的片元给排除掉了。
混合操作指的是源颜色*因子后和目标颜色*因子后的值的操作方式,一般默认是+起来的,但是我们可以使用混合操作命令修改操作。
BlendOp BlendOperation
BlendOperation有:
对于混合,就会有一个等式来计算。结果颜色就是O,S就是源颜色(片元的颜色),D为目标颜色(颜色缓冲读取出来的)。这时候就需要一些因子来计算出结果O了,RGB和A是使用不同的因子的,所以将会有4个因子的存在。
O(rgb) = 因子1 * S(rgb) + 因子2 * D(rgb)
O(a) = 因子3 * S(a) + 因子4 * D(a)
我们能控制的,就是对这些因子的配置来控制混合的计算。
第一种设置表示 因子3 = 因子1,因子4 = 因子2。第二种就是一一对应了。
因子的值可设置为:
Shader "Unlit/pass2" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color("Color",Color) = (1,1,1,1) _AlphaScale("Alpha Scale",Range(0,5)) = 0.5 } SubShader { Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } //第一个通道用来写入深度值,但是不写入颜色值 Pass { ZWrite on ColorMask 0 } Pass { Tags{ "LightMode" = "ForwardBase" } ZWrite Off //关闭深度测试 Blend SrcAlpha OneMinusSrcAlpha //开启混合,设置混合因子 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _Color; float _AlphaScale; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); fixed3 wn = normalize(i.worldNormal); fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0.rgb * _Color.rgb * col.rgb; return fixed4(diffuse,col.a * _AlphaScale);//只有开启了Blend,返回透明度才有用 } ENDCG } } }
这里和混合实例是差不多的,就是多了一个Pass通道来写入深度值,达到的效果就是在一些互相交叉的物体上有了深度值,剔除一些元素,使自身透明不能看到自身。
看图:左边双通道,右边单纯的混合
Shader "Unlit/blend" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color("Color",Color) = (1,1,1,1) _AlphaScale("Alpha Scale",Range(0,5)) = 1 } SubShader { Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Tags{ "LightMode" = "ForwardBase" } ZWrite Off //关闭深度写入 Blend SrcAlpha OneMinusSrcAlpha //开启混合,设置混合因子 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _Color; float _AlphaScale; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); fixed3 wn = normalize(i.worldNormal); fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0.rgb * _Color.rgb * col.rgb; return fixed4(diffuse,col.a * _AlphaScale);//只有开启了Blend,返回透明度才有用 } ENDCG } } }
Shader "Unlit/transparent" { Properties { _Color("Color",Color)=(1,1,1,1) _MainTex ("Texture", 2D) = "white" {} _Cutoff("CutOff",Range(0,1))=0.5 } SubShader { Tags{ "Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout" } Pass { Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _Color; float _Cutoff; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); clip(col.a - _Cutoff);//透明度测试 /*if ((col.a - _Cutoff) < 0.0) { discard;//不通过直接舍弃 }*/ fixed3 wn = normalize(i.worldNormal); fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0 * _Color * col; return fixed4(diffuse,1); } ENDCG } } }