shader篇-高光反射模型
背景
這裏的高光反射模型,並不是現實意義上的高光反射,而是隻是用來計算物理上那些沿着完全鏡面反射方向被反射的光線,讓物體看起來有光澤,例如金屬材質
高光反射計算公式如下
v是視角方向,r是反射方向
反射方向可用表面法線n和光照方向l計算
逐頂點光照
Shader "Test Shader/SpecularVertexLevel{
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 color:COLOR;
};
v2f vert(a2v v){
v2f o;
//利用unity內置的模型-觀察-投影矩陣將頂點座標轉換到裁剪空間
o.pos=UnityObjectToClipPos(v.vertex);
//通過內置變量獲取環境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
//法線轉換到世界座標
//unity_WorldToObject爲模型空間到世界空間的變換矩陣的逆矩陣
fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
//獲取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式計算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//利用Cg內置反射光線方向計算函數計算反射光線
fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
//mul(unity_ObjectToWorld, v.vertex)將頂點座標轉換爲世界座標
//視角方向=攝像頭位置-頂點位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
//根據公式計算高光反射
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 "Test Shader/SpecularPixelLevel"{
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;
//利用unity內置的模型-觀察-投影矩陣將頂點座標轉換到裁剪空間
o.pos=UnityObjectToClipPos(v.vertex);
//法線轉換到世界座標
//unity_WorldToObject爲模型空間到世界空間的變換矩陣的逆矩陣
o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
//通過內置變量獲取環境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
//獲取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式計算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//利用Cg內置反射光線方向計算函數計算反射光線
fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
//mul(unity_ObjectToWorld, v.vertex)將頂點座標轉換爲世界座標
//視角方向=攝像頭位置-頂點位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
//根據公式計算高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient+diffuse+specular,1.0);
}
ENDCG
}
}
FallBack "Specular"}
這樣的光照反射模型更平滑,而這一光照模型就是著名的phong光照模型
Blinn-Phong光照模型
blinn模型沒有使用反射方向。而是引入了矢量h
blinn模型光照公式如下
Shader "Test Shader/BlinnPhone"{
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;
//利用unity內置的模型-觀察-投影矩陣將頂點座標轉換到裁剪空間
o.pos=UnityObjectToClipPos(v.vertex);
//法線轉換到世界座標
//unity_WorldToObject爲模型空間到世界空間的變換矩陣的逆矩陣
o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
//通過內置變量獲取環境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
//獲取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式計算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//mul(unity_ObjectToWorld, v.vertex)將頂點座標轉換爲世界座標
//視角方向=攝像頭位置-頂點位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
//計算矢量h
fixed3 h=normalize(worldLight+viewDir);
//根據公式計算高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, h)), _Gloss);
return fixed4(ambient+diffuse+specular,1.0);
}
ENDCG
}
}
FallBack "Specular"}
這樣的高光反射模型反射部分更大更亮些,大多數情況下,我們都會選擇這一高光反射模型
最後結果如下
從右到左分別是逐頂點光照,逐像素光照phong模型,blinn模型