Unity Shader总结(四)——光照模型

自发光: c e m i s s i v e c_{emissive} cemissive
高光发射: c s p e c u l a r c_{specular} cspecular
漫反射: c d i f f u s e c_{diffuse} cdiffuse
环境光: c a m b i e n t c_{ambient} cambient

一、漫反射

1.1 兰伯特模型

c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ) m a x ( 0 , n ^ ⋅ l ^ ) c_{diffuse}=(c_{light}\cdot m_{diffuse})max(0,\widehat{n}\cdot \widehat{l}) cdiffuse=clightmdiffusemax(0,n l )
其中, c l i g h t c_{light} clight是光源颜色和强度, m d i f f u s e m_{diffuse} mdiffuse是漫反射系数, n ^ \widehat{n} n 是表面法线, l ^ \widehat{l} l 是指向光源的单位矢向量。
m a x max max函数可以用 s a t u r a t e ( x ) saturate(x) saturate(x)函数实现。

实践

逐顶点:

Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}
	SubShader {
    
    
		Pass {
    
     
			//光照模式
			Tags {
    
     "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			
			struct a2v {
    
    
				float4 vertex : POSITION;
				//顶点法线
				float3 normal : NORMAL;
			};
			
			struct v2f {
    
    
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Transform the normal from object space to world space
				//因为法线直接用变换矩阵不能保证变换后垂直,所以用原变换矩阵的逆转置矩阵
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				// Get the light direction in world space
				//_WorldSpaceLightPos0是假设只有一个光源且是平行光
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
				
				o.color = ambient + diffuse;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				return fixed4(i.color, 1.0);
			}
			
			ENDCG
		}
	}
	FallBack "Diffuse"
}

逐像素:

Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-Level" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}
	SubShader {
    
    
		Pass {
    
     
			Tags {
    
     "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			
			struct a2v {
    
    
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
    
    
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);

				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Get the normal in world space
				fixed3 worldNormal = normalize(i.worldNormal);
				// Get the light direction in world space
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				fixed3 color = ambient + diffuse;
				
				return fixed4(color, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

1.2 半兰伯特模型

c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ) ( 0.5 ( n ^ ⋅ l ^ ) + 0.5 ) c_{diffuse}=(c_{light}\cdot m_{diffuse})(0.5(\widehat{n}\cdot \widehat{l})+0.5) cdiffuse=clightmdiffuse(0.5(n l )+0.5)

这样可以将 n ^ ⋅ l ^ \widehat{n}\cdot \widehat{l} n l 的结果从[-1,1]映射到[0,1];原兰伯特模型中模型背光面只会映射到0,现在不同的点积结果可以映射到不同的值,模型背光面就可以有明暗变化。

实践

Shader "Unity Shaders Book/Chapter 6/Half Lambert" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}
	SubShader {
    
    
		Pass {
    
     
			Tags {
    
     "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			
			struct a2v {
    
    
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
    
    
				float4 pos : SV_POSITION;
				//使用模型的第一套纹理坐标
				float3 worldNormal : TEXCOORD0;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Get the normal in world space
				fixed3 worldNormal = normalize(i.worldNormal);
				// Get the light direction in world space
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
				
				fixed3 color = ambient + diffuse;
				
				return fixed4(color, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Diffuse"
}


二、高光反射

2.1 Phong模型

r ^ = 2 ( n ^ ⋅ l ^ ) n ^ − l ^ \widehat{r}=2(\widehat{n}\cdot \widehat{l})\widehat{n}-\widehat{l} r =2(n l )n l
其中, r ^ \widehat{r} r 为反射方向;
c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , v ^ ⋅ r ^ ) m g l o s s c_{specular}=(c_{light}\cdot m_{specular})max(0,\widehat{v}\cdot \widehat{r})^{m_{gloss}} cspecular=clightmspecularmax(0,v r )mgloss
其中, v ^ \widehat{v} v 为视角方向, m g l o s s m_{gloss} mgloss是材质的光泽度。

实践

逐顶点:

Shader "Unity Shaders Book/Chapter 6/Specular Vertex-Level" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
    
    
		Pass {
    
     
			Tags {
    
     "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
    
    
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
    
    
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Transform the normal from object space to world space
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				// Get the light direction in world space
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				// Get the reflect direction in world space
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				// Get the view direction in world space
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
				
				// Compute specular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
				
				o.color = ambient + diffuse + specular;
							 	
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				return fixed4(i.color, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

逐像素:

Shader "Unity Shaders Book/Chapter 6/Specular Pixel-Level" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
    
    
		Pass {
    
     
			Tags {
    
     "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.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;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				// Transform the vertex from object spacet to world space
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				// Get the reflect direction in world space
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				// Get the view direction in world space
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
				// Compute specular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

2.2 Blinn-Phong模型

Blinn模型避免了计算反射方向 r ^ \widehat{r} r ,引入新矢量 h ^ \widehat{h} h ;
h ^ = v ^ + l ^ ∣ v ^ + l ^ ∣ \widehat{h}=\frac{\widehat{v}+\widehat{l}}{\left | \widehat{v}+\widehat{l} \right |} h =v +l v +l

c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , n ^ ⋅ h ^ ) m g l o s s c_{specular}=(c_{light}\cdot m_{specular})max(0,\widehat{n}\cdot \widehat{h})^{m_{gloss}} cspecular=clightmspecularmax(0,n h )mgloss
在摄像机和光源距离模型足够远时,Blinn模型更快,因为此时 v ^ \widehat{v} v l ^ \widehat{l} l 相当于定值,因此 h ^ \widehat{h} h 将是一个常量。

实践

Shader "Unity Shaders Book/Chapter 6/Blinn-Phong" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
    
    
		Pass {
    
     
			Tags {
    
     "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.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;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				
				// Transform the vertex from object spacet to world space
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
				
				// Get the view direction in world space
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
				// Get the half direction in world space
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				// Compute specular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

使用内置函数改写:

Shader "Unity Shaders Book/Chapter 6/Blinn-Phong Use Built-in Functions" {
    
    
	Properties {
    
    
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(1.0, 500)) = 20
	}
	SubShader {
    
    
		Pass {
    
     
			Tags {
    
     "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
    
    
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
    
    
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float4 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v) {
    
    
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Use the build-in funtion to compute the normal in world space
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
    
    
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 worldNormal = normalize(i.worldNormal);
				//  Use the build-in funtion to compute the light direction in world space
				// Remember to normalize the result
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
				
				// Use the build-in funtion to compute the view direction in world space
				// Remember to normalize the result
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

猜你喜欢

转载自blog.csdn.net/memory_MM_forever/article/details/113781804