Linux 下的 OpenGL 之路(八):贴图和材质

发布时间 2023-07-27 10:53:11作者: 京山游侠

前言

按道理,材质和贴图是影响光照的。上一节,我只是简单地弄了一下光照,主要是因为偷懒,主要是想早一点看到 3D 模型的效果。如果要考虑到贴图和材质,需要额外做一点工作。我这里还是使用 Assimp 库载入了我之前收集的一个小木屋模型(是.obj格式的),然后使用 stb_image.h 这个库载入图片进行贴图。多的话不说,先展示效果:

这一节我就不贴代码了,主要是讲一些贴图、材质、光照和我自己的一些设计思路。

简单的光照原理

在现在的 OpenGL 版本中,光照都是写到 Shader 中的,固定管线已经过时。当然,我们需要把计算光照所需要的一些信息传递到 Shader Program 中。究竟有哪些数据是需要传递到 Shader Program 中的呢?我们这里按照简单的光照模型总结一下:

  1. 顶点的位置,这个不用多说,这是组成模型的基础;
  2. 顶点的法线向量。法线向量在光照的计算中非常重要。简单的光照可以理解为包含三个部分:环境光、漫反射光、镜面反射光。其中,漫反射光的强度和光源的方向和法线方向有关,我们一般会将光源方向和法线方向点乘,然后再和模型的漫反射颜色、灯光的颜色相乘。镜面反射光和光源方向、视线方向、法线方向都有关,一般是先计算视线方向和光源方向的中间向量,然后把这个中间向量和法线方向点乘,然后再做一个幂运算,然后和模型的镜面反射颜色、灯光颜色相乘。环境光和法线方向无关,等于模型的环境光颜色和场景的环境光颜色点乘。最后,将以上三个部分相加即可。(这里需要注意,所有的方向向量点乘之前,需要将其长度规范化为1。其次,如果是点光源,还要考虑距离衰减,我们这里暂时忽略。)根据以上原理,其伪代码基本如下:
in vec3 fNormal;
uniform vec3 ambientColor;
uniform vec3 ka;
uniform vec3 kd;
uniform vec3 ks;
uniform vec3 lightDirection;
uniform vec3 eyeDirection;
uniform float shiness;
ambient = ka * ambientColor;
diffuse = kd * dot(normalize(fNormal), normalize(lightDirection)) * lightColor;
speculuar = ks * pow(dot(normalize(fNormal), normalize((lightDirection + eyeDirection)/2)), shiness) * lightColor;
fColor = ambient + diffuse + specular;

当然,其中每一步都要判断是否大于0.0小于1.0。

  1. 根据以上简单光照的原理,我们的模型需要将模型固有的环境光颜色、漫反射颜色、镜面反射颜色、反射指数等数据传入 Shader Program。根据惯例,我们可以把它们简称ka、kd、ks。
  2. 根据以上简单光照的原理,我们的场景需要将灯光位置、灯光颜色、环境光颜色传入 Shader Program。
  3. 除了直接传入模型的颜色值,还可以使用纹理贴图,所以顶点需要纹理坐标,需要将纹理坐标传入 Shader Program。
  4. 纹理贴图需要绑定到纹理单元,然后在 Shader 中使用。根据惯例,我们可以把它们简称为 mapKa、mapKd、mapKs。如果使用纹理,其 Shader 的伪代码基本如下:
in vec3 fNormal;
in vec2 fTexCoords;
uniform vec3 ambientColor;
uniform vec3 ka;
uniform vec3 kd;
uniform vec3 ks;
uniform vec3 lightDirection;
uniform vec3 eyeDirection;
uniform float shiness;

uniform int hasMapKs;
uniform sampler2D mapKs;
uniform int hasMapKd;
uniform sampler2D mapKd;

ambient = ka * ambientColor;
if(hasMapKd){
    diffuse = texture(mapKd, fTexCoords) * dot(normalize(fNormal), normalize(lightDirection)) * lightColor;
}else{
    diffuse = kd * dot(normalize(fNormal), normalize(lightDirection)) * lightColor;
}
if(hasMapKs){
    speculuar = texture(mapKs, fTexCoords) * pow(dot(normalize(fNormal), normalize((lightDirection + eyeDirection)/2)), shiness) * lightColor;
}else{
    speculuar = ks * pow(dot(normalize(fNormal), normalize((lightDirection + eyeDirection)/2)), shiness) * lightColor;
}
fColor = ambient + diffuse + specular;

因为不是所有的模型都有贴图,所以在 Shader 里面判断一下,如果有贴图,就使用 mapKd 和 mapKs,否则,就直接使用 kd 和 ks 的值。

在这里,这些 ka、kd、ks、mapKd、mapKs、shiness 等,就构成了模型的材质信息。