Unity Shader着色器学习(二)

半兰伯特光照模型

半兰伯特光照模型

为什么需要半兰伯特光照模型
在这里插入图片描述
在前面我们使用的是兰伯特光照模型,它有一个问题,在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节表现。实际上我们可以通过添加环境光来得到非全黑的效果,但即便这样仍然无法解决背光面明暗一样的缺点。为此,有一种改善技术被提出来,这就是半兰伯特(Half Lambert)光照模型。

半兰伯特光照模型公式

广义的半兰伯特光照模型的公式如下:
在这里插入图片描述

c_diffuse=(c_light∙m_diffuse)(α(n ̂⋅l ̂ )+β)
可以看出,与原兰伯特模型相比,半兰伯特光照模型没有使用max操作来防止n ̂和l ̂的点积为负值,而是对其结果进行了一个α倍的缩放再加上一个β大小的偏移。绝大多数情况下,α和β的值均为0.5,即公式为:
c_diffuse=(c_light∙m_diffuse )(“0.5” (n ̂⋅l ̂ )+“0.5” )
在这里插入图片描述

通过这样的方式,我们可以把n ̂⋅l ̂的结果范围从[-1, 1]映射到[0, 1]范围内。也就是说,对于模型的背光面,在原兰伯特光照模型中点积结果将映射到同一个值,即0值处;而在半兰伯特模型中,背光面也可以有明暗变化,不同的点积结果会映射到不同的值上。
需要注意的是,半兰伯特是没有任何物理依据的,它仅仅是一个视觉加强效果。

由于n ̂⋅l ̂的值等于cos⁡θ,因此我们可以查看(“0.5” (n ̂⋅l ̂ )+“0.5” )的函数图像,这样比较清晰明了。
在这里插入图片描述

Shader编写

DiffuseFragment_HalfLambert.shader

Shader "Shader Learning Siki/Lighting/Diffuse PerFragment Half Lambert"
{
    
    
	Properties
	{
    
    
		_Diffuse("Diffuse Color", Color) =(1.0, 1.0, 1.0, 1.0)
	}

	SubShader
	{
    
    
		Pass
		{
    
    
			Tags{
    
     "LightMode" = "ForwardBase" }  //这里一定不要忘记定义了,要定义合适的LightMode

	CGPROGRAM

	#include "Lighting.cginc"
	#include "UnityCG.cginc"

	#pragma vertex vert
	#pragma fragment frag

	fixed4 _Diffuse;

	struct a2v
	{
    
    
		float4 vertex : POSITION;
		float3 normal : NORMAL;
	};

	struct v2f
	{
    
    
		float4 pos : SV_POSITION;
		fixed3 worldNormal : COLOR;
	};

	v2f vert(a2v v)
	{
    
    
		v2f o;
		o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
		o.worldNormal = mul(v.normal, (float3x3)_World2Object);

	return o;
	}

	fixed4 frag(v2f i) : SV_Target
	{
    
    
		fixed3 worldNormalize = normalize(i.worldNormal);
		//fixed3 worldNormal = normalize(mul((float3x3)_Object2World, v.normal));
		fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);   //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光

	fixed halfLambert = dot(worldNormalize, worldLight) * 0.5 + 0.5;
	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;

	fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
	fixed3 color = diffuse + ambient;

	return fixed4(color, 1.0);
	}

	ENDCG
	}
	}

}

逐顶点高光反射计算

高光反射光照模型

Specular = 直射光 * pow ( max(cos⁡θ, 0) , 高光的参数 )
θ:是反射光方向和视野方向的夹角
在这里插入图片描述

pow 是求次方,pow(x, y)是求x的y次方
由下可以看出,高光的参数值越大,函数值衰减的越快(后半部分有值的实际都为0,因为有max的控制),所以反射光方向和视野方向的夹角越大得到的高光反射值越接近0,而且衰减得特别快。
在这里插入图片描述
在这里插入图片描述

逐顶点的高光反射Shader实现

在逐顶点漫反射的基础上实现
Specular.shader

Shader "Shader Learning Siki/Lighting/Specular PerVertex"
{
    
    
	Properties
	{
    
    
		_Diffuse("Diffuse Color", Color) = (1, 1, 1, 1)
		_Specular("Specular Color", Color) = (1, 1, 1, 1)
		_Gloss("Gloss", Range(8, 200)) = 10
	}
	SubShader
	{
    
    
		Pass
		{
    
    
			Tags{
    
     "LightMode" = "ForwardBase"}
			
			CGPROGRAM
			
			//用于取得第一个直射光的颜色 _LightColor0
			//用于取得第一个直射光的位置 _WorldSpaceLightPos0
			#include "Lighting.cginc" 
			#pragma vertex vert
			#pragma fragment frag
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			half _Gloss;
			
			
			struct a2v
			{
    
    
				float4 vertex : POSITION;  //告诉Unity把模型空间下的顶点坐标填充给vertex
				float3 normal : NORMAL;
			};
			
			struct v2f
			{
    
    
				float4 position : SV_POSITION;
				fixed3 color : COLOR;
			};
			
			v2f vert(a2v v)
			{
    
    
				v2f f;
				f.position = mul(UNITY_MATRIX_MVP, v.vertex);
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;  //获取环境光
				
				fixed3 normalDir = normalize( mul(v.normal, (float3x3)_World2Object));
				
				fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
				fixed3 diffuse = _LightColor0.rgb * max(dot( normalDir, lightDir), 0) * _Diffuse.rgb;  //取得漫反射的颜色
				
				fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
				
				//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul( v.vertex, _World2Object).xyz);   //不可以这样子用,会有问题  
				
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(_Object2World, v.vertex).xyz);  
				
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( max(0, dot(reflectDir, viewDir)), _Gloss);
				
				f.color = diffuse + specular + ambient;  
				return f;
			}
			
			fixed4 frag(v2f f) : SV_Target
			{
    
    
				return fixed(f.color, 1.0);
			}
			
			ENDCG
		}
	}
	
	Fallback "Diffuse"
}

