UnityShader学习笔记(三)利用镜面反射让游戏闪耀起来
发表于2017-01-22
大家好,我是Zander。 今天来说一下在游戏中如何使用镜面反射来给玩家制造出强烈的视觉冲击和超现实感。
在Unity中,已经为我们提供了一个高光函数,我们可以在自己的着色器中使用。这个函数也就是BlinnPhong镜面反射光照模型,它是高光类型中的俄译中更加基础且高效的形式。由于它已经集成到Unity着色器语言挡住,所以我们可以在Unity着色器中建立高光效果:
首先创建一个新的着色器SpecShader,添加属性代码
1 2 3 4 5 6 | Properties { _MainTex ( "Base (RGB)" , 2D) = "white" {} _MainTint( "Diffuse Tint" ,Color) = (1,1,1,1) _SpecColor( "Specular Color" ,Color) = (1,1,1,1) _SpecPower( "Specular Power" ,Range(0,1)) = 0.5 } |
然后,我们在CGPROGRAM中添加相应的变量,这样我们才能在着色器的CGPROGRAM中使用属性中的数据,这里我们没有声明_SpecColor属性做变量,因为在内存的高光模型中Unity已经为我们声明了该变量:
1 2 3 | sampler2D _MainTex; float4 _MainTint; float _SpecPower; |
我们将BlinnPhong添加到#pragma语句后面:
1 | #pragma surface surf BlinnPhong |
然后修改我们的Surf()函数
1 2 3 4 5 6 7 8 | void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint; o.Specular = _SpecPower; o.Gloss = 1.0; o.Albedo = c.rgb; o.Alpha = c.a; } |
运行Unity,就会看到如下图所示:
详细代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Shader "Custom/SpecShader" { Properties { _MainTex ( "Base (RGB)" , 2D) = "white" {} _MainTint( "Diffuse Tint" ,Color) = (1,1,1,1) _SpecColor( "Specular Color" ,Color) = (1,1,1,1) _SpecPower( "Specular Power" ,Range(0,1)) = 0.5 } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf BlinnPhong sampler2D _MainTex; float4 _MainTint; float _SpecPower; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint; o.Specular = _SpecPower; o.Gloss = 1.0; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } |
接下来我们来介绍一下Phong高光类型,Phong高光模型是最基础且表现最友好的高光类型。它会计算出光在物体表面的反射方向与观察者视线方向之间的对比结果。虽然在镜面反射的精确建模上它并不是最接近现实的,但在大多数情况下它都显得极其逼真。 接下来我们来实现一下Phong:
首先创建一个着色器PhongShader,第一步还是添加属性代码:
1 2 3 4 5 6 7 8 | Properties { _MainTint ( "Diffuse Tint" , Color) = (1,1,1,1) _MainTex ( "Base (RGB)" , 2D) = "white" {} _SpecularColor ( "Specular Color" , Color) = (1,1,1,1) _SpecPower ( "Specular Power" , Range(0.1,30)) = 1 } |
在CGRPAGRAM中添加相应的变量
1 2 3 4 | float4 _SpecularColor; sampler2D _MainTex; float4 _MainTint; float _SpecPower; |
现在我们添加自定义的光照模型,以便计算Phong高光
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | inline fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) { //Calculate diffuse and the reflection vector float diff = dot(s.Normal, lightDir); float3 reflectionVector = normalize((2.0 * s.Normal * diff) - lightDir); //Calculate the Phong specular float spec = pow(max(0,dot(reflectionVector, viewDir)), _SpecPower); float3 finalSpec = _SpecularColor.rgb * spec; //Create final color fixed4 c; c.rgb = (s.Albedo * _LightColor0.rgb * diff) + (_LightColor0.rgb * finalSpec); c.a = 1.0; return c; } |
因为我们使用了自定义的光照函数,所以我们要在#pragma语句进行修改:
#pragma surface surf Phong运行Unity ,和之前的镜面反射对比如图:
我们会看到Phong更加的自然光滑
全部代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | Shader "Custom/PhongShader" { Properties { _MainTint ( "Diffuse Tint" , Color) = (1,1,1,1) _MainTex ( "Base (RGB)" , 2D) = "white" {} _SpecularColor ( "Specular Color" , Color) = (1,1,1,1) _SpecPower ( "Specular Power" , Range(0.1,30)) = 1 } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Phong float4 _SpecularColor; sampler2D _MainTex; float4 _MainTint; float _SpecPower; inline fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) { //Calculate diffuse and the reflection vector float diff = dot(s.Normal, lightDir); float3 reflectionVector = normalize((2.0 * s.Normal * diff) - lightDir); //这样做是为了实现法线朝向光源弯曲的效果 //Calculate the Phong specular float spec = pow(max(0,dot(reflectionVector, viewDir)), _SpecPower); float3 finalSpec = _SpecularColor.rgb * spec; //Create final color fixed4 c; c.rgb = (s.Albedo * _LightColor0.rgb * diff) + (_LightColor0.rgb * finalSpec); c.a = 1.0; return c; } struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } |
最后我们来实现一个金属软高光的效果:代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | Shader "Custom/MetallicSoft" { Properties { _MainTint ( "Diffuse Tint" , Color) = (1,1,1,1) _MainTex ( "Base (RGB)" , 2D) = "white" {} _RoughnessTex ( "Roughness texture" , 2D) = "" {} _Roughness ( "Roughness" , Range(0,1)) = 0.5 _SpecularColor ( "Specular Color" , Color) = (1,1,1,1) _SpecPower ( "Specular Power" , Range(0,30)) = 2 _Fresnel ( "Fresnel Value" , Range(0,1.0)) = 0.05 } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf MetallicSoft #pragma target 3.0 sampler2D _MainTex; sampler2D _RoughnessTex; float _Roughness; float _Fresnel; float _SpecPower; float4 _MainTint; float4 _SpecularColor; inline fixed4 LightingMetallicSoft (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) { //Compute simple diffuse and view direction values float3 halfVector = normalize(lightDir + viewDir); float NdotL = saturate(dot(s.Normal, normalize(lightDir))); float NdotH_raw = dot(s.Normal, halfVector); float NdotH = saturate(dot(s.Normal, halfVector)); float NdotV = saturate(dot(s.Normal, normalize(viewDir))); float VdotH = saturate(dot(halfVector, normalize(viewDir))); //Micro facets distribution float geoEnum = 2.0*NdotH; float3 G1 = (geoEnum * NdotV)/NdotH; float3 G2 = (geoEnum * NdotL)/NdotH; float3 G = min(1.0f, min(G1, G2)); //Sample our Spceular look up BRDF float roughness = tex2D(_RoughnessTex, float2(NdotH_raw * 0.5 + 0.5, _Roughness)).r; //Create our custom fresnel value float fresnel = pow(1.0-VdotH, 5.0); fresnel *= (1.0 - _Fresnel); fresnel += _Fresnel; //Create the final spec float3 spec = float3(fresnel * G * roughness * roughness) * _SpecPower; float4 c; c.rgb = (s.Albedo * _LightColor0.rgb * NdotL)+ (spec * _SpecularColor.rgb) * (atten * 2.0f); c.a = s.Alpha; return c; } struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } |
所需要的图片:
效果如图: