什么是光照模型
光照模型就是一個公式蛾派,使用這個公式來計算在某個點(diǎn)的光照效果
標(biāo)準(zhǔn)光照模型
在標(biāo)準(zhǔn)光照模型里面,我們把進(jìn)入攝像機(jī)的光分為下面四個部分
自發(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() 用來把一個向量躯护,單位化(原來方向保持不變尸变,長度變?yōu)?)
max() 用來取得函數(shù)中最大的一個
dot 用來取得兩個向量的點(diǎn)積
_WorldSpaceLightPos0 取得平行光的位置
_LightColor0取得平行光的顏色
UNITY_MATRIX_MVP 這個矩陣用來把一個坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
_World2Object 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
UNITY_LIGHTMODEL_AMBIENT用來獲取環(huán)境光
(1)蘭伯特光照模型計算公式(漫反射計算方式)
(漫反射)Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
下面我們使用逐頂點(diǎn)光照和逐像素光照來實現(xiàn)蘭伯特光照模型計算公式忿等。
逐頂點(diǎn)光照
兩種方式可以實現(xiàn)漫反射 第一種是逐頂點(diǎn)實現(xiàn)漫反射,第二種方式是逐像素實現(xiàn)漫反射咽瓷。
逐頂點(diǎn)就是將光照的計算方式放在頂點(diǎn)函數(shù)里面设凹,因為頂點(diǎn)個數(shù)是有限的,而像素個數(shù)是無限的茅姜,逐頂點(diǎn)計算是頂點(diǎn)與頂點(diǎn)之間的計算時是使用了插值的計算方式計算闪朱,這樣比較平和,但是頂點(diǎn)是直接計算出來的所以頂點(diǎn)上不會太平和匈睁。下面我們來實現(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://取得第一個直射光的顏色 _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 這個矩陣用來把一個坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
f.position = UnityObjectToClipPos(v.vertex);
//環(huán)境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//_World2Object 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//normalize() 用來把一個向量航唆,單位化(原來方向保持不變胀蛮,長度變?yōu)?)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對于每一個頂點(diǎn)來說 光的位置就是光的方向 因為光是平行光
//_LightColor0取得平行光的顏色
//max() 用來取得函數(shù)中最大的一個
//dot 用來取得兩個向量的點(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"
}
因為在程序里面設(shè)置了環(huán)境光所以在暗部會受到環(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://取得第一個直射光的顏色 _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 這個矩陣用來把一個坐標(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 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.worldNormalDir);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//normalize() 用來把一個向量,單位化(原來方向保持不變任岸,長度變?yōu)?)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點(diǎn)來說 光的位置就是光的方向 因為光是平行光
//_LightColor0取得平行光的顏色
//max() 用來取得函數(shù)中最大的一個
//dot 用來取得兩個向量的點(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)半蘭伯特光照模型計算公式(漫反射計算方式)
Diffuse = 直射光顏色 *( cosθ *0.5 +0.5 )
因為蘭伯特光照模型在背光面會完全看不清再榄,所以半蘭伯特光照模型應(yīng)時而生。
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://取得第一個直射光的顏色 _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 這個矩陣用來把一個坐標(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 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.worldNormalDir);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//normalize() 用來把一個向量困鸥,單位化(原來方向保持不變,長度變?yōu)?)
//_WorldSpaceLightPos0 取得平行光的位置
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點(diǎn)來說 光的位置就是光的方向 因為光是平行光
float halfLambert = dot(normalDir, lightDir)*0.5 + 0.5;
//_LightColor0取得平行光的顏色
//max() 用來取得函數(shù)中最大的一個
//dot 用來取得兩個向量的點(diǎn)積
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) θ:是反射光方向和視野方向的夾角
這里使用逐頂點(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://取得第一個直射光的顏色 _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 這個矩陣用來把一個坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環(huán)境光
//_World2Object 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對于每一個頂點(diǎ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);//計算高光
f.color = diffuse+ ambient+ specular;
return f;
}
fixed4 frag(v2f f) :SV_Target{
return fixed4(f.color,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
下面我們用逐像素來實現(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://取得第一個直射光的顏色 _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 這個矩陣用來把一個坐標(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 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.WorldNomormal);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點(diǎ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);//計算高光
fixed3 tempColor = diffuse + ambient + specular;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
可以看到逐像素更加柔和平滑
Blinn-Phong光照模型
下面對我們對Blinn光照模型又作了一種改進(jìn)姑荷,其計算方式為:
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://取得第一個直射光的顏色 _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 這個矩陣用來把一個坐標(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 這個矩陣用來把一個方向從世界空間轉(zhuǎn)換到模型空間
fixed3 normalDir = normalize(f.WorldNomormal);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
//fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對于每一個頂點(diǎn)來說 光的位置就是光的方向 因為光是平行光
//WorldSpaceLightDir(float4 v) 模型空間中的頂點(diǎn)坐標(biāo) == 》世界空間中從這個點(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)==》世界空間從這個點(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);//計算高光
fixed3 tempColor = diffuse + ambient + specular;
return fixed4(tempColor,1);
}
ENDCG
}
}
Fallback"Diffuse"
}
在這里我們可以看到Blinn-Phong光照模型的高光更加大更加亮鼠冕,并且在背光面B模型因為一些計算所以背光面也會有一些高光添寺,但是B—P模型就不會出現(xiàn)這種現(xiàn)象,一般我們更傾向于Blinn-Phong的計算方式.