4 - 光照贴图
实际上,每个物体可以 同时拥有多种材质。例如一辆汽车,它同时有金属般光泽的外壳,也有比较粗糙的轮胎。接下来引入 漫反射贴图(Map) 和 镜面光贴图,允许我们更精确的控制这两个分量。
漫反射贴图
介绍
漫反射贴图(Diffuse Map),它是一个表现了物体所有的漫反射颜色的纹理图像。接下来尝试将这张漫反射贴图引入:
采样
先修改一下之前定义的Material结构体,将环境光颜色去掉(因为大多数情况下,环境光颜色和漫反射颜色相同),然后将漫反射颜色类型变成sampler2D
:
// 材质结构体
struct Material
{
sampler2D diffuse;
vec3 specular;
float shininess;
};
// 改一下两个量的计算方式
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
vec3 diffuse = texture(material.diffuse, TexCoords).rgb * (light.diffuse / (r * r) ) * max(0.0, dot(norm, lightDir));
然后像之前那样,引入纹理:
修改顶点数据:在这里。
解析顶点坐标属性:
// 解析纹理坐标属性 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2);
加载纹理:
// 创建并绑定纹理 unsigned int diffuseTexture; glGenTextures(1, &diffuseTexture); glBindTexture(GL_TEXTURE_2D, diffuseTexture); // 设定环绕/过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加载纹理图片 stbi_set_flip_vertically_on_load(true); int texWidth, texHeight, texNrChannels; unsigned char* data = stbi_load("./images/container2.png", &texWidth, &texHeight, &texNrChannels, 0); if (data) { // 生成纹理和Mipmap glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { std::cout << "ERROR: Failed to load texture" << std::endl; } // 释放内存 stbi_image_free(data);
告诉sampler2D用哪个纹理:
cubeShader.setInt("material.diffuse", 0);
最后结果如下(为了展示成果,暂时去掉了光的衰减):
镜面光贴图
从上边可以发现,物体的边框是钢制的,应该有更强的镜面光,同时木头部分给的镜面光也太过高了。因此要引入镜面光贴图,更加灵活的控制物体不同部分的镜面光分量。
介绍
如图,镜面光贴图是 黑白的,用来定义物体每部分的镜面光强度:
在片段着色器中,接下来会取样对应的颜色值并将它乘以光源的镜面强度,一个像素越白,物体的镜面光分量越亮。
制作这种贴图可以用PhotoShop
或Gimp
之类的工具,将漫反射纹理适当修改即可。
采样
和漫反射贴图同理,但不要忘了将不同纹理贴图绑定到不同的GL_TEXTURE
上:
cubeShader.setInt("material.diffuse", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseTexture);
cubeShader.setInt("material.specular", 1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specTexture);
最后结果如下(为了展示成果,暂时去掉了光的衰减):
可以看到,现在只有箱子的金属边框有高光了。
其他贴图
除了上边的贴图,还有 法线/凹凸贴图(Normal/Bump Map),反射贴图(Reflection Map)和自发光贴图(Emission Map)等贴图,增添物体细节。
自发光/放射光贴图
添加一个叫做放射光贴图(Emission Map)的东西,它是一个储存了每个片段的发光值(Emission Value)的贴图。发光值是一个包含(假设)光源的物体发光(Emit)时可能显现的颜色,这样的话物体就能够忽略光照条件进行发光(Glow)。
游戏中某个物体在发光的时候,你通常看到的就是放射光贴图:
接下来尝试将下图纹理作为自发光贴图添加到箱子上,产生这些字母都在发光的效果:
(同理,加个自发光项即可)
本文代码详见这里。
参考资料
- 光照贴图 - LearnOpenGL CN (learnopengl-cn.github.io)