OpenGl实战笔记(3)基于qt5.15.2+mingw64+opengl实现光照变化效果

一、作用原理

1、作用 :

增强真实感:通过明暗变化模拟立体和材质。

体现空间感:让物体不再"平面",更具三维效果。

表现材质特性:模拟金属、塑料等不同表面的反光。

实现动态光影:支持移动光源、阴影、高光等效果。

2、原理

OpenGL 光照效果基于 光与表面交互的数学模型,常见为 冯氏模型(Phong Model),包括:

环境光 Ambient 整体照亮,无方向,用于模拟环境反射光。

漫反射 Diffuse 与法线夹角有关,光越垂直表面越亮。

高光 Specular 反射光靠近视角方向时产生亮点,表现光滑表面反光。

3、常见光源类型

平行光:有方向,无位置(如太阳)。

点光源:有位置,向各方向发光(如灯泡)。

聚光灯:有方向和角度限制(如手电筒)。

二、实现效果

三、参考代码

cpp 复制代码
#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();

    // 创建并填充 VBO
    vbo.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);

欢迎关注我,一起交流!

相关推荐
Zhao-Jichao3 小时前
AI Prompting for Everyone 课程笔记 - P21 总结 Conclusion
人工智能·笔记
欢欢李3 小时前
【LTE L3 笔记 05】LTE同步信号
笔记·无线通信·物理层·lte·空口协议
BroNekoX5 小时前
我将基于 QtQuick 框架开发的音乐播放器性能做到顶级
qt
La Pulga5 小时前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
蒸蒸yyyyzwd5 小时前
秋招学习笔记 day106
笔记·学习
小李不想当小白6 小时前
WDG看门狗(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
欢欢李6 小时前
【LTE L3 笔记 04】LTE帧结构
笔记·无线通信·物理层·lte·空口协议
小小晓.8 小时前
Qt 对话框、QLineEdit 与布局管理实战详解
开发语言·qt
Zhao-Jichao9 小时前
AI Prompting for Everyone 课程笔记 - P20 实验与期末项目 Lab and Final Project
前端·人工智能·笔记
Z21-X9 小时前
云原生运维实战笔记:企业网站综合上云部署与云运维服务全景解析
运维·笔记·学习·云原生