Shader理論《六》高光反射光照模型

 歡迎加入Unity業內qq交流羣:956187480

qq掃描二維碼加羣


 基本光照模型中高光反射部分的計算模型:C_{specular} = (C_{light}\cdot M_{specular})max(0,\hat{v}\cdot \hat{r})^{M_{gloss}}

計算高光反射我們需要知道四個參數:入射光線的顏色跟強度,材質的高光反射係數,視角方向v以及反射方向r。

其中反射方向r可以由表面法線n和光源方向l計算:\hat{r}=\hat{l}-2(\hat{n}\cdot\hat{l})\hat{n},此公式我們可以用函數reflect(i,n),i入射方向跟n法線方向

一.逐頂點高光反射,高光部分明顯不平滑,因爲高光反射部分的計算是非線性的,而頂點着色器計算光照再進行插值的過程是線性的,破壞了原計算的非線性關係就出現了視覺問題。

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "a幻世界/SpecularVertex"
{
    Properties
	{ 
	  _Diffuse ("Diffuse", Color) = (1, 1, 1, 1) //漫反射顏色
	  _Specular ("Specular", Color) = (1, 1, 1, 1) //控制高光的反射顏色
	  _Gloss ("Gloss", Range(8, 256)) = 20 //控制高光範圍
	}
		SubShader
	  {
			Pass{
			Tags { "RenderType" = "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 color : COLOR;
	  };
	  v2f vert(a2v v) {
		  v2f o;
		  o.pos = UnityObjectToClipPos(v.vertex);//模型空間頂點轉換到裁剪空間
		  fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//獲取環境光
		  fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));//把模型空間的法線轉換到世界座標
		  fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);//獲取世界空間的光方向
		  fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLightDir));//計算漫反射
		  fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));//計算反射方向
		  fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-UnityObjectToClipPos(v.vertex));//相機的世界座標-頂點轉換到世界空間=世界空間下的視角方向
		  
		  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);
	  }
		  ENDCG
		}
	  }
    FallBack "Specular"
}

二.逐像素高光反射,按照逐像素處理我們就能得到更加平滑的高光效果

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "a幻世界/SpecularPixel"
{
	Properties
	{
	  _Diffuse("Diffuse", Color) = (1, 1, 1, 1) //漫反射顏色
	  _Specular("Specular", Color) = (1, 1, 1, 1) //控制高光的反射顏色
	  _Gloss("Gloss", Range(8, 256)) = 20 //控制高光範圍
	}
		SubShader
	{
		  Pass{
		  Tags { "RenderType" = "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;
		o.pos = UnityObjectToClipPos(v.vertex);//模型空間頂點轉換到裁剪空間
		o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
		o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
		return o;
	}
	fixed4 frag(v2f i) :SV_Target {
		fixed3 ambienm = UNITY_LIGHTMODEL_AMBIENT.xyz;
	    fixed3 worldNormal = normalize(i.worldNormal);
		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
		fixed3 diffuse = _LightColor0.rbg*_Diffuse.rgb*saturate(dot( worldLightDir, worldNormal ) );
		fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));
		fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
		fixed3 specular = _LightColor0.rgb*_Specular.rbg*pow(saturate(dot(reflectDir, viewDir ) ),_Gloss);
		return fixed4(ambienm+diffuse+specular,1);
	}
		ENDCG

	  }
	}
		FallBack "Specular"
}

三.Blinn-Phong光照模型,高光反射部分看起來更大更亮一些。而具體在實際渲染中選擇哪一種我們要視情況而定

Shader "a幻世界/SpecularBlinnPhong"
{
	Properties
	{
	  _Diffuse("Diffuse", Color) = (1, 1, 1, 1) //漫反射顏色
	  _Specular("Specular", Color) = (1, 1, 1, 1) //控制高光的反射顏色
	  _Gloss("Gloss", Range(8, 256)) = 20 //控制高光範圍
	}
		SubShader
	{
		  Pass{
		  Tags { "RenderType" = "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;
		o.pos = UnityObjectToClipPos(v.vertex);//模型空間頂點轉換到裁剪空間
		o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
		o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
		return o;
	}
	fixed4 frag(v2f i) :SV_Target {
		fixed3 ambienm = UNITY_LIGHTMODEL_AMBIENT.xyz;
		fixed3 worldNormal = normalize(i.worldNormal);
		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
		fixed3 diffuse = _LightColor0.rbg*_Diffuse.rgb*saturate(dot(worldLightDir, worldNormal));
		fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));
		fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
		fixed3 halfDir = normalize(worldLightDir+viewDir);
		fixed3 specular = _LightColor0.rgb*_Specular.rbg*pow(max(0,dot(worldNormal, halfDir)),_Gloss);
		return fixed4(ambienm + diffuse + specular,1);
	}
		ENDCG
	  }
	}
		FallBack "Specular"
}

四.使用Unity內置函數來寫shader以上面的模型爲例

Shader "a幻世界/SpecularBlinnPhongUseBuildInunction"
{
	Properties
	{
	  _Diffuse("Diffuse", Color) = (1, 1, 1, 1) //漫反射顏色
	  _Specular("Specular", Color) = (1, 1, 1, 1) //控制高光的反射顏色
	  _Gloss("Gloss", Range(8, 256)) = 20 //控制高光範圍
	}
    SubShader
	{
		Pass{
		Tags { "RenderType" = "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;
		o.pos = UnityObjectToClipPos(v.vertex);//模型空間頂點轉換到裁剪空間
		o.worldNormal = UnityObjectToWorldNormal(v.normal);
		o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
		return o;
	}
	fixed4 frag(v2f i) :SV_Target {
		fixed3 ambienm = UNITY_LIGHTMODEL_AMBIENT.xyz;
		fixed3 worldNormal = normalize(i.worldNormal);
		fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(i.worldPos));
		fixed3 diffuse = _LightColor0.rbg*_Diffuse.rgb*saturate(dot(worldLightDir, worldNormal));
		fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));
		fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
		fixed3 halfDir = normalize(worldLightDir + viewDir);
		fixed3 specular = _LightColor0.rgb*_Specular.rbg*pow(max(0,dot(worldNormal, halfDir)),_Gloss);
		return fixed4(ambienm + diffuse + specular,1);
	}
		ENDCG

	  }
	}
    FallBack "Diffuse"
}

 歡迎加入Unity業內qq交流羣:956187480

qq掃描二維碼加羣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章