現(xiàn)實世界的光照是極其復(fù)雜的,而且會受到諸多因素的影響,這是以目前我們所擁有的處理能力無法模擬的。因此OpenGL的光照僅僅使用了簡化的模型并基于對現(xiàn)實的估計來進行模擬,這樣處理起來會更容易一些,而且看起來也差不多一樣。這些光照模型都是基于我們對光的物理特性的理解。其中一個模型被稱為馮氏光照模型(Phong Lighting Model)。馮氏光照模型的主要結(jié)構(gòu)由3個元素組成:環(huán)境(Ambient)、漫反射(Diffuse)和鏡面(Specular)光照。這些光照元素看起來像下面這樣:

環(huán)境光照(Ambient Lighting):即使在黑暗的情況下,世界上也仍然有一些光亮(月亮、一個來自遠處的光),所以物體永遠不會是完全黑暗的。我們使用環(huán)境光照來模擬這種情況,也就是無論如何永遠都給物體一些顏色。
漫反射光照(Diffuse Lighting):模擬一個發(fā)光物對物體的方向性影響(Directional Impact)。它是馮氏光照模型最顯著的組成部分。面向光源的一面比其他面會更亮。
鏡面光照(Specular Lighting):模擬有光澤物體上面出現(xiàn)的亮點。鏡面光照的顏色,相比于物體的顏色更傾向于光的顏色。
光照特性
1.發(fā)射光:由物體自身發(fā)光
2.環(huán)境光:就是在環(huán)境中充分散射的光,而且無法分辨光的方向
3.漫反射光:光線來自某個方向,但是在物體上各個方向反射
4.鏡面高光:光線來自一個特定的方向,然后在物體表面上以一個特定的方向反射出去
材質(zhì)屬性
1.泛射材質(zhì):光線直射,反射率較高
2.漫反射材質(zhì):需要考慮光的入射角和反射角的
3.鏡面反射材質(zhì):斑點
4.發(fā)射材質(zhì):物體本身就可以發(fā)光的材質(zhì)
光照計算
1.環(huán)境光的計算
環(huán)境光是不來自任何特定方向的光,在整個場景中經(jīng)典光照模型把它當成一個常量,組成一個合適的第一近似值來縮放場景中的光照部分。
環(huán)境光 = 光源的環(huán)境光顏色 * 物體的材質(zhì)顏色

- 示例代碼
varying vec3 objectColor;
void main()
{
//?至少有%10的光找到物體所有?面
float ambientStrength = 0.1;
//環(huán)境光顏?色
vec3 ambient = ambientStrength * lightColor;
//最終顏?色 = 環(huán)境光顏?色 * 物體顏?色
vec3 result = ambient * objectColor;
gl_FragColor = vec4(result, 1.0);
}
2.發(fā)射光的計算
如果這個物體本身就是有顏色的,比如說夜明珠,那么這個時候這個光就是這個物體材質(zhì)的顏色
發(fā)射顏色 = 物體的反射材質(zhì)顏色
3.漫反射光的計算
漫反射光是散射在各個方向上的均勻的表面特定光源,漫反射光依賴于表面法線方向和光源方向來計算,但是沒有包含視線方向,它同樣依賴于表面的顏色。

漫反射顏色 = 光源的漫反射顏色 * 物體的漫發(fā)射材質(zhì)顏色 * DiffuseFactor
其中N表示法向量,L表示光源,法向量N和光源L之間的夾角決定了光照射的面積。夾角越大照射面積越大。
漫反射光的計算
漫反射因?DiffuseFactor 是光線 與頂點法線向量的點積
DiffuseFactor = max(0,dot(N,L))
漫反射因子
光線照射到物體表面,決定了物體表面的光照強度,光照強度是光本身強度和光線與物體表面法線夾角cos的乘積

