什么是光照模型
光照模型就是一個(gè)公式,使用這個(gè)公式來計(jì)算在某個(gè)點(diǎn)的光照效果
標(biāo)準(zhǔn)光照模型
在標(biāo)準(zhǔn)光照模型里面,我們把進(jìn)入攝像機(jī)的光分為下面四個(gè)部分
自發(fā)光
高光反射
Blinn光照模型
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和視野方向的夾角
Blinn-Phong光照模型
Specular=直射光 * pow( max(cosθ,0),10) θ:是法線和x的夾角 x 是平行光和視野方向的平分線
漫反射 Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
環(huán)境光
Tags{ "LightMode"="ForwardBase" }
只有定義了正確的LightMode才能得到一些Unity的內(nèi)置光照變量
include "Lighting.cginc"
包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量
normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
max() 用來取得函數(shù)中最大的一個(gè)
dot 用來取得兩個(gè)向量的點(diǎn)積
_WorldSpaceLightPos0 取得平行光的位置
_LightColor0取得平行光的顏色
UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
UNITY_LIGHTMODEL_AMBIENT用來獲取環(huán)境光
(1)蘭伯特光照模型計(jì)算公式(漫反射計(jì)算方式)
(漫反射)Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
下面我們使用逐頂點(diǎn)光照和逐像素光照來實(shí)現(xiàn)蘭伯特光照模型計(jì)算公式。
逐頂點(diǎn)光照
兩種方式可以實(shí)現(xiàn)漫反射 第一種是逐頂點(diǎn)實(shí)現(xiàn)漫反射,第二種方式是逐像素實(shí)現(xiàn)漫反射。
逐頂點(diǎn)就是將光照的計(jì)算方式放在頂點(diǎn)函數(shù)里面,因?yàn)轫旤c(diǎn)個(gè)數(shù)是有限的,而像素個(gè)數(shù)是無限的,逐頂點(diǎn)計(jì)算是頂點(diǎn)與頂點(diǎn)之間的計(jì)算時(shí)是使用了插值的計(jì)算方式計(jì)算,這樣比較平和,但是頂點(diǎn)是直接計(jì)算出來的所以頂點(diǎn)上不會(huì)太平和。下面我們來實(shí)現(xiàn)第一種逐頂點(diǎn)。
Shader"Diffuse"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
}
SubShader{
Pass {
//定義正確的LightMode得到內(nèi)置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量 相當(dāng)于引入內(nèi)置的光照模型
#include "Lighting.cginc"http://取得第一個(gè)直射光的顏色 _LightColor0第一個(gè)值是光的位置_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 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
f.position = UnityObjectToClipPos(v.vertex);
//環(huán)境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向 因?yàn)楣馐瞧叫泄? //_LightColor0取得平行光的顏色
//max() 用來取得函數(shù)中最大的一個(gè)
//dot 用來取得兩個(gè)向量的點(diǎn)積
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"
}
因?yàn)樵诔绦蚶锩嬖O(shè)置了環(huán)境光所以在暗部會(huì)受到環(huán)境光的影響

這里設(shè)置受SkyBox環(huán)境光的影響,也可以自己將其他物體或者顏色設(shè)置成環(huán)境光顏色

逐像素光照
逐頂點(diǎn)光照沒有逐像素光照渲染的平和 ,但是逐像素光照渲染更加耗費(fèi)性能
Shader"Fragment"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
}
SubShader{
Pass {
//定義正確的LightMode得到內(nèi)置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量 相當(dāng)于引入內(nèi)置的光照模型
#include "Lighting.cginc"http://取得第一個(gè)直射光的顏色 _LightColor0第一個(gè)值是光的位置_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 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
return f;
}
fixed4 frag(v2f f) :SV_Target{
//環(huán)境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.worldNormalDir);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向 因?yàn)楣馐瞧叫泄?
//_LightColor0取得平行光的顏色
//max() 用來取得函數(shù)中最大的一個(gè)
//dot 用來取得兩個(gè)向量的點(diǎn)積
fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
fixed3 tempColor = diffuse + ambient;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}

(2)半蘭伯特光照模型計(jì)算公式(漫反射計(jì)算方式)
Diffuse = 直射光顏色 *( cosθ *0.5 +0.5 )
因?yàn)樘m伯特光照模型在背光面會(huì)完全看不清,所以半蘭伯特光照模型應(yīng)時(shí)而生。

