在现实世界里,每个物体会对光产生不同的反应。比如说钻石看起来闪闪发光,塑料看起来就回暗一些。表面比较光滑的反射光的时候会产生亮点,表面比较粗糙一点的,反射光会形成一个光斑。
在上一篇文章中,我们指定了一个物体和光的颜色,以及结合环境光和镜面强度分量,来定义物体的视觉输出。当描述一个物体的时候,我们可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对物体的颜色输出有着精细的控制了。现在,我们再添加反光度(Shininess)这个分量到上述的三个颜色中,这就有我们需要的所有材质属性了:
我们创建一个结构体来表示材质:
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shineness;
};
ambient 表示在环境光照下这个物体反射的颜色,通常这个是和物体相同的颜色,diffuse定义了在漫反射光照下物体的颜色(和环境光照一样),specular设置的是镜面光照对物体的颜色影响,最后,shineness影响镜面高光的散射/半径(这里可以举个例子解释:比如光照到比较粗糙的表面,可能散射的光圈半径就大一些,照到类似玻璃,钢铁这类物体的表面,形成的光斑直径就很小)。
这四个元素定义了一个物体的材质,通过它们,我们能模拟出很多现实世界中的材质。但是要想模拟出很真实的效果,这些参数需要很多次实验才能得到较为准确的结果。
让我们接着上一篇的代码,用材质结构体代替物体颜色来模拟物体的效果
顶点着色器如下:
#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;
//物体材质
uniform Material material;
in vec3 Normal;
in vec3 fragPos;
out vec4 gColor;
void main() {
//计算环境光照
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),也就是白光
得到的效果如图:
这里我们看到了效果,大致是对的,但是会不会很奇怪,为什么这么亮,在上一篇文章中,我们通过一个环境光照强度和镜面光照强度来控制这两个分量对最终颜色的影响,而这里我们去掉了这两个强度,所以相当于环境光照,漫反射光照和镜面光照对物体颜色的影响都是以1.0的强度处理的,所以这里特别亮。
这一步的代码对应的是Cube_material-01文件夹
那么接下来我们改进这一点,我们定义一个结构体Light:
struct Light {
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
我们通过改变Light的三个参数 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;
//物体材质
uniform Material material;
//光照的属性
uniform Light light;
in vec3 Normal;
in vec3 fragPos;
out vec4 gColor;
void main() {
//计算环境光照
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);
}
注意这几处的修改:
这里我们设置的光的颜色还是白色光 vec3(1.0,1.0,1.0);
这里我们传入Light结构体,对这几种光照强度进行设置:
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);
效果如图:
这样看上去是不是比较接近真实效果了。
这一步对的代码对应Cube_material-02这个文件夹
彩蛋
现实世界中,环境光可能不只是单一的颜色,现在我们做一个变动,根据时间来改变环境光的颜色,看看有什么不一样的效果。
代码如下:
//构建一个随时间改变光照颜色
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);
我们这里传入随时间改变的颜色进去,然后乘到环境光和漫射光的参数上。最终效果如下:
这里通过改变环境光,影响物体最终的颜色输出,是不是类似呼吸灯的效果?
这一步的代码对应文件夹Cube_material-03。