效果如下:
渲染头发时可能会遇到如下问题:
1. 因为头发本质上是一个一个的透明的面片,理所当然会想到使用 blend 混合方式来渲染。
但当由于用 blend 时,要关闭Z缓存写,即执行 Zwirte Off,不然透明的区域也会遮挡后面的像素。
此时就会出现问题,头发之间的层级会完全混乱,因为头发是多个面片穿插在一起的。
2. 因此不能使用blend的方式,就只能使用 Alpha Test 的方式来强制丢弃透明的像素。
但 Alpha Test 的问题的边缘部分不够平滑,剧齿感明显。
解决此问题的思路是,一个通道执行 Alpha Test,把透明区域直接丢弃掉。
另一个通道执行 Blend 混合,把 Alpha Test 丢弃的像素再重新渲染一遍。
示例代码如下:
1 Shader "Character/Example-Diffuse-Hair" 2 { 3 Properties 4 { 5 _Color ("Main Color", Color) = (1,1,1,1) 6 _MainTex ("Base (RGB)", 2D) = "white" {} 7 _Cutoff( "Cutoff", Range (0,1)) = 0.5 8 } 9 10 SubShader 11 { 12 Tags 13 { 14 "RenderType" = "Transparent" 15 "IgnoreProjector" = "True" 16 "Queue" = "Transparent+100" 17 } 18 LOD 200 19 20 Pass 21 { 22 Tags 23 { 24 "LightMode" = "ForwardBase" 25 } 26 Blend SrcAlpha OneMinusSrcAlpha 27 Cull Off 28 29 CGPROGRAM 30 #pragma vertex vert 31 #pragma fragment frag 32 #pragma multi_compile_fwdbase 33 34 #include "UnityCG.cginc" 35 #include "AutoLight.cginc" 36 #include "Lighting.cginc" 37 38 fixed4 _Color; 39 sampler2D _MainTex; 40 float _Cutoff; 41 42 struct appdata 43 { 44 float4 vertex : POSITION; 45 float2 uv : TEXCOORD0; 46 float3 normal : NORMAL; 47 }; 48 49 struct v2f 50 { 51 float4 pos : SV_POSITION; 52 float2 uv : TEXCOORD0; 53 float3 worldPos : TEXCOORD1; 54 float3 worldNormal : TEXCOORD2; 55 }; 56 57 v2f vert(appdata v) 58 { 59 v2f o; 60 o.pos = UnityObjectToClipPos(v.vertex); 61 o.uv = v.uv; 62 o.worldPos = mul(unity_ObjectToWorld, v.vertex); 63 o.worldNormal = UnityObjectToWorldNormal(v.normal); 64 return o; 65 } 66 67 fixed4 frag(v2f i) : SV_TARGET 68 { 69 fixed4 albedo = tex2D(_MainTex, i.uv) * _Color; 70 clip(albedo.a - _Cutoff); 71 72 fixed3 ambient = albedo.rgb * UNITY_LIGHTMODEL_AMBIENT.rgb; 73 74 fixed3 worldLight = UnityWorldSpaceLightDir(i.worldPos); 75 float d = dot(worldLight, i.worldNormal) * 0.5 + 0.5; 76 fixed3 diffuse = albedo.rgb * _LightColor0.rgb * d; 77 78 fixed4 col = fixed4(ambient + diffuse * 2, albedo.a); 79 80 return col; 81 } 82 83 ENDCG 84 } 85 86 Pass 87 { 88 Tags 89 { 90 "LightMode" = "ForwardBase" 91 } 92 Blend SrcAlpha OneMinusSrcAlpha 93 ZWrite Off 94 95 CGPROGRAM 96 #pragma vertex vert 97 #pragma fragment frag 98 #pragma multi_compile_fwdbase 99 100 #include "UnityCG.cginc" 101 #include "AutoLight.cginc" 102 #include "Lighting.cginc" 103 104 fixed4 _Color; 105 sampler2D _MainTex; 106 float _Cutoff; 107 108 struct appdata 109 { 110 float4 vertex : POSITION; 111 float2 uv : TEXCOORD0; 112 float3 normal : NORMAL; 113 }; 114 115 struct v2f 116 { 117 float4 pos : SV_POSITION; 118 float2 uv : TEXCOORD0; 119 float3 worldPos : TEXCOORD1; 120 float3 worldNormal : TEXCOORD2; 121 }; 122 123 v2f vert(appdata v) 124 { 125 v2f o; 126 o.pos = UnityObjectToClipPos(v.vertex); 127 o.uv = v.uv; 128 o.worldPos = mul(unity_ObjectToWorld, v.vertex); 129 o.worldNormal = UnityObjectToWorldNormal(v.normal); 130 return o; 131 } 132 133 fixed4 frag(v2f i) : SV_TARGET 134 { 135 fixed4 albedo = tex2D(_MainTex, i.uv) * _Color; 136 clip(_Cutoff - albedo.a); 137 138 fixed3 ambient = albedo.rgb * UNITY_LIGHTMODEL_AMBIENT.rgb; 139 140 fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos)); 141 float d = dot(worldLight, i.worldNormal) * 0.5 + 0.5; 142 fixed3 diffuse = albedo.rgb * _LightColor0.rgb * d; 143 144 fixed4 col = fixed4(ambient + diffuse * 2, albedo.a); 145 146 return col; 147 } 148 149 ENDCG 150 } 151 152 153 } 154 155 Fallback "Diffuse" 156 }