Shader"Lambert"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
}
SubShader{
Pass {
//定義正確的LightMode得到內(nèi)置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量 相當(dāng)于引入內(nèi)置的光照模型
#include "Lighting.cginc"http://取得第一個(gè)直射光的顏色 _LightColor0第一個(gè)值是光的位置_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 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
return f;
}
fixed4 frag(v2f f) :SV_Target{
//環(huán)境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.worldNormalDir);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向 因?yàn)楣馐瞧叫泄?
float halfLambert = dot(normalDir, lightDir)*0.5 + 0.5;
//_LightColor0取得平行光的顏色
//max() 用來取得函數(shù)中最大的一個(gè)
//dot 用來取得兩個(gè)向量的點(diǎn)積
fixed3 diffuse = _LightColor0.rgb*halfLambert*_Diffuse.rgb;//取得漫反射的顏色
fixed3 tempColor = diffuse + ambient;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}

這樣對(duì)比一下背光面蘭伯特光照模型和半蘭伯特光照模型??梢钥吹胶苊黠@的差別,半蘭伯特光照模型即使暗部就不會(huì)是全黑。
(3)高光反射計(jì)算方式
Blinn光照模型 計(jì)算公式
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和視野方向的夾角
這里使用逐頂點(diǎn)光照模型寫出高光反射。
Shader"Specular"{
properties{
_Diffuse("Color",Color) = (1,1,1,1)
_Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
_Gloss("Gloss",Range(8,200))=10//控制高光強(qiáng)度
}
SubShader{
Pass {
//定義正確的LightMode得到內(nèi)置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量 相當(dāng)于引入內(nèi)置的光照模型
#include "Lighting.cginc"http://取得第一個(gè)直射光的顏色 _LightColor0第一個(gè)值是光的位置_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 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環(huán)境光
//_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向 因?yàn)楣馐瞧叫泄?
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);//計(jì)算高光
f.color = diffuse+ ambient+ specular;
return f;
}
fixed4 frag(v2f f) :SV_Target{
return fixed4(f.color,1);
}
ENDCG
}
}
Fallback"Diffuse"
}


下面我們用逐像素來實(shí)現(xiàn)高光反射
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//控制高光強(qiáng)度
}
SubShader{
Pass {
//定義正確的LightMode得到內(nèi)置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量 相當(dāng)于引入內(nèi)置的光照模型
#include "Lighting.cginc"http://取得第一個(gè)直射光的顏色 _LightColor0第一個(gè)值是光的位置_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 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
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;//環(huán)境光
//_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.WorldNomormal);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向 因?yàn)楣馐瞧叫泄?
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);//計(jì)算高光
fixed3 tempColor = diffuse + ambient + specular;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
可以看到逐像素更加柔和平滑

Blinn-Phong光照模型
下面對(duì)我們對(duì)Blinn光照模型又作了一種改進(jìn),其計(jì)算方式為:
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//控制高光強(qiáng)度
}
SubShader{
Pass {
//定義正確的LightMode得到內(nèi)置的光照變量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量 相當(dāng)于引入內(nèi)置的光照模型
#include "Lighting.cginc"http://取得第一個(gè)直射光的顏色 _LightColor0第一個(gè)值是光的位置_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 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
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;//環(huán)境光
//_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.WorldNomormal);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向 因?yàn)楣馐瞧叫泄?
//WorldSpaceLightDir(float4 v) 模型空間中的頂點(diǎn)坐標(biāo) == 》世界空間中從這個(gè)點(diǎn)到光源的方向
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) 世界空間中的頂點(diǎn)坐標(biāo)==》世界空間從這個(gè)點(diǎn)到攝像機(jī)的觀察方向
fixed3 vlewDir = normalize(UnityWorldSpaceViewDir(f.WorldVertex));
fixed3 halfDir = normalize(vlewDir + lightDir);
fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);//計(jì)算高光
fixed3 tempColor = diffuse + ambient + specular;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
在這里我們可以看到Blinn-Phong光照模型的高光更加大更加亮,并且在背光面B模型因?yàn)橐恍┯?jì)算所以背光面也會(huì)有一些高光,但是B—P模型就不會(huì)出現(xiàn)這種現(xiàn)象,一般我們更傾向于Blinn-Phong的計(jì)算方式.