逐像素高光反射

Shader "Shader Learning Siki/Lighting/Specular Pixel Level"
{
    
    
	Properties
	{
    
    
		_Diffuse("Diffuse Color", Color) = (1, 1, 1, 1)
		_Specular("Specular Color", Color) = (1, 1, 1, 1)
		_Gloss("Gloss", Range(8.0, 200)) = 20
	}
	SubShader
	{
    
    
		Pass
		{
    
    
			Tags{
    
     "LightMode" = "ForwardBase"}
			
			CGPROGRAM
			
			//用于取得第一个直射光的颜色 _LightColor0
			//用于取得第一个直射光的位置 _WorldSpaceLightPos0
			#include "Lighting.cginc" 
			#pragma vertex vert
			#pragma fragment frag
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			half _Gloss;
			
			
			struct a2v
			{
    
    
				float4 vertex : POSITION;  //告诉Unity把模型空间下的顶点坐标填充给vertex
				float3 normal : NORMAL;
			};
			
			struct v2f
			{
    
    
				float4 position : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v)
			{
    
    
				v2f f;
				f.position = mul(UNITY_MATRIX_MVP, v.vertex);
				f.worldNormal = mul(v.normal, (float3x3)_World2Object);
				f.worldPos = mul(_Object2World, v.vertex).xyz;
				
				return f;
			}
			
			fixed4 frag(v2f f) : SV_Target
			{
    
    
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;  //获取环境光
				
				fixed3 normalDir = normalize(f.worldNormal);
				
				fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
				fixed3 diffuse = _LightColor0.rgb * max(dot( normalDir, lightDir), 0) * _Diffuse.rgb;  //取得漫反射的颜色
				
				fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
				
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos);  
				
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( saturate(dot(reflectDir, viewDir)), _Gloss);
				
				fixed3 color = diffuse + specular + ambient;  
			
				return fixed4(color, 1.0);
			}
			
			ENDCG
		}
	}
	
	Fallback "Diffuse"
}

Blinn-Phong光照模型

在之前我们使用的是Phong光照模型
高光反射还有一种模型Blinn-Phong光照模型
Specular = 直射光 * pow ( max(cos⁡θ, 0) , 高光的参数 )
θ:是光源方向和视野方向的夹角的平分线

Shader "Shader Learning/06BasicLighting/Specular BlinnPhong Pixel Level"
{
    
    
	Properties
	{
    
    
		_Diffuse("Diffuse Color", Color) = (1, 1, 1, 1)
		_Specular("Specular Color", Color) = (1, 1, 1, 1)
		_Gloss("Gloss", Range(8.0, 200)) = 20
	}
	SubShader
	{
    
    
		Pass
		{
    
    
			Tags{
    
     "LightMode" = "ForwardBase"}
			
			CGPROGRAM
			
			//用于取得第一个直射光的颜色 _LightColor0
			//用于取得第一个直射光的位置 _WorldSpaceLightPos0
			#include "Lighting.cginc" 
			#pragma vertex vert
			#pragma fragment frag
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			half _Gloss;
			
			
			struct a2v
			{
    
    
				float4 vertex : POSITION;  //告诉Unity把模型空间下的顶点坐标填充给vertex
				float3 normal : NORMAL;
			};
			
			struct v2f
			{
    
    
				float4 position : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v)
			{
    
    
				v2f f;
				f.position = mul(UNITY_MATRIX_MVP, v.vertex);
				f.worldNormal = mul(v.normal, (float3x3)_World2Object);
				f.worldPos = mul(_Object2World, v.vertex).xyz;
				
				return f;
			}
			
			fixed4 frag(v2f f) : SV_Target
			{
    
    
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;  //获取环境光
				
				fixed3 normalDir = normalize(f.worldNormal);
				
				fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
				fixed3 diffuse = _LightColor0.rgb * max(dot( normalDir, lightDir), 0) * _Diffuse.rgb;  //取得漫反射的颜色
				
				fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
				
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos);  
				
				fixed3 halfDir = normalize(lightDir + viewDir);

	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( saturate(dot(normalDir, halfDir)), _Gloss);
	
	fixed3 color = diffuse + specular + ambient;  
	
		return fixed4(color, 1.0);
	}
	
	ENDCG
	}
	}
	
	Fallback "Diffuse"
}

猜你喜欢

转载自blog.csdn.net/ithot/article/details/125830238