前言:该系列教程主要参考自网站www.opengl-tutorial.org和learnopengl.com/,基于开源GUI框架imgui v1.61实现,imgui自带的例子里面直接集成了glfw+gl3w环境,本系列教程将gl3w换成了glew,glew具体环境配置可参考:OpenGL环境配置教程:VS2012 + GLEW + GLFW + GLM。
教程目录(持续更新中):
现代OpenGL教程(一):绘制三角形(ImGui+OpenGL3.3)
现代OpenGL教程(二):矩阵变换(ImGui+OpenGL3.3)
现代OpenGL教程(三):绘制彩色立方体(ImGui+OpenGL3.3)
现代OpenGL教程(四):立方体纹理贴图(ImGui+OpenGL3.3)
现代OpenGL教程(五):obj模型加载(ImGui+OpenGL3.3)
现代OpenGL教程(六):鼠标和键盘(ImGui+OpenGL3.3)
现代OpenGL教程(七):基础光照——颜色(ImGui+OpenGL3.3)
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)
现代OpenGL教程(九):基础光照——材质(ImGui+OpenGL3.3)
本节概要
本节教程在上一节(现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)的基础上,重构了部分片段着色器的代码,并模拟了多种材质。本节完整代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/opengl-material
运行效果如下:
往期回顾
在前一节的教程中,我们知道OpenGL如何表达颜色:
- 物体颜色
objColor
:吸收光线后,反射出来的光的颜色 - 物体颜色 × \times × 光源颜色 能够模拟反射现象
在上一节的教程中,我们知道了Phong光照模型的原理:
- 环境光分量:
ambient = lightColor
- 漫反射光分量:
diffuse = lightColor * diff
,其中,diff
为光线和法线夹角 θ \theta θ的余弦值 - 镜面光分量:
specular = lightColor * spec
,其中spec
为视线和反射光线夹角的余弦值的反光度次幂 - 片段的颜色:
fragColor = objColor * (ambient + diffuse + specular)
将片段颜色计算公式的括号展开,则有:
- 环境光颜色值:
ambientColor = objColor * ambient
- 漫反射光颜色值:
diffuseColor = objColor * diffuse
- 镜面光颜色值:
specularColor = objColor * specular
- 片段的颜色:
fragColor = ambientColor + diffuseColor + specularColor
设置物体的材质
为了更精细地描绘物体材质,我们不再使用单一的物体颜色objColor
来描述物体的材质,而是用4个材质向量来描述:
material.ambient
:设置了在环境光照下物体反射的颜色,通常就是物体的颜色material.diffuse
:设置了在漫反射光照下物体反射的颜色material.specular
:设置了在镜面光照下物体反射的颜色material.shininess
:其实就是反光度,但一般不同的材质反光度不一样,所以也把反光度作为材质的属性
在片段着色器中,将material定义成结构体,可以更方便地管理这4个材质向量:
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
片段颜色输出的公式变成:
- 环境光颜色值:
ambientColor = material.ambient * ambient
- 漫反射光颜色值:
diffuseColor = material.diffuse * diffuse
- 镜面光颜色值:
specularColor = material.specular * specular
- 片段的颜色:
fragColor = ambientColor + diffuseColor + specularColor
将ambient
,diffuse
和specular
展开,上述公式变成:
- 环境光颜色值:
ambientColor = material.ambient * lightColor
- 漫反射光颜色值:
diffuseColor = material.diffuse * lightColor * diff
- 镜面光颜色值:
specularColor = material.specular * lightColor * spec
- 片段的颜色:
fragColor = ambientColor + diffuseColor + specularColor
转换成片段着色器代码:
void main()
{
// 1.ambient
vec3 ambientColor = lightColor * material.ambient;
// 2.diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuseColor = lightColor * diff * material.diffuse;
// 3.specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specularColor = lightColor * spec * material.specular;
vec3 result = ambientColor + diffuseColor + specularColor;
FragColor = vec4(result, 1.0);
}
网站 http://www.barradeau.com/nicoptere/dump/materials.html 列出了常用的一些材质属性,根据这些属性绘制出来的材质效果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/21ee03bc259e1c6028001a617a8b82aa.gif)
本节完整代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/opengl-material
参考资料:
材质 - LearnOpenGL CN