1. 光照和反射
要知道看到的物體的顏色實際上是物體反射的光的顏色曹阔,物體吸收了部分頻率的光玷犹,將不能吸收的光進行了反射儡循,從而我們看到了對應(yīng)物體呈現(xiàn)的顏色舶吗。
光照對于構(gòu)建一個三維圖形有著很大的影響,所以首先一起來討論一下基本的光照類型以及反射類型择膝。
1.1 光照類型
光照類型有很多誓琼,根據(jù)光源的不同以及發(fā)射出來的光線的特性,有三種最常用的:平行光源肴捉,點光源腹侣,環(huán)境光源
1.1.1 平行光源
平行光源光線是平行的,太陽光可以認為是平行光源齿穗。其實將一個點光源放在無限遠處筐带,那么最后我們所看到的光基本就是處于平行的,所以平行光源是點光源的極限狀態(tài)缤灵,同一表面上點對光的處理是一樣的(反射角度一樣)
1.1.2 點光源
點光源的特點就是光源是點狀伦籍,發(fā)射出來的光線角度是不一致的,同一表面上每個點對光線的處理會不同腮出。
1.1.3 環(huán)境光源
環(huán)境光源是墻面反射光線以后照射到物體表面的光帖鸦,環(huán)境光對于物體所有表面每一個點增加的強度可以認為是一致的。
1.2 反射類型
1.2.1 漫反射
漫反射是針對平行光源和點光源而言胚嘲,特點就是將物體默認為是類似鏡面的物體作儿,所有的光會以固定的角度反射回去。漫反射是一種理想的反射條件馋劈,忽略了物體本身材質(zhì)的影響(例如巖石攻锰,紙張會是斑駁的晾嘶,現(xiàn)實中沒有完全的鏡面,反射角度都不是固定的)
對于漫反射而言娶吞,我們看到的物體所呈現(xiàn)的顏色垒迂,其實就是:
漫反射顏色 = 入射光顏色 * 物體表面基底色 * cosx
其中的cosx是指入射光與物體表面的夾角。
1.2.2 環(huán)境反射
環(huán)境反射是針對環(huán)境光源而言妒蛇,可以認為光線均勻的鋪在物體表面机断,物體反射出的光線無論什么角度強度都是一樣的,其計算公式為:
環(huán)境反射顏色 = 入射光顏色 * 物體表面基底色
PS:于是最后物體反射的顏色(反射的顏色也就是我們看到的顏色)公式:物體反射顏色 = 漫反射顏色 + 環(huán)境反射顏色
2. 光照計算
于是在在WebGL中绣夺,根據(jù)光照類型以及對應(yīng)光照類型的反射特性來計算對應(yīng)物體的光照效果
2.1 平行光計算
平行光的反射類型是漫反射吏奸,所以根據(jù)漫反射公式,如果要計算物體表面顏色陶耍,那么需要指定到三個數(shù)據(jù):入射光顏色奋蔚,物體表面基底色,入射光角度烈钞。
入射光顏色我們和物體表面基底色在我們創(chuàng)建光源和物體表面的時候就可以進行指定泊碑,于是主要需要計算的部分就是入射光角度。
平行光的入射光角度是光源和物體表面的夾角棵磷,要根據(jù)入射光方向和物體表面朝向(法線方向)計算,其實并不容易晋涣。不過所幸可以通過矢量的點積的方式來計算出對應(yīng)入射角的余弦值(自己線性代數(shù)基本上忘了差不多了仪媒,補習(xí)中,不能很好說明為什么矢量的點積可以計算出這個結(jié)果)谢鹊。
于是說了那么多就變成了一下幾點:
- 獲取到表面的法線方向
- 光線方向和法線方向計算點積
也就是最后的計算公式就是
平行光漫反射 = 入射光顏色 * 物體表面基底色 * dot( 光線方向 * 法線方向)
以正方體且法線方向就是x,y,z軸的正負方向為例算吩,那么著色器中需要做的就是將我們的計算公式:
// VertexShader中增加
void main () {
...
attribute vec4 a_Normal; // 法向量
uniform vec3 u_LightDIrection; // 光線方向(歸一化)
uniform vec3 u_LightColor; // 光線顏色
// 法向量歸一化
vec3 normal = normalize(vec3(a_Normal))
// 點積計算
float nDotL = max(dot(u_LightDirection, normal), 0.0);
// 顏色計算
vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL
v_Color = vec4(diffuse, a_Color.a);
}
需要注意的是這里的在進行點積計算的時候,法線和光線方向一定都要首先經(jīng)過歸一化佃扼,變成單位矢量后進行計算偎巢,所以u_LightDirection
也要進行歸一化處理
2.2 環(huán)境光的疊加
環(huán)境光之前說是均勻的,所以環(huán)境光只需要入射光顏色和物體表面顏色進行確定兼耀,所以如果疊加上環(huán)境光压昼,反射光的計算公式就變?yōu)椋?br>
漫反射 = 入射光顏色 * 物體表面基底色 * dot( 光線方向 * 法線方向)+ 環(huán)境入射光顏色 * 物體表面基底色
所以頂點著色器中繼續(xù)增加環(huán)境光只需要
...
uniform vec3 u_AmbientLight;
vec3 ambient = u_AmbientLight * a_Color.rgb;
v_Color = vec4(diffuse + ambient, a_Color.a);
...
2.3 點光源點處理
點光源其實原理和平行光源類似,只是由于點光源的每個點的反射角度不同瘤运,所以不設(shè)定一個統(tǒng)一的光線方向窍霞,需要將平行光的光源方向變量u_LightDirection
替換為,光源坐標(biāo)和表面頂點位置的矢量計算結(jié)果
// VertexShader中增加
void main () {
...
uniform vec3 u_LightPosition; // 光源位置
...
// 計算光線方向
vec3 lightDirection = normalize(u_LightPosition - vec3(a_Position))
// 將u_LightDirection替換為lightDirection進行點積計算
float nDotL = max(dot(lightDirection, normal), 0.0);
}
2.4 片元著色器內(nèi)插隊光照計算的影響
剛才都是將計算放在頂點著色器內(nèi)進行拯坟,但是由于片元著色器的內(nèi)插過程影響但金,所以如果在頂點著色器內(nèi)渲染,內(nèi)插過程會出現(xiàn)偏差失真郁季,于是冷溃,要將在頂點著色器中渲染的內(nèi)容放到片元著色器中通過頂點和法向量的內(nèi)插值進行計算钱磅。
// VertexShader中計算光線方向和法線方向
void main () {
...
varying vec3 v_Position;
varying vec3 v_Normal;
varying vec4 v_Color;
// 計算光線方向
v_Position = a_Position;
v_Normal = normalize(a_Normal);
v_Color = a_Color;
}
// FragmentShader色器
void main () {
...
varying vec3 v_Position;
varying vec3 v_Normal;
varying vec4 v_Color;
uniform vec3 u_LightPosition; // 光源位置
uniform vec3 u_LightColor; // 光線顏色
// 法向量歸一化
vec3 normal = normalize(v_Normal)
// 計算光線方向
vec3 lightDirection = normalize(u_LightPosition - v_Position)
// 將u_LightDirection替換為lightDirection進行點積計算
float nDotL = max(dot(lightDirection, normal), 0.0);
// 顏色計算
vec3 diffuse = u_LightColor * vec3(v_Color) * nDotL
gl_FragColor = vec4(diffuse, v_Color.a);
}
3. 總結(jié)
總的來說要計算光照對物體的影響,需要獲取到光線方向(光源位置)和法線方向似枕,通過物體漫反射光計算公式進行計算
這是基本上WebGL入門學(xué)習(xí)筆記的最后一期盖淡,雖然還有很多學(xué)習(xí)到的其他特性并沒有記錄,但是在接下來會用各種具體實現(xiàn)的事例的方式來解決具體問題
4. 參考
《WebGL編程指南》