本帖将参考LearnOpenGL这一经典教程,使用Qt的原生环境完成教程中所提的所有流程,并尽量和原教程保持一致,如有错误,欢迎评论!
为了方便大家参考,我将项目分享至了gitee,并实时进行更行:Qt实现OpenGL的经典教程: (gitee.com)
Qt版本:6.7
操作系统:Windows10
环境光和漫反射
基础代码请参考我的gitee或者上一篇专栏中的内容。我们在此基础上对代码进行修改。
我们分别设置灯对象和用于显示光照的物体对象的shader和vao:
private:
QOpenGLShaderProgram *_shaderProgram;
QOpenGLShaderProgram *_shaderProgram1;
QOpenGLTexture *_texture;
QOpenGLTexture *_texture1;
QOpenGLVertexArrayObject *_vao;
QOpenGLVertexArrayObject *_vao1;
QOpenGLBuffer *_vbo;
QOpenGLBuffer *_ebo;
在物体对象的顶点着色器中,我们将顶点的世界坐标位置和法线向量传给片段着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
在片段着色器中,我们依靠这两个向量实现环境光和漫反射:
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main()
{
// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
环境光比较简单,通过加和灯光颜色即可,漫反射对光线照射与法向量夹角进行计算,用其内积作为评估标准,光线照射越垂直,则内积越大,反之越小,以此来反应漫反射的强弱
镜面高光
镜面高光需要考虑到摄像机的位置,当观察者的视线正对着反射光线时,那么此时就有最大的镜面高光,所以需要计算反射光线和摄像机位置的内积:
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main()
{
// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
为了避免负值的出现(当摄像机位置和反射光线的角度超过90度时),这里使用了一个max函数,同时使用幂函数来使反射出出现高光的效果。
PaintGL渲染
void MyOpenGLWidget::paintGL()
{
//update();
//由于继承了QOpenGLFunctions,可以直接使用OpenGL中的函数
// render
// ------
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓冲
// bind Texture
// glActiveTexture(GL_TEXTURE0);
// _texture->bind();
// glActiveTexture(GL_TEXTURE1);
// _texture1->bind();
//更新帧时间,由于定时器60fps,所以这里的_deltaTime为17上下
_deltaTime = (QTime::currentTime().msecsSinceStartOfDay() - _lastMescCount) /1000.0f;
_lastMescCount = QTime::currentTime().msecsSinceStartOfDay();
_shaderProgram->bind();
_shaderProgram->setUniformValue("objectColor", 1.0f, 0.5f, 0.31f);
_shaderProgram->setUniformValue("lightColor", 1.0f, 1.0f, 1.0f);
_shaderProgram->setUniformValue("lightPos", 1.2f, 1.0f, 2.0f);
_shaderProgram->setUniformValue("viewPos", _cameraPos);
QMatrix4x4 projection;
QMatrix4x4 view;
view.lookAt(_cameraPos, _cameraPos + _cameraFront, _cameraUp);
projection.perspective(45.0f,
float(this->width())/float(this->height()),
0.1f,
100.0f);
_shaderProgram->setUniformValue("view", view);
_shaderProgram->setUniformValue("projection", projection);
QMatrix4x4 model;
_shaderProgram->setUniformValue("model", model);
_vao->bind();
glDrawArrays(GL_TRIANGLES, 0, 36);
_shaderProgram1->bind();
_shaderProgram1->setUniformValue("view", view);
_shaderProgram1->setUniformValue("projection", projection);
model.translate(1.2f, 1.0f, 2.0f);
model.scale(0.2);
_shaderProgram1->setUniformValue("model", model);
_vao1->bind();
glDrawArrays(GL_TRIANGLES, 0, 36);
}
调用两个shader和vao分别渲染灯和光照物体,点击运行,便得到了如下结果:
代码已经分享至gitee,详细请看文章开头。