在現(xiàn)實世界里,每個物體會對光產(chǎn)生不同的反應(yīng)。比如說鉆石看起來閃閃發(fā)光,塑料看起來就回暗一些。表面比較光滑的反射光的時候會產(chǎn)生亮點,表面比較粗糙一點的,反射光會形成一個光斑。
在上一篇文章中,我們指定了一個物體和光的顏色,以及結(jié)合環(huán)境光和鏡面強度分量,來定義物體的視覺輸出。當描述一個物體的時候,我們可以用這三個分量來定義一個材質(zhì)顏色(Material Color):環(huán)境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和鏡面光照(Specular Lighting)。通過為每個分量指定一個顏色,我們就能夠?qū)ξ矬w的顏色輸出有著精細的控制了?,F(xiàn)在,我們再添加反光度(Shininess)這個分量到上述的三個顏色中,這就有我們需要的所有材質(zhì)屬性了:
我們創(chuàng)建一個結(jié)構(gòu)體來表示材質(zhì):
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shineness;
};
ambient 表示在環(huán)境光照下這個物體反射的顏色,通常這個是和物體相同的顏色,diffuse定義了在漫反射光照下物體的顏色(和環(huán)境光照一樣),specular設(shè)置的是鏡面光照對物體的顏色影響,最后,shineness影響鏡面高光的散射/半徑(這里可以舉個例子解釋:比如光照到比較粗糙的表面,可能散射的光圈半徑就大一些,照到類似玻璃,鋼鐵這類物體的表面,形成的光斑直徑就很小)。
這四個元素定義了一個物體的材質(zhì),通過它們,我們能模擬出很多現(xiàn)實世界中的材質(zhì)。但是要想模擬出很真實的效果,這些參數(shù)需要很多次實驗才能得到較為準確的結(jié)果。
讓我們接著上一篇的代碼,用材質(zhì)結(jié)構(gòu)體代替物體顏色來模擬物體的效果
頂點著色器如下:
#version 300 es
precision mediump float;
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shineness;
};
uniform vec3 lightColor;
//光源位置
uniform vec3 lightPos;
//觀察點的位置
uniform vec3 viewPos;
//物體材質(zhì)
uniform Material material;
in vec3 Normal;
in vec3 fragPos;
out vec4 gColor;
void main() {
//計算環(huán)境光照
vec3 ambient = lightColor * material.ambient;
//計算漫反射
vec3 norm = normalize(Normal);
vec3 lightDirection = normalize(lightPos - fragPos);
float diffu = dot(norm,lightDirection);
vec3 diffuse = lightColor * (diffu * material.diffuse);
//計算鏡面光照
//觀察向量
vec3 viewDirection = normalize(viewPos - fragPos);
//反射向量
vec3 reflectDirection = reflect(-lightDirection,norm);
float spec = pow(max(dot(viewDirection,reflectDirection), 0.0) , material.shineness);
vec3 specular = lightColor * (spec * material.specular);
//最終顏色(這里指定物體顏色為紅色)
vec3 result = ambient + diffuse + specular;
gColor = vec4(result,1.0);
}
這里我們假定lightColor為(1.0,1.0,1.0),也就是白光
得到的效果如圖:
這里我們看到了效果,大致是對的,但是會不會很奇怪,為什么這么亮,在上一篇文章中,我們通過一個環(huán)境光照強度和鏡面光照強度來控制這兩個分量對最終顏色的影響,而這里我們?nèi)サ袅诉@兩個強度,所以相當于環(huán)境光照,漫反射光照和鏡面光照對物體顏色的影響都是以1.0的強度處理的,所以這里特別亮。
這一步的代碼對應(yīng)的是Cube_material-01文件夾
那么接下來我們改進這一點,我們定義一個結(jié)構(gòu)體Light:
struct Light {
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
我們通過改變Light的三個參數(shù) ambient ,diffuse, specular來改變每種光照的強度,
修改后的頂點著色器:
precision mediump float;
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shineness;
};
struct Light {
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform vec3 lightColor;
//光源位置
uniform vec3 lightPos;
//觀察點的位置
uniform vec3 viewPos;
//物體材質(zhì)
uniform Material material;
//光照的屬性
uniform Light light;
in vec3 Normal;
in vec3 fragPos;
out vec4 gColor;
void main() {
//計算環(huán)境光照
vec3 ambient = light.ambient * material.ambient;
//計算漫反射
vec3 norm = normalize(Normal);
vec3 lightDirection = normalize(lightPos - fragPos);
float diffu = dot(norm,lightDirection);
vec3 diffuse = light.diffuse * (diffu * material.diffuse);
//計算鏡面光照
//觀察向量
vec3 viewDirection = normalize(viewPos - fragPos);
//反射向量
vec3 reflectDirection = reflect(-lightDirection,norm);
float spec = pow(max(dot(viewDirection,reflectDirection), 0.0) , material.shineness);
vec3 specular = light.specular * (spec * material.specular);
//最終顏色(這里指定物體顏色為紅色)
vec3 result = ambient + diffuse + specular;
gColor = vec4(result,1.0);
}
注意這幾處的修改:

這里我們設(shè)置的光的顏色還是白色光 vec3(1.0,1.0,1.0);
這里我們傳入Light結(jié)構(gòu)體,對這幾種光照強度進行設(shè)置:
glUniform3f(glGetUniformLocation(_esContext.program, "material.ambient"), 1.0, 0.5, 0.31);
glUniform3f(glGetUniformLocation(_esContext.program, "material.diffuse"), 1.0, 0.5, 0.31);
glUniform3f(glGetUniformLocation(_esContext.program, "material.specular"), 0.5, 0.5, 0.5);
glUniform1f(glGetUniformLocation(_esContext.program, "material.shineness"), 32.f);
glUniform3f(glGetUniformLocation(_esContext.program, "light.ambient"), 0.2 ,0.2, 0.2);
glUniform3f(glGetUniformLocation(_esContext.program, "light.diffuse"), 0.5, 0.5, 0.5);
glUniform3f(glGetUniformLocation(_esContext.program, "light.specular"), 1.0, 1.0, 1.0);
效果如圖:
這樣看上去是不是比較接近真實效果了。
這一步對的代碼對應(yīng)Cube_material-02這個文件夾
彩蛋
現(xiàn)實世界中,環(huán)境光可能不只是單一的顏色,現(xiàn)在我們做一個變動,根據(jù)時間來改變環(huán)境光的顏色,看看有什么不一樣的效果。
代碼如下:
//構(gòu)建一個隨時間改變光照顏色
float r = sin(self.elapsedTime * 1.2);
float g = sin(self.elapsedTime * 1.3);
float b = sin(self.elapsedTime * 1.7);
GLKVector3 lightColor = GLKVector3Make(r, g, b);
GLKVector3 lightAmbient = GLKVector3Make(0.2, 0.2, 0.2);
GLKVector3 lightDiffuse = GLKVector3Make(0.5, 0.5, 0.5);
lightAmbient = GLKVector3Multiply(lightColor, lightAmbient);
lightDiffuse = GLKVector3Multiply(lightColor, lightDiffuse);
glUniform3fv(glGetUniformLocation(_esContext.program, "light.ambient"), 1, lightAmbient.v);
glUniform3fv(glGetUniformLocation(_esContext.program, "light.diffuse"), 1, lightDiffuse.v);
glUniform3f(glGetUniformLocation(_esContext.program, "light.specular"), 1.0, 1.0, 1.0);
我們這里傳入隨時間改變的顏色進去,然后乘到環(huán)境光和漫射光的參數(shù)上。最終效果如下:

這里通過改變環(huán)境光,影響物體最終的顏色輸出,是不是類似呼吸燈的效果?
這一步的代碼對應(yīng)文件夾Cube_material-03。