結(jié)論:
有效的光照方向是與物體表面法線夾 角在0~90度之間的
- 示例代碼
uniform vec3 lightColor; //光源?
uniform vec3 lightPo; //光源位置
uniform vec3 objectColor; //物體?
uniform vec3 viewPo; //物體位置
varying vec3 outNormal; //傳入當前頂點平面的法向量
void main()
{
//確保法線為單位向量量
vec3 norm = normalize(outNormal); //頂點指向光源 單位向量量
vec3 lightDir = normalize(lightPo - FragPo); //得到兩向量量的cos值 ?小于0則則為0
float diff = max(dot(norm, lightDir),0.0); //得到漫反射收的光源向量量
vec3 diffuse = diff * lightColor;
vec3 result = diffuse * ojbectColor;
gl_FragColor = vec4(result,1.0);
}
4.鏡面光計算
鏡面光是由表面直接反射的高亮光,這個高亮光就像鏡子一樣跟表面材質(zhì)多少有關(guān)。

N : 平?法線 R: 反射光線
a? : 視點與反射光的夾?角
鏡?面反射顏?色 = 光源的鏡?面光的顏?色 * 物體的鏡?面材質(zhì)顏?色 * SpecularFactor
SpecularFactor = power(max(0,dot(N,H)),shininess)
H :視線向量E 與 光線向量L 的半向量 dot(N,H):H,N的點積?幾何意義,平方線與法線夾角的cos值.
shiniess : ?光的反光度.
發(fā)光值
一個物體的發(fā)光值越高,反射光的能力越強,散射得越少,高光點越小。在下面的圖片里,你會看到不同發(fā)光值對視覺(效果)的影響:

- 示例代碼
uniform vec3 viewPo; //視角位置
uniform sampler2D specularTexture; //鏡面紋理
uniform vec3 lightPo; //光源位置
uniform vec3 viewPo; //視角位置
in vec2 outTexCoord; //紋理坐標
in vec3 FragPo; //頂點坐標
in vec3 outNormal; //頂點法向量
void main()
{
//鏡?面強度
float specularStrength = 0.5;
//反射率
float reflectance = 256.0;
//當前頂點 至 光源的的單位向量
vec3 lightDir = normalize(lightPo - FragPo);
//鏡面反射
vec3 viewDir = normalize(viewPo - FragPo);
// reflect (genType I, genType N),返回反射向量
vec3 reflectDir = reflect(-lightDir,outNormal);
//SpecularFactor = power(max(0,dot(N,H)),shininess)
float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
//鏡面反射顏色 = 光源的鏡面光的顏色 * 物體的鏡面材質(zhì)顏色 * SpecularFactor
vec3 specular = specularStrength * spec * texture(specularTexture,outTexCoord).rgb;
}
5.衰減因子
注意
環(huán)境光,漫反射光和鏡?光的強度都會受距離的增大而衰減,只有發(fā)射光和全局環(huán)境光的強度不會受影響
光照顏色 =(環(huán)境顏色 + 漫反射顏色 + 鏡?反射顏色)* 衰減因子
衰減因子 = 1.0/(距離衰減常量 + 線性衰減常量 * 距離 + ?次衰減常量 * 距離的平?)
衰減因子計算公式
- 示例代碼
//距離衰減常量量
float constantPara = 1.0f;
//線性衰減常量量
float linearPara = 0.09f;
//?二次衰減因?子
float quadraticPara = 0.032f;
//距離
float LFDistance = length(lightPo - FragPo);
//衰減因?子
float lightWeakPara = 1.0/(constantPara + linearPara
* LFDistance + quadraticPara * (LFDistance*LFDistance));
6.聚光燈因子
聚光燈夾角cos值 = power(max(0,dot(單位光源位置,單位光線向量)),聚光燈指數(shù));
- 單位光線向量是從光源指向頂點的單位向量
- 聚光燈指數(shù),表示聚光燈的亮度程度
- 公式解讀:單位光源位置 * 單位光線向量 點積 的 聚光燈指數(shù)次方。
聚光燈無過渡 與 有過渡處理

