shader篇-陰影
标簽(空格分隔): shader
- shader篇-陰影
- 簡介
- 投射陰影實作
- 接受陰影
- Unity統一管理光照衰減和陰影
- 透明度物體的陰影
簡介
陰影的實作需要2個過程:
一、接受陰影的物體,需要在shader中采樣陰影映射紋理,并把采樣後結果與光照結果相乘獲得陰影效果。
二、一個物體如果想投射陰影,就需要将該物體加入光照映射紋理的計算中,以便被采樣時可以擷取該物體相關資訊。Unity Shader中該過程是又LightgMode 為ShadeowCaster的Pass實作。
投射陰影實作
Unity中,是否讓物體投射或接受陰影,是由mesh renderer元件的Cast Shadows和Receive Shadows的設定來實作
不過即便沒有設定,如果fallback能調用含LightMode 為ShadowCaster的Pass時,一樣能投射陰影,比如說 Fallback “Specualr”,通過回調内置的Specular可以間接調用含有LightMode 為ShadowCaster的Pass。
接受陰影
編寫接受陰影的shader時需要#include “AutoLight.cginc”,計算陰影的宏都是來自這個檔案。
配置
Properties {
_Diffuse ("Diffuse", Color) = (, , , )
_Specular ("Specular", Color) = (, , , )
_Gloss ("Gloss", Range(, )) =
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS()
};
注意:為保證内置宏運作正确,a2v結構體頂點坐标變量名必須
頂點着色器
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 計算陰影值
TRANSFER_SHADOW(o);
return o;
}
之後,我們使用内置宏SHADOW_ATTENUATION在片元着色器中計算陰影值
Unity統一管理光照衰減和陰影
unity内置了一個宏計算光照衰減和陰影UNITY_LIGHT_ATTENUATION
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(worldNormal, halfDir)), _Gloss);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4((diffuse + specular) * atten, );
}
這種内置宏可以簡化我們某個工作
透明度物體的陰影
對于大多數不透明物體,fallback設為VertexLit就可以得到正确的陰影,但不透明物體就要小心了。
配置
Properties {
_Color ("Color Tint", Color) = (, , , )
_MainTex ("Main Tex", D) = "white" {}
_Cutoff ("Alpha Cutoff", Range(, )) =
}
SubShader {
Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Off
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
SHADOW_COORDS()
};
注意:SHADOW_COORDS的參數是3,意味着它将占有第四個插值寄存器TEXCOORD3
FallBack "Transparent/Cutout/VertexLit"
}
還有fallback,這一次不能間接調用Vertexlit了,我們需要直接調用Transparent/Cutout/VertexLit,隻有這樣才能調用含透明度測試的shader
注意:Unity内置的半透明shader都不會添加陰影,因為半透明物體關閉了深度寫入,必須處理好渲染順序,否則陰影處理會非常複雜