OpenGL固定管線中默認(rèn)的光照類似于將三種光照分量組合起來:環(huán)境光+漫反射+鏡面反射。
其中漫反射之前就說過了。環(huán)境光和光源之于頂點(diǎn)的入射角度無關(guān),僅僅與物體本身的反射系數(shù)相關(guān)。本身也非常好理解,給出公式如下

Ambient公式
鏡面反射模型如下圖所示

怎么理解這個(gè)模型呢。鏡面反射模型中,最終我們看到的光照強(qiáng)度是光源向量與頂點(diǎn)法線的反射向量的點(diǎn)積。想象一下拿著光束照鏡子,與光束的反射角越大,則越不刺眼,就能理解這個(gè)模型了。模型的公式為

鏡面反射公式
公式本身是很好理解的,其中系數(shù)f為鏡面反射系數(shù)。類似物體的光滑程度吧,相同角度的光源照射在石塊上的反射光強(qiáng)度和玻璃上的反射光強(qiáng)度是不同的。這里就通過這個(gè)鏡面反射系數(shù)f來決定。
這個(gè)模型稱為Phong反射模型。最終公式如下:

有了公式之后剩下的就是代碼實(shí)現(xiàn)了。這里給出關(guān)鍵的shader代碼——
先上vertex shader的
attribute vec3 vertPosition;
attribute vec3 vertNormal;
varying vec3 lightIntensity;
struct LightInfo {
vec3 Position; // Light position in eye - coordinate
vec3 La; // Ambient light intensity
vec3 Ld; // Diffuse light intensity
vec3 Ls; // Sepcular light intesity
};
uniform LightInfo light;
struct MaterialInfo {
vec3 Ka; // Ambient reflectivity
vec3 Kd; // Diffuse reflectivity
vec3 Ks; // Specular reflectivity
float Shininess;// Specular shininess factor
};
uniform MaterialInfo material;
uniform mat3 normMat;
uniform mat4 modelview;
uniform mat4 projection;
void main() {
vec3 tnorm = normalize(normMat * vertNormal);
vec3 eyecoord = vec3(modelview * vec4(vertPosition, 1.0));
vec3 s = normalize(light.Position - eyecoord);
vec3 v = normalize(-eyecoord.xyz);
vec3 r = reflect(-s, tnorm);
vec3 ambient = light.La * material.Ka;
float sDotN = max(dot(s, tnorm), 0.0);
vec3 diffuse = light.Ld * material.Kd * sDotN;
vec3 spec = vec3(0.0);
if (sDotN > 0.0)
spec = light.Ls * material.Ks * pow(max(dot(r, v), 0.0), material.Shininess);
lightIntensity = ambient + diffuse + spec;
gl_Position = projection * modelview * vec4(vertPosition, 1.0);
}
OpenGL ES shading 語法類似C的語法。結(jié)構(gòu)體定義是一樣的。首先定義了表示光源的結(jié)構(gòu)體LightInfo以及相關(guān)的光源坐標(biāo)、三種光的反射強(qiáng)度,然后是材質(zhì)結(jié)構(gòu)MaterialInfo以及相關(guān)三種光的材質(zhì)反射系數(shù)以及鏡面光反射系數(shù)Shininess,然后是變換矩陣。
其他的語句不過多解釋了,和之前的處理差不多。
vec3 v = normalize(-eyecoord.xyz);
這里取頂點(diǎn)坐標(biāo)的反向向量是因?yàn)樵谟^察坐標(biāo)下,要計(jì)算反射光和觀察點(diǎn)的角度,換句話來說,就是從頂點(diǎn)發(fā)射出去的光和從頂點(diǎn)到觀察點(diǎn)的向量之間的夾角的余弦值才是我們要的值。
接下來給出Fragment shader的代碼
precision mediump float;
varying vec3 lightIntensity;
void main() {
gl_FragColor = vec4(lightIntensity, 1.0);
}
這個(gè)代碼和OpenGL ES 光照模型(一)是一樣的。
猛戳代碼