- 增加過渡計算
聚光燈因子 = clamp((外環(huán)的聚光燈角度cos值 - 當前頂點的聚光燈?度cos值)/ (外環(huán)的聚光燈?度cos值- 內(nèi)環(huán)聚光燈的角度的cos值),0,1);
- 示例代碼
//距離衰減常量
//(一些復(fù)雜的計算操作 應(yīng)該讓CPU做,提高效率,不變的量也建議外部傳輸,避免重復(fù)計算)
//內(nèi)錐角cos值
float inCutOff = cos(radians(10.0f)); //外錐?cos值
float outCutOff = cos(radians(15.0f)); //聚光朝向
vec3 spotDir = vec3(-1.2f,-1.0f,-2.0f);
//光源指向物體的向量 和 聚光朝向的 cos值
float theta = dot(lightDir ,normalize(-spotDir)); //內(nèi)外錐角cos差值
float epsilon = inCutOff - outCutOff;
//clamp(a,b,c);若b<a<c 則函數(shù)返回值為a 若不是,則返回值最?為b 最大 為c
// (theta - outCutOff)/epsilon 若theta的?度?于內(nèi)錐角 則其值 >=1 若theta的角度?于外錐角 則其值<=0 這樣光線就在內(nèi)外錐角之間平滑變化.
float intensity = clamp((theta - outCutOff)/epsilon,
0.0,1.0);
7.光照計算終極公式
光照顏色 = 發(fā)射顏色 + 全局環(huán)境顏色 + (環(huán)境顏色 + 漫反射顏色 + 鏡?面反射顏色) * 聚光燈效果 * 衰減因?
一.平面光終極公式示例代碼

- 示例代碼
//環(huán)境因?子
float ambientStrength = 0.3; //鏡面強度
float specularStrength = 2.0; //反射強度
float reflectance = 256.0;
//平行光方向
vec3 paraLightDir = normalize(vec3(-0.2,-1.0,-0.3));
//環(huán)境光
vec3 ambient = ambientStrength * texture(Texture ,outTexCoord).rgb;
//漫反射
vec3 norm = normalize(outNormal);
vec3 lightDir = normalize(lightPo - FragPo); //當前頂點至光源的單位向量
float diff = max(dot(norm ,paraLightDir),0.0);
vec3 diffuse = diff * lightColor*texture(Texture ,outTexCoord).rgb;
//鏡?反射
vec3 viewDir = normalize(viewPo - FragPo);
vec3 reflectDir = reflect(-paraLightDir ,outNormal);
float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
vec3 specular = specularStrength * spec * texture(specularTexture ,outTexCoord).rgb;
//最終光照顏色
vec3 res = ambient + diffuse + specular;
FragColor = vec4(res,1.0);
二.點光源終極公式示例代碼

- 示例代碼
float ambientStrength = 0.3;
float specularStrength = 2.0;
float reflectance = 256.0;
float constantPara = 1.0f;
float linearPara = 0.09f;
float quadraticPara = 0.032f; //?次項部分因數(shù)
//環(huán)境光
vec3 ambient = ambientStrength * texture(Texture ,outTexCoord).rgb;
//漫反射
vec3 norm = normalize(outNormal);
vec3 lightDir = normalize(lightPo - FragPo); //當前頂點至光源的單位向量
//點光源
float diff = max(dot(norm ,lightDir),0.0); //光源與法線夾角
vec3 diffuse = diff * lightColor*texture(Texture ,outTexCoord).rgb;
//鏡?反射
vec3 viewDir = normalize(viewPo - FragPo);
vec3 reflectDir = reflect(-lightDir ,outNormal);
float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
vec3 specular = specularStrength * spec * texture(specularTexture ,outTexCoord).rgb;
//光線衰弱
float LFDistance = length(lightPo - FragPo);
float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));
vec3 res = (ambient + diffuse + specular)*lightWeakPara;
FragColor = vec4(res,1.0);



