2. 绘制第一个三角形

任务目标

在Qt窗口中通过OpenGL着色器绘制一个彩色三角形,理解顶点数据如何经过顶点着色器、片段着色器最终显示到屏幕上的基本渲染管线流程。

环境依赖

  • Qt 5.12+(含Qt OpenGL模块)
  • 编译器:MSVC 2017+ 或 MinGW
  • 无需额外第三方库,使用Qt自带的QOpenGLFunctions

核心实现

创建自定义QOpenGLWidget子类,在initializeGL中编译着色器并生成顶点缓冲,在paintGL中执行绘制。顶点数据包含位置和颜色属性,通过着色器实现彩色渐变效果。

// trianglewidget.h
#ifndef TRIANGLEWIDGET_H
#define TRIANGLEWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>

class TriangleWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit TriangleWidget(QWidget *parent = nullptr);
    ~TriangleWidget();

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

private:
    QOpenGLShaderProgram *m_program = nullptr;
    QOpenGLBuffer m_vbo;
};

#endif // TRIANGLEWIDGET_H
// trianglewidget.cpp
#include "trianglewidget.h"
#include <QOpenGLShader>
#include <QVector>

TriangleWidget::TriangleWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
}

TriangleWidget::~TriangleWidget()
{
    makeCurrent();
    m_vbo.destroy();
    delete m_program;
    doneCurrent();
}

void TriangleWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

    // 顶点数据:位置(x,y) + 颜色(r,g,b)
    const GLfloat vertices[] = {
        // 位置       // 颜色
         0.0f,  0.5f,  1.0f, 0.0f, 0.0f,  // 顶点0 - 红色
        -0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  // 顶点1 - 绿色
         0.5f, -0.5f,  0.0f, 0.0f, 1.0f   // 顶点2 - 蓝色
    };

    // 编译着色器
    m_program = new QOpenGLShaderProgram(this);
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
        "#version 330 core\n"
        "layout (location = 0) in vec2 aPos;\n"
        "layout (location = 1) in vec3 aColor;\n"
        "out vec3 vColor;\n"
        "void main() {\n"
        "    gl_Position = vec4(aPos, 0.0, 1.0);\n"
        "    vColor = aColor;\n"
        "}\n");
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
        "#version 330 core\n"
        "in vec3 vColor;\n"
        "out vec4 FragColor;\n"
        "void main() {\n"
        "    FragColor = vec4(vColor, 1.0);\n"
        "}\n");
    m_program->link();

    // 创建并填充VBO
    m_vbo.create();
    m_vbo.bind();
    m_vbo.allocate(vertices, sizeof(vertices));
    m_vbo.release();
}

void TriangleWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT);

    m_program->bind();
    m_vbo.bind();

    // 设置顶点属性指针
    m_program->enableAttributeArray(0);
    m_program->setAttributeBuffer(0, GL_FLOAT, 0, 2, 5 * sizeof(GLfloat));

    m_program->enableAttributeArray(1);
    m_program->setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(GLfloat), 3, 5 * sizeof(GLfloat));

    // 绘制三角形
    glDrawArrays(GL_TRIANGLES, 0, 3);

    m_vbo.release();
    m_program->release();
}

void TriangleWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}
// main.cpp
#include <QApplication>
#include "trianglewidget.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    TriangleWidget widget;
    widget.resize(800, 600);
    widget.show();
    return app.exec();
}

运行与验证

  1. 在Qt Creator中新建Qt Widgets Application项目,将上述三个文件加入项目。
  2. 在.pro文件中添加:QT += openglwidgets
  3. 构建并运行程序。

通过条件:窗口显示深灰色背景,中央出现一个红、绿、蓝三色渐变填充的三角形。三个顶点颜色分别为纯红、纯绿、纯蓝,内部颜色平滑过渡,无闪烁或变形。

常见问题

  • 着色器编译失败:检查OpenGL版本是否支持330 core,若使用旧版驱动可改为150 core。
  • 三角形不显示:确认VBO数据布局与setAttributeBuffer中的步长参数一致(本例步长为5个GLfloat)。
  • 窗口全黑:检查initializeGL中是否调用了initializeOpenGLFunctions()。
顶点0 (0, 0.5) 红色 顶点1 (-0.5, -0.5) 绿色 顶点2 (0.5, -0.5) 蓝色 片段着色器插值 产生平滑渐变

上图展示了本案例的渲染结果结构:三个顶点分别指定红、绿、蓝纯色,片段着色器在三角形内部自动进行颜色插值,形成平滑的彩色渐变效果。这是OpenGL渲染管线中顶点数据→顶点着色器→光栅化→片段着色器流程的直观体现。