【OpenGL】--- OpenGL中如何使用“颜色贴图”、“高光贴图”、“自发光贴图”

一、引言

在计算机图形学中,使用 OpenGL 和 QT 进行图形渲染是一项常见的任务。本博客将演示如何使用 QT 和 OpenGL 创建一个带有颜色贴图、高光贴图和自发光贴图的立体 Box。

二、着色器文件

  • 创建顶点着色器文件 .vert。传递顶点属性和纹理坐标。
  • 创建片段着色器文件 .frag。实现颜色贴图、高光贴图和自发光贴图的效果。
vertex_shader.vert:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 aNormal;

out vec2 TexCoord;
out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main() {
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = mat3(transpose(inverse(model))) * aNormal;
    TexCoord = aTexCoord;
}
fragment_shader.frag:
#version 330 core
out vec4 FragColor;

in vec2 TexCoord;
in vec3 FragPos;
in vec3 Normal;

uniform sampler2D texture1;
uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;

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 = texture(texture1, TexCoord) * vec4(result, 1.0);
}

三、绘制立体 Box

  • 在主渲染循环中绘制立体 Box。
  • 使用顶点数组定义立体 Box 的几何形状。
  • 创建 Vertex Buffer Objects (VBOs) 和 Vertex Array Objects (VAOs)。
  • 使用 QT 的 QImage 加载颜色、高光和自发光贴图。
  • 设置光照、投影等参数。
myopenglwidget.h:
// myopenglwidget.h

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QTime>

class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    MyOpenGLWidget(QWidget *parent = nullptr);

protected:
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;

private slots:
    void on_timeout();

private:
    QTime m_time;
    QTimer m_timer;
    QOpenGLTexture *texture;
    QOpenGLShaderProgram shaderProgram;

    GLuint VAO, VBO;

    // Projection matrix
    QMatrix4x4 projection;
    // Model matrix
    QMatrix4x4 model;
    // View matrix
    QMatrix4x4 view;

    QVector3D viewInitPos = QVector3D(0.0f, 0.0f, 3.0f);

    struct Vertex {
        QVector3D position;
        QVector2D textureCoords;
        QVector3D normal;
    };

    QVector<Vertex> vertices;

    void setupVertices();
};

#endif // MYOPENGLWIDGET_H
myopenglwidget.cpp: 
// myopenglwidget.cpp

#include "myopenglwidget.h"
#include <QImage>
#include <QFile>
#include <QTextStream>

MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(on_timeout()));
    m_timer.start(16);  // 60 FPS
    m_time.start();
    m_camera.Position = viewInitPos;
    setFocusPolicy(Qt::StrongFocus);

    setupVertices();
}

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
    glEnableVertexAttribArray(0);
    // Texture attribute
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, textureCoords));
    glEnableVertexAttribArray(1);
    // Normal attribute
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
    glEnableVertexAttribArray(2);

    // Load and create a texture
    texture = new QOpenGLTexture(QImage(":/path/to/your/texture/image.jpg").mirrored());

    // Load shaders from file
    QFile vertexShaderFile(":/shaders/vertex_shader.vert");
    QFile fragmentShaderFile(":/shaders/fragment_shader.frag");

    if (!vertexShaderFile.open(QIODevice::ReadOnly | QIODevice::Text) ||
        !fragmentShaderFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "Failed to open shader files.";
        return;
    }

    QTextStream vertexShaderStream(&vertexShaderFile);
    QTextStream fragmentShaderStream(&fragmentShaderFile);

    QString vertexShaderSource = vertexShaderStream.readAll();
    QString fragmentShaderSource = fragmentShaderStream.readAll();

    const char *vertexShaderCode = vertexShaderSource.toLocal8Bit().constData();
    const char *fragmentShaderCode = fragmentShaderSource.toLocal8Bit().constData();

    // Compile shaders
    GLuint vertexShader, fragmentShader;
    GLint success;
    GLchar infoLog[512];

    // Vertex Shader
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderCode, nullptr);
    glCompileShader(vertexShader);

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, sizeof(infoLog), nullptr, infoLog);
        qWarning() << "Vertex shader compilation failed\n" << infoLog;
    }

    // Fragment Shader
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderCode, nullptr);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, sizeof(infoLog), nullptr, infoLog);
        qWarning() << "Fragment shader compilation failed\n" << infoLog;
    }

    // Shader Program
    shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderCode);
    shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderCode);
    shaderProgram.link();

    // Delete the shaders as they're linked into our program now and no longer necessary
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
}

void MyOpenGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
    projection.setToIdentity();
    projection.perspective(45.0f, static_cast<float>(w) / static_cast<float>(h), 0.1f, 100.0f);
}

void MyOpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shaderProgram.bind();
    shaderProgram.setUniformValue("model", model);
    shaderProgram.setUniformValue("view", view);
    shaderProgram.setUniformValue("projection", projection);

    // Bind texture
    glActiveTexture(GL_TEXTURE0);
    texture->bind();
    shaderProgram.setUniformValue("texture1", 0);

    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, vertices.size());  // Assuming vertices.size() vertices

    // Draw more objects...
}

void MyOpenGLWidget::setupVertices()
{
    // Set up vertex data and buffers
    vertices.clear();

    // Define vertices with positions, texture coordinates, and normals
    // ...

    // Add vertices to the vertices vector

    // Example:
    vertices.push_back({QVector3D(-0.5f, -0.5f, -0.5f), QVector2D(0.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f)});
    vertices.push_back({QVector3D(0.5f, -0.5f, -0.5f), QVector2D(1.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f)});
    vertices.push_back({QVector3D(0.5f, 0.5f, -0.5f), QVector2D(1.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f)});
    // ... Add more vertices for other objects

    // ...
}

四、总结

在本教程中,我们将学习如何使用Qt和OpenGL创建一个具有颜色贴图、高光贴图和自发光贴图效果的立体盒子。以下是整个制作流程的详细总结:

首先,我们需要创建一个Qt Widgets应用程序项目,并添加OpenGL模块。接着,定义一个自定义的OpenGL小部件类,继承自QOpenGLWidget,例如MyOpenGLWidget,该类将用于渲染OpenGL场景。

为了在立体盒子上应用纹理,我们需要加载颜色、高光、自发光贴图的图片文件。Qt的QOpenGLTexture类可用于方便地加载和管理这些纹理。

接下来,准备顶点着色器和片段着色器的外部文件(.vert和.frag文件),并使用QFileQTextStream加载着色器源代码。

在着色器文件中,我们编写顶点和片段着色器代码,实现光照、纹理贴图等效果。这些着色器将负责处理立体盒子的渲染过程。

在OpenGL初始化代码中,我们使用initializeGL函数进行OpenGL的初始化工作,包括生成顶点数组对象(VAO)和顶点缓冲对象(VBO)等。

在OpenGL渲染代码中,我们使用paintGL函数编写渲染代码,设置模型矩阵、视图矩阵、投影矩阵等,绑定纹理,绘制立体盒子。

为了确保在窗口大小调整时保持正确的比例,我们在resizeGL函数中设置视口大小和投影矩阵。通过QOpenGLShaderProgram类,我们连接顶点着色器和片段着色器,创建一个OpenGL着色器程序。

最后,在主窗口或主窗体中创建MyOpenGLWidget实例,将其设置为中心窗口,运行程序。通过正确设置文件路径和名称,你将能够在Qt环境下运行这个简单但功能强大的OpenGL程序,呈现一个具有颜色贴图、高光贴图和自发光贴图效果的立体盒子。这个技术博客提供了一个完整的制作流程,帮助你构建更复杂的OpenGL场景。

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值