OpenGl实战笔记(3)基于qt5.15.2+mingw64+opengl实现光照变化效果
一、作用原理
1、作用:
增强真实感:通过明暗变化模拟立体和材质。
体现空间感:让物体不再“平面”,更具三维效果。
表现材质特性:模拟金属、塑料等不同表面的反光。
实现动态光影:支持移动光源、阴影、高光等效果。
2、原理
OpenGL 光照效果基于 光与表面交互的数学模型,常见为 冯氏模型(Phong Model),包括:
环境光 Ambient 整体照亮,无方向,用于模拟环境反射光。
漫反射 Diffuse 与法线夹角有关,光越垂直表面越亮。
高光 Specular 反射光靠近视角方向时产生亮点,表现光滑表面反光。
3、常见光源类型
平行光:有方向,无位置(如太阳)。
点光源:有位置,向各方向发光(如灯泡)。
聚光灯:有方向和角度限制(如手电筒)。
二、实现效果
三、参考代码
#pragma once// 渲染模块接口定义(用于模块化设计)
#include "RenderModuleInterface.h"// Qt OpenGL 所需的类
#include <QOpenGLFunctions> // 提供所有 OpenGL ES 2.0 函数
#include <QOpenGLShaderProgram> // 管理顶点与片元着色器
#include <QOpenGLBuffer> // 管理 VBO(顶点缓冲对象)
#include <QOpenGLVertexArrayObject> // 管理 VAO(顶点数组对象)
#include <QObject> // 支持 Qt 的信号槽机制// 光照渲染类,继承自 QObject(用于信号槽)
// 同时实现渲染接口 RenderModuleInterface,并继承 OpenGL 函数支持
class LightingRenderer : public QObject,public RenderModuleInterface,protected QOpenGLFunctions
{
public:// 初始化 OpenGL 状态、着色器、顶点数据等void initialize() override;// 视口大小改变时调用,设置 OpenGL 视口void resize(int w, int h) override;// 渲染主函数,绘制一帧图像void render() override;public slots:// 每帧更新函数,用于驱动动画等逻辑void onFrameUpdate();private:QOpenGLShaderProgram shader; // 着色器程序对象(包含顶点 + 片元着色器)QOpenGLBuffer vbo; // 顶点缓冲对象,用于存储顶点数据QOpenGLVertexArrayObject vao; // 顶点数组对象,保存顶点属性配置int width = 0, height = 0; // 当前窗口宽高,用于设置视口bool isInitialized = false; // 标志位,确保只初始化一次float t = 0.0f; // 用于控制动画变化的时间参数(如光源移动等)
};
#include "LightingRenderer.h"
#include <QMatrix4x4>
#include <QVector3D>
#include <QtMath>// 顶点着色器:传递位置、法线,并计算世界坐标和变换后的法线
static const char* lightingVert = R"(
#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;out vec3 FragPos;
out vec3 Normal;uniform mat4 u_mvp; // 模型-视图-投影矩阵
uniform mat4 u_model; // 模型矩阵,用于变换法线和世界坐标void main()
{FragPos = vec3(u_model * vec4(position, 1.0)); // 计算世界坐标Normal = mat3(transpose(inverse(u_model))) * normal; // 法线矩阵(保持正确方向)gl_Position = u_mvp * vec4(position, 1.0); // 最终顶点位置
})";// 片元着色器:实现漫反射光照
static const char* lightingFrag = R"(
#version 330 core
in vec3 FragPos;
in vec3 Normal;
out vec4 FragColor;uniform vec3 lightDir; // 光照方向(单位向量)
uniform vec3 lightColor; // 光颜色
uniform vec3 objectColor; // 物体基础颜色void main()
{// 漫反射分量(dot 表示光与法线夹角余弦)float diff = max(dot(normalize(Normal), -lightDir), 0.0);vec3 result = diff * lightColor * objectColor;FragColor = vec4(result, 1.0); // 输出最终颜色
})";
void LightingRenderer::initialize()
{if (isInitialized) return; // 防止重复初始化initializeOpenGLFunctions(); // 初始化 OpenGL 函数表if (shader.isLinked()) return;// 三角形顶点(每个顶点包含位置 + 法线,共 6 个 float)GLfloat vertices[] = {// position // normal-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f};// 编译并链接着色器程序shader.addShaderFromSourceCode(QOpenGLShader::Vertex, lightingVert);shader.addShaderFromSourceCode(QOpenGLShader::Fragment, lightingFrag);shader.link();shader.bind(); // 绑定以设置属性// 创建 VAO(保存所有绑定状态)vao.create();vao.bind();// 创建并填充 VBOvbo.create();vbo.bind();vbo.allocate(vertices, sizeof(vertices));// 设置属性 location 0(位置)和 location 1(法线)shader.enableAttributeArray(0);shader.setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));shader.enableAttributeArray(1);shader.setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 6 * sizeof(float));// 解绑vao.release();vbo.release();shader.release();isInitialized = true;
}
void LightingRenderer::resize(int w, int h)
{glViewport(0, 0, w, h); // 设置 OpenGL 渲染区域width = w;height = h;
}
void LightingRenderer::render()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST); // 启用深度测试shader.bind();vao.bind();// 构造变换矩阵QMatrix4x4 model, view, proj;model.setToIdentity(); // 没有旋转缩放view.lookAt(QVector3D(0, 0, 2), QVector3D(0, 0, 0), QVector3D(0, 1, 0));proj.perspective(45.0f, float(width) / height, 0.1f, 10.0f);// 设置 uniform 变量QMatrix4x4 mvp = proj * view * model;shader.setUniformValue("u_mvp", mvp);shader.setUniformValue("u_model", model);// 动态光源方向(左右摆动)QVector3D lightDir = QVector3D(qSin(t), 0, -1).normalized();shader.setUniformValue("lightDir", lightDir);shader.setUniformValue("lightColor", QVector3D(1, 1, 1)); // 白光shader.setUniformValue("objectColor", QVector3D(1.0f, 0.5f, 0.3f)); // 橘色// 绘制三角形glDrawArrays(GL_TRIANGLES, 0, 3);vao.release();shader.release();
}
void LightingRenderer::onFrameUpdate()
{t += 0.03f; // 控制光源角度变化if (t > 2 * M_PI) t -= 2 * M_PI;
}
//使用
modules["光照效果"] = new LightingRenderer();
modules["光照效果"]->initialize();
if (auto* light = dynamic_cast<LightingRenderer*>(modules["光照效果"]))connect(this, &OpenGLSceneWidget::frameSwapped, light, &LightingRenderer::onFrameUpdate);
欢迎关注我,一起交流!