效果比较好的头发shader
效果如下:
渲染头发时可能会遇到如下问题:
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 }
以下是带高光的头发shader,头发的高光比较特殊,是一圈一圈的高光,如下图效果,故不能用普通的高光算法。
代码如下:
1 Shader "America/Character/America-Diffuse-Specular-Hair2" 2 { 3 Properties 4 { 5 _Color ("Main Color", Color) = (1,1,1,1) 6 _MainTex ("Base (RGB)", 2D) = "white" {} 7 _Ramp ("Toon Ramp (RGB)", 2D) = "gray" {} 8 _Cutoff( "Cutoff", Range (0,1)) = 0.5 9 10 _OverlyingColor("Overlying Color", Color) = (0.5, 0.5, 0.5, 1) 11 12 _SpecularGloss ("Specular Gloss", float) = 20 13 _AnisotropyBias("Anisotropy-Bias", Range( -1 , 1)) = -1 14 _DiffuseRate ("Diffuse Rate", float) = 2 15 _SpecularRate ("Specular Rate", float) = 0.6 16 } 17 18 SubShader 19 { 20 Tags 21 { 22 "RenderType" = "Transparent" 23 "IgnoreProjector" = "True" 24 "Queue" = "Transparent+100" 25 } 26 LOD 200 27 28 Pass 29 { 30 Tags 31 { 32 "LightMode" = "ForwardBase" 33 } 34 Blend SrcAlpha OneMinusSrcAlpha 35 Cull Off 36 37 CGPROGRAM 38 #pragma vertex vert 39 #pragma fragment frag 40 #pragma multi_compile_fwdbase 41 42 #include "UnityCG.cginc" 43 #include "AutoLight.cginc" 44 #include "Lighting.cginc" 45 46 #include "../AmericaCG.cginc" 47 48 fixed4 _Color; 49 sampler2D _MainTex; 50 sampler2D _Ramp; 51 float _Cutoff; 52 fixed4 _OverlyingColor; 53 float _SpecularGloss; 54 half _AnisotropyBias; 55 float _DiffuseRate; 56 float _SpecularRate; 57 58 struct appdata 59 { 60 float4 vertex : POSITION; 61 float2 uv : TEXCOORD0; 62 float3 normal : NORMAL; 63 float4 tangent : TANGENT; 64 }; 65 66 struct v2f 67 { 68 float4 pos : SV_POSITION; 69 float2 uv : TEXCOORD0; 70 float4 T2W1 : TEXCOORD1; 71 float4 T2W2 : TEXCOORD2; 72 float4 T2W3 : TEXCOORD3; 73 }; 74 75 v2f vert(appdata v) 76 { 77 v2f o; 78 o.pos = UnityObjectToClipPos(v.vertex); 79 o.uv = v.uv; 80 81 float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); 82 float3 worldNormal = UnityObjectToWorldNormal(v.normal); 83 float3 binormal = cross(normalize(worldNormal), normalize(worldTangent)) * v.tangent.w; 84 float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; 85 o.T2W1 = float4(worldTangent.x, binormal.x, worldNormal.x, worldPos.x); 86 o.T2W2 = float4(worldTangent.y, binormal.y, worldNormal.y, worldPos.y); 87 o.T2W3 = float4(worldTangent.z, binormal.z, worldNormal.z, worldPos.z); 88 return o; 89 } 90 91 fixed4 frag(v2f i) : SV_TARGET 92 { 93 float3 worldPos = float3(i.T2W1.w, i.T2W2.w, i.T2W3.w); 94 float3 worldNormal = float3(i.T2W1.z, i.T2W2.z, i.T2W3.z); 95 //float3 worldTangent = float3(i.T2W1.x, i.T2W2.x, i.T2W3.x); 96 float3 worldBitangent = float3(i.T2W1.y, i.T2W2.y, i.T2W3.y); 97 98 float3 worldLight = normalize(UnityWorldSpaceLightDir(worldPos)); 99 float3 worldView = normalize(UnityWorldSpaceViewDir(worldPos)); 100 101 fixed4 albedo = tex2D(_MainTex, i.uv) * _Color; 102 clip(albedo.a - _Cutoff); 103 104 fixed3 ambient = albedo.rgb * UNITY_LIGHTMODEL_AMBIENT.rgb; 105 106 float d = dot(worldLight, worldNormal) * 0.5 + 0.5; 107 fixed3 rampCol = tex2D(_Ramp, float2(d, d)).rgb; 108 fixed3 diffuse = albedo.rgb * _LightColor0.rgb * rampCol; 109 110 // spec 111 float clampSpec = clamp(albedo.r + albedo.g * _AnisotropyBias - 0.2 , -1.0 , 1.33); 112 float dotSpec = dot(worldBitangent + worldNormal * clampSpec, worldView); 113 fixed3 specular = _LightColor0.rgb * pow(1 - dotSpec * dotSpec, _SpecularGloss); 114 specular = clamp(specular, float3(0,0,0), half3(1.51,1.51,1.51)); 115 116 fixed4 col = fixed4(ambient + diffuse * _DiffuseRate + specular * _SpecularRate, albedo.a); 117 col.rgb = Overlay(col, _OverlyingColor); 118 119 return col; 120 } 121 122 ENDCG 123 } 124 125 Pass 126 { 127 Tags 128 { 129 "LightMode" = "ForwardBase" 130 } 131 Blend SrcAlpha OneMinusSrcAlpha 132 ZWrite Off 133 134 CGPROGRAM 135 #pragma vertex vert 136 #pragma fragment frag 137 #pragma multi_compile_fwdbase 138 139 #include "UnityCG.cginc" 140 #include "AutoLight.cginc" 141 #include "Lighting.cginc" 142 143 #include "../AmericaCG.cginc" 144 145 fixed4 _Color; 146 sampler2D _MainTex; 147 sampler2D _Ramp; 148 float _Cutoff; 149 fixed4 _OverlyingColor; 150 float _DiffuseRate; 151 152 struct appdata 153 { 154 float4 vertex : POSITION; 155 float2 uv : TEXCOORD0; 156 float3 normal : NORMAL; 157 }; 158 159 struct v2f 160 { 161 float4 pos : SV_POSITION; 162 float2 uv : TEXCOORD0; 163 float3 worldPos : TEXCOORD1; 164 float3 worldNormal : TEXCOORD2; 165 }; 166 167 v2f vert(appdata v) 168 { 169 v2f o; 170 o.pos = UnityObjectToClipPos(v.vertex); 171 o.uv = v.uv; 172 o.worldPos = mul(unity_ObjectToWorld, v.vertex); 173 o.worldNormal = UnityObjectToWorldNormal(v.normal); 174 return o; 175 } 176 177 fixed4 frag(v2f i) : SV_TARGET 178 { 179 fixed4 albedo = tex2D(_MainTex, i.uv) * _Color; 180 clip(_Cutoff - albedo.a); 181 182 fixed3 ambient = albedo.rgb * UNITY_LIGHTMODEL_AMBIENT.rgb; 183 184 fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldPos)); 185 float d = dot(worldLight, i.worldNormal) * 0.5 + 0.5; 186 fixed3 rampCol = tex2D(_Ramp, float2(d, d)).rgb; 187 fixed3 diffuse = albedo.rgb * _LightColor0.rgb * rampCol; 188 189 fixed4 col = fixed4(ambient + diffuse * _DiffuseRate, albedo.a); 190 col.rgb = Overlay(col, _OverlyingColor); 191 192 return col; 193 } 194 195 ENDCG 196 } 197 198 199 } 200 201 Fallback "Diffuse" 202 }