什么是光照模型
光照模型就是一個公式,使用這個公式來計算在某個點的光照效果
標準光照模型
在標準光照模型里面,我們把進入攝像機的光分為下面四個部分
自發光
高光反射
Blinn光照模型
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和視野方向的夾角
Blinn-Phong光照模型
Specular=直射光 * pow( max(cosθ,0),10) θ:是法線和x的夾角 x 是平行光和視野方向的平分線
漫反射 Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
環境光
Tags{ "LightMode"="ForwardBase" }
只有定義了正確的LightMode才能得到一些Unity的內置光照變量
include "Lighting.cginc"
包含unity的內置的文件,才可以使用unity內置的一些變量
normalize() 用來把一個向量,單位化(原來方向保持不變,長度變為1)
max() 用來取得函數中最大的一個
dot 用來取得兩個向量的點積
_WorldSpaceLightPos0 取得平行光的位置
_LightColor0取得平行光的顏色
UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
UNITY_LIGHTMODEL_AMBIENT用來獲取環境光
(1)蘭伯特光照模型計算公式(漫反射計算方式)
(漫反射)Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
下面我們使用逐頂點光照和逐像素光照來實現蘭伯特光照模型計算公式。
逐頂點光照
兩種方式可以實現漫反射 第一種是逐頂點實現漫反射,第二種方式是逐像素實現漫反射。
逐頂點就是將光照的計算方式放在頂點函數里面,因為頂點個數是有限的,而像素個數是無限的,逐頂點計算是頂點與頂點之間的計算時是使用了插值的計算方式計算,這樣比較平和,但是頂點是直接計算出來的所以頂點上不會太平和。下面我們來實現第一種逐頂點。
Shader"Diffuse"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
}
SubShader{
Pass {
//定義正確的LightMode得到內置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內置的文件,才可以使用unity內置的一些變量 相當于引入內置的光照模型
#include "Lighting.cginc"http://取得第一個直射光的顏色 _LightColor0第一個值是光的位置_WorldSpaceLightPos0
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v) {
v2f f;
//UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
f.position = UnityObjectToClipPos(v.vertex);
//環境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當于把模型空間轉換到世界空間
//normalize() 用來把一個向量,單位化(原來方向保持不變,長度變為1)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對于每一個頂點來說 光的位置就是光的方向 因為光是平行光
//_LightColor0取得平行光的顏色
//max() 用來取得函數中最大的一個
//dot 用來取得兩個向量的點積
fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
f.color = diffuse+ ambient;
return f;
}
fixed4 frag(v2f f) :SV_Target{
return fixed4(f.color,1);
}
ENDCG
}
}
Fallback"VertexLit"
}
因為在程序里面設置了環境光所以在暗部會受到環境光的影響
這里設置受SkyBox環境光的影響,也可以自己將其他物體或者顏色設置成環境光顏色
逐像素光照
逐頂點光照沒有逐像素光照渲染的平和 ,但是逐像素光照渲染更加耗費性能
Shader"Fragment"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
}
SubShader{
Pass {
//定義正確的LightMode得到內置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內置的文件,才可以使用unity內置的一些變量 相當于引入內置的光照模型
#include "Lighting.cginc"http://取得第一個直射光的顏色 _LightColor0第一個值是光的位置_WorldSpaceLightPos0
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
fixed3 worldNormalDir : COLOR0;
};
v2f vert(a2v v) {
v2f f;
//UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
return f;
}
fixed4 frag(v2f f) :SV_Target{
//環境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
fixed3 normalDir = normalize(f.worldNormalDir);//相當于把模型空間轉換到世界空間
//normalize() 用來把一個向量,單位化(原來方向保持不變,長度變為1)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點來說 光的位置就是光的方向 因為光是平行光
//_LightColor0取得平行光的顏色
//max() 用來取得函數中最大的一個
//dot 用來取得兩個向量的點積
fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
fixed3 tempColor = diffuse + ambient;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
(2)半蘭伯特光照模型計算公式(漫反射計算方式)
Diffuse = 直射光顏色 *( cosθ *0.5 +0.5 )
因為蘭伯特光照模型在背光面會完全看不清,所以半蘭伯特光照模型應時而生。
Shader"Lambert"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
}
SubShader{
Pass {
//定義正確的LightMode得到內置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內置的文件,才可以使用unity內置的一些變量 相當于引入內置的光照模型
#include "Lighting.cginc"http://取得第一個直射光的顏色 _LightColor0第一個值是光的位置_WorldSpaceLightPos0
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
fixed3 worldNormalDir : COLOR0;
};
v2f vert(a2v v) {
v2f f;
//UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
return f;
}
fixed4 frag(v2f f) :SV_Target{
//環境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
fixed3 normalDir = normalize(f.worldNormalDir);//相當于把模型空間轉換到世界空間
//normalize() 用來把一個向量,單位化(原來方向保持不變,長度變為1)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點來說 光的位置就是光的方向 因為光是平行光
float halfLambert = dot(normalDir, lightDir)*0.5 + 0.5;
//_LightColor0取得平行光的顏色
//max() 用來取得函數中最大的一個
//dot 用來取得兩個向量的點積
fixed3 diffuse = _LightColor0.rgb*halfLambert*_Diffuse.rgb;//取得漫反射的顏色
fixed3 tempColor = diffuse + ambient;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
這樣對比一下背光面蘭伯特光照模型和半蘭伯特光照模型。可以看到很明顯的差別,半蘭伯特光照模型即使暗部就不會是全黑。
(3)高光反射計算方式
Blinn光照模型 計算公式
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和視野方向的夾角
這里使用逐頂點光照模型寫出高光反射。
Shader"Specular"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
_Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
_Gloss("Gloss",Range(8,200))=10//控制高光強度
}
SubShader{
Pass {
//定義正確的LightMode得到內置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內置的文件,才可以使用unity內置的一些變量 相當于引入內置的光照模型
#include "Lighting.cginc"http://取得第一個直射光的顏色 _LightColor0第一個值是光的位置_WorldSpaceLightPos0
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
half _Gloss;
fixed4 _Specular;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v) {
v2f f;
//UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環境光
//_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當于把模型空間轉換到世界空間
fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對于每一個頂點來說 光的位置就是光的方向 因為光是平行光
fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
fixed3 vlewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex, unity_WorldToObject).xyz);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot( reflectDir, vlewDir), 0), _Gloss);//計算高光
f.color = diffuse+ ambient+ specular;
return f;
}
fixed4 frag(v2f f) :SV_Target{
return fixed4(f.color,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
下面我們用逐像素來實現高光反射
Shader"Specular Fragment"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
_Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
_Gloss("Gloss",Range(8,200))=10//控制高光強度
}
SubShader{
Pass {
//定義正確的LightMode得到內置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內置的文件,才可以使用unity內置的一些變量 相當于引入內置的光照模型
#include "Lighting.cginc"http://取得第一個直射光的顏色 _LightColor0第一個值是光的位置_WorldSpaceLightPos0
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
half _Gloss;
fixed4 _Specular;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
float3 WorldNomormal : TEXCOORD0;
float3 WorldVertex:TEXCOORD1;
};
v2f vert(a2v v) {
v2f f;
//UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.WorldNomormal = mul(v.normal, (float3x3)unity_WorldToObject);
f.WorldVertex = mul(v.vertex,_World2Object).xyz;
return f;
};
fixed4 frag(v2f f) :SV_Target{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環境光
//_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
fixed3 normalDir = normalize(f.WorldNomormal);//相當于把模型空間轉換到世界空間
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點來說 光的位置就是光的方向 因為光是平行光
fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
fixed3 vlewDir = normalize(_WorldSpaceCameraPos.xyz - f.WorldVertex);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot(reflectDir, vlewDir), 0), _Gloss);//計算高光
fixed3 tempColor = diffuse + ambient + specular;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
可以看到逐像素更加柔和平滑
Blinn-Phong光照模型
下面對我們對Blinn光照模型又作了一種改進,其計算方式為:
Specular=直射光 * pow( max(cosθ,0),10) θ:是法線和x的夾角 x 是平行光和視野方向的平分線。
Shader"Learning/Specular Fragment BlinnPhone"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
_Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
_Gloss("Gloss",Range(8,200))=10//控制高光強度
}
SubShader{
Pass {
//定義正確的LightMode得到內置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內置的文件,才可以使用unity內置的一些變量 相當于引入內置的光照模型
#include "Lighting.cginc"http://取得第一個直射光的顏色 _LightColor0第一個值是光的位置_WorldSpaceLightPos0
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
half _Gloss;
fixed4 _Specular;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
float3 WorldNomormal : TEXCOORD0;
float4 WorldVertex:TEXCOORD1;
};
v2f vert(a2v v) {
v2f f;
//UNITY_MATRIX_MVP 這個矩陣用來把一個坐標從模型空間轉換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
/*f.WorldNomormal = mul(v.normal, (float3x3)unity_WorldToObject);*/
//UnityObjectToWorldNormal(float3 norm) 把法線方向 模型空間 == 》世界空間
f.WorldNomormal = UnityObjectToWorldNormal(v.normal);
f.WorldVertex = mul(v.vertex,unity_WorldToObject);
return f;
};
fixed4 frag(v2f f) :SV_Target{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環境光
//_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
fixed3 normalDir = normalize(f.WorldNomormal);//相當于把模型空間轉換到世界空間
//fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點來說 光的位置就是光的方向 因為光是平行光
//WorldSpaceLightDir(float4 v) 模型空間中的頂點坐標 == 》世界空間中從這個點到光源的方向
fixed3 lightDir = normalize(WorldSpaceLightDir(f.WorldVertex).xyz);
fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
//fixed3 vlewDir = normalize(_WorldSpaceCameraPos.xyz - f.WorldVertex);
//UnityWorldSpaceViewDir(float4 v) 世界空間中的頂點坐標==》世界空間從這個點到攝像機的觀察方向
fixed3 vlewDir = normalize(UnityWorldSpaceViewDir(f.WorldVertex));
fixed3 halfDir = normalize(vlewDir + lightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);//計算高光
fixed3 tempColor = diffuse + ambient + specular;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
在這里我們可以看到Blinn-Phong光照模型的高光更加大更加亮,并且在背光面B模型因為一些計算所以背光面也會有一些高光,但是B—P模型就不會出現這種現象,一般我們更傾向于Blinn-Phong的計算方式.