现实中,不同的物体,它们对光产生的反应是不一样的,也就是说当光线照射在物体上,它们反射的颜色不一样,亮度也不一样,这是由物体本身的材质决定的。木头、石头、金属对同一种光线产生的反应当然是不一样的,金属反光度会强很多,石头次之,木头最弱。
一. 物体材质
所以我们需要给物体定义一个材质颜色属性。物体材质定义成如下几个部分:
- 环境光照(ambient)
- 漫反射光照(diffuse)
- 镜面光照(specular)
- 反光度(shininess)
在glsl中这样定义:
// 物体材质struct
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material; // 使用Material类型,定义一个材质uniform
#version 300 es
precision highp float;
in vec3 v_normal;
in vec3 v_pos;
out vec4 FragColor;
uniform vec3 u_lightColor; // 光照颜色
uniform vec3 u_lightDirection; // 光照方向
uniform vec3 viewPos; // 摄像机位置
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
void main() {
// 环境光照
vec3 ambient = material.ambient * u_lightColor;
// 漫反射
vec3 lightDirectionReverse = normalize(-u_lightDirection);
vec3 normal = normalize(v_normal);
float diffuseStrength = max(dot(normal, lightDirectionReverse), 0.0);
vec3 diffuse = u_lightColor * (diffuseStrength * material.diffuse);
// 镜面光照
vec3 viewDir = normalize(viewPos - v_pos);
vec3 reflectDir = reflect(u_lightDirection, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = u_lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
设置属性材质,向着色器传递数据:
shader.setFloat3("material.ambient", 1, 0.5, 0.31);
shader.setFloat3("material.diffuse", 1, 0.5, 0.31);
shader.setFloat3("material.specular", 0.5, 0.5, 0.5);
shader.setFloat("material.shininess", 32);
运行程序,你应该能看见如下场景。
二.光的材质
虽然我们添加了物体材质,在着色器中添加了Material Struct, 但是最后的结果有点过于亮?物体过亮的原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都会去全力反射,假设光的颜色是vec3(1.0),那么之前我们计算不同分量的光如下:
vec3 ambient = vec3(1.0) * material.ambient;
vec3 diffuse = vec3(1.0) * (diff * material.diffuse);
vec3 specular = vec3(1.0) * (spec * material.specular);
其实光源对环境光、漫反射和镜面反射光的分量也具有着不同的强度的。所以我们需要为光也定义一个属性,也就是光的材质。以环境光为例,环境光一般会对物体的颜色产生很小的影响,我们希望其计算如下:
vec3 ambient = vec3(0.1) * material.ambient; // 使用vec3(0.1)而不是vec3(1.0), 否则过亮
可以使用同样的方式定义光的材质:
struct Light {
vec3 direction; // 如果需要点光源,可以设置成vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
给着色器传递light数据:
shader.setFloat3("light.ambient", 0.2, 0.2, 0.2);
shader.setFloat3("light.diffuse", 0.5, 0.5, 0.5); // 将光照调暗了一些以搭配场景
shader.setFloat3("light.specular", 1.0, 1.0, 1.0);
shader.setFloat3("light.direction", 1.0, -0.5, -1.0);
如果你的代码正确,应该能看到如下效果: