Qt与OpenGL编程 - IBO与线框模式


1. 索引缓冲对象(IBO)

索引缓冲对象(Element Buffer Object, EBO,也叫Index Buffer Object, IBO)。假设我们要绘制得不是三角形,而是四边形,那么我们要写生成得节点组合可能是这样的:

GLfloat vertices[] = {
    // 第一个三角形
    0.5f, 0.5f, 0.0f,   // 右上角
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, 0.5f, 0.0f,  // 左上角
    // 第二个三角形
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, -0.5f, 0.0f, // 左下角
    -0.5f, 0.5f, 0.0f   // 左上角
};

可以看到我们要生成得四边形只有4个顶点,而我们却生成6个顶点。如果一个模型有很对顶点,为了避免不必要得开销,索引我们引入了索引缓冲对象。IBO是一个索引顶点得缓冲对象,OpenGL调用这些顶点的索引来决定该绘制哪些顶点。我们只需要定义顶点和绘制矩形所需的索引:

GLfloat vertices[] = {
    0.5f, 0.5f, 0.0f,   // 右上角
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, -0.5f, 0.0f, // 左下角
    -0.5f, 0.5f, 0.0f   // 左上角
};

GLuint indices[] = { // 注意索引从0开始! 
    0, 1, 3, // 第一个三角形
    1, 2, 3  // 第二个三角形
};

(1) 创建IBO

同VBO使用函数 glGenBuffers 创建一个IBO对象

// 创建IBO
GLuint m_nIBOId;
glGenBuffers(1, &m_nIBOId);

(2)设置IBO的数据

使用函数 glBindBuffer 绑定IBO, 函数 glBufferData 设置IBO的数据

// 初始化IBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

(3)绘制

使用函数 glDrawElements 绘制

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
// 绘制
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

函数 glDrawElements 的原型如下:
glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)

  • 参数 mode : 绘制模式, GL_TRIANGLES 为三角形。
  • 参数 count : 绘制的顶点数目。
  • 参数 type : 数据的类型,这里索引数据存储的为 GL_UNSIGNED_INT
  • 参数 indices : 表示偏移。

下图为绘制的四变型效果:
Qt与OpenGL编程 - IBO与线框模式

2. 线框模式

函数 glPolygonMode 表示OpenGL使用何种方式绘制图元。

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

表示使用线框模式,效果如下图说是
Qt与OpenGL编程 - IBO与线框模式
如果要恢复成填充模式,可以使用如下代码:

glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

完整代码如下:
头文件:

#ifndef OPENGLRENDERWIDGET_H
#define OPENGLRENDERWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions_2_0>
#include <QOpenGLFunctions_3_3_Core>

class OpenglRenderWidget : public QOpenGLWidget, public QOpenGLFunctions_2_0
{
    Q_OBJECT
public:
    struct VertexAttributeData
    {
        // Postion
        float pos[3];
        float color[3];
    };

public:
    OpenglRenderWidget(QWidget* parent = nullptr);
    ~OpenglRenderWidget();

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

private:
    bool initShaderProgram(void);
    void createVertexAttributeData(VertexAttributeData* pVetAttr);

    GLuint m_shaderProgramId;
    QOpenGLShaderProgram* m_pShaderProgram = nullptr;
    QOpenGLShader* m_pVertexShader = nullptr;
    QOpenGLShader* m_pFragmentShader = nullptr;

    GLuint m_nVBOId;
    GLuint m_nIBOId;

    // Attribute Location
    GLint m_nPosAttrLocationId;
    GLint m_nColorAttrLocationId;
};

#endif

cpp文件

#include "OpenglRenderWidget.h"
#include <QDebug>

OpenglRenderWidget::OpenglRenderWidget(QWidget* parent)
    :QOpenGLWidget(parent)
{

}

OpenglRenderWidget::~OpenglRenderWidget()
{

}

void OpenglRenderWidget::initializeGL()
{
    this->initializeOpenGLFunctions();

    // 初始化GPU程序
    bool result = initShaderProgram();
    if (!result)
        return;

    m_shaderProgramId = m_pShaderProgram->programId();
    // 获取位置和颜色的locationID
    m_nPosAttrLocationId = glGetAttribLocation(m_shaderProgramId, "pos");
    m_nColorAttrLocationId = glGetAttribLocation(m_shaderProgramId, "color");

    // 创建顶点属性数据
    VertexAttributeData vAttrData[4];
    createVertexAttributeData(vAttrData);
    // 创建IBO数据
    GLuint indices[] = {0, 1, 3, 1, 2, 3};

    // 创建VBO
    glGenBuffers(1, &m_nVBOId);
    // 创建IBO
    glGenBuffers(1, &m_nIBOId);

    // 初始化VBO
    glBindBuffer(GL_ARRAY_BUFFER, m_nVBOId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vAttrData), vAttrData, GL_STATIC_DRAW);

    // 初始化IBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // 设置顶点信息属性指针
    glVertexAttribPointer(m_nPosAttrLocationId, 4, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)0);
    glEnableVertexAttribArray(m_nPosAttrLocationId);
    // 设置原色信息属性指针
    glVertexAttribPointer(m_nColorAttrLocationId, 4, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)(sizeof (float) * 3));
    glEnableVertexAttribArray(m_nColorAttrLocationId);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void OpenglRenderWidget::resizeGL(int w, int h)
{
    this->glViewport(0, 0, w, h);

    return QOpenGLWidget::resizeGL(w, h);
}

void OpenglRenderWidget::paintGL()
{
    glClearColor(51.0f / 255.0f, 76.0f / 255.0f, 76.0f / 255.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // 使用shader
    m_pShaderProgram->bind();

    //glBindBuffer(GL_ARRAY_BUFFER, m_nVBOId);
//    // 绘制
//    glDrawArrays(GL_POLYGON, 0, 4);
    //glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIBOId);
    // 绘制
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


    m_pShaderProgram->release();
}

bool OpenglRenderWidget::initShaderProgram(void)
{
    m_pShaderProgram = new QOpenGLShaderProgram(this);

    // 加载顶点着色器
    QString vertexShaderStr(":/vertexshader.vsh");
    m_pVertexShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    bool result = m_pVertexShader->compileSourceFile(vertexShaderStr);
    if (!result)
    {
        qDebug() << m_pVertexShader->log();
        return false;
    }

    // 加载片段着色器
    QString fragmentShaderStr(":/fragmentshader.fsh");
    m_pFragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    result = m_pFragmentShader->compileSourceFile(fragmentShaderStr);
    if (!result)
    {
        qDebug() << m_pFragmentShader->log();
        return false;
    }

    // 创建ShaderProgram
    m_pShaderProgram = new QOpenGLShaderProgram(this);
    m_pShaderProgram->addShader(m_pVertexShader);
    m_pShaderProgram->addShader(m_pFragmentShader);
    return m_pShaderProgram->link();
}

void OpenglRenderWidget::createVertexAttributeData(VertexAttributeData* pVetAttr)
{
    // 第一个点位置信息
    pVetAttr[0].pos[0] = -0.5f;
    pVetAttr[0].pos[1] = 0.5f;
    pVetAttr[0].pos[2] = 0.0f;
    // 第一个点颜色信息
    pVetAttr[0].color[0] = 1.0f;
    pVetAttr[0].color[1] = 0.0f;
    pVetAttr[0].color[2] = 0.0f;

    // 第二个点位置信息
    pVetAttr[1].pos[0] = -0.5f;
    pVetAttr[1].pos[1] = -0.5f;
    pVetAttr[1].pos[2] = 0.0f;
    // 第二个点颜色信息
    pVetAttr[1].color[0] = 0.0f;
    pVetAttr[1].color[1] = 1.0f;
    pVetAttr[1].color[2] = 0.0f;

    // 第三个点位置信息
    pVetAttr[2].pos[0] = 0.5f;
    pVetAttr[2].pos[1] = -0.5f;
    pVetAttr[2].pos[2] = 0.0f;
    // 第三个点颜色信息
    pVetAttr[2].color[0] = 0.0f;
    pVetAttr[2].color[1] = 0.0f;
    pVetAttr[2].color[2] = 1.0f;

    // 第四个点位置信息
    pVetAttr[3].pos[0] = 0.5f;
    pVetAttr[3].pos[1] = 0.5f;
    pVetAttr[3].pos[2] = 0.0f;
    // 第四个点颜色信息
    pVetAttr[3].color[0] = 0.0f;
    pVetAttr[3].color[1] = 1.0f;
    pVetAttr[3].color[2] = 1.0f;
}

<think>嗯,用户想在Qt中使用OpenGL实现网格的渲染和处理。首先,我需要回忆一下QtOpenGL相关的模块。Qt提供了QOpenGLWidget,可以用来集成OpenGL渲染到Qt应用程序中。另外,还有Qt 3D模块,可能更适合高级的三维图形处理,但用户可能更倾向于直接使用OpenGL API。 接下来,用户提到的网格渲染,通常涉及顶点缓冲对象(VBO)、顶点数组对象(VAO)和着色器程序。所以,可能需要指导用户如何创建这些对象。我记得QtQOpenGLFunctions类封装了OpenGL的函数,这样用户不需要直接处理平台相关的上下文初始化。 另外,用户可能需要一个简单的示例代码,展示如何绘制网格。比如,如何定义顶点数据,设置着色器,配置VAO和VBO。同时,还需要考虑如何绘制线框或者实体网格,这涉及到图元类型的选择,比如GL_LINES或GL_TRIANGLES。 然后,处理部分可能包括网格的变换,比如旋转、平移,这可以通过模型视图投影矩阵传递给着色器来实现。用户可能需要了解如何在Qt中处理输入事件,比如鼠标拖动来旋转网格,这需要重写QOpenGLWidget的事件处理函数,如mousePressEvent和mouseMoveEvent。 另外,用户的问题中还提到了Qt中的pyqtgraph的使用,引用3中提到GLGridItem类。可能用户对使用pyqtgraph中的现有组件感兴趣,比如GLGridItem来显示网格。需要解释如何利用这些现有的类,或者如何自定义修改它们以满足需求。 还需要考虑性能优化,比如使用索引缓冲对象(IBO)来减少顶点数据重复,或者使用实例化渲染来处理大量网格。同时,引用1提到QSurface3DSeries利用OpenGL进行高效渲染,可能用户对性能有一定要求,需要强调高效的方法。 最后,确保回答结构清晰,分步骤说明,并提供示例代码。同时,生成相关问题,比如性能优化、交互操作、高级功能等,帮助用户进一步探索。</think>在 Qt 中通过 OpenGL 实现网格渲染可分为以下步骤: ### 一、基础环境搭建 1. 创建继承自 `QOpenGLWidget` 的自定义控件 2. 重写三个关键方法: ```cpp void initializeGL() override; // 初始化OpenGL资源 void resizeGL(int w, int h) override; // 处理窗口尺寸变化 void paintGL() override; // 执行实际绘制 ``` ### 二、网格数据准备 以立方体网格为例,定义顶点和索引数据: ```cpp // 顶点数据(位置 + 颜色) GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下(红色) 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下(绿色) 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部(蓝色) }; // 线框索引数据 GLuint indices[] = { 0, 1, // 底边 1, 2, // 右边 2, 0 // 左边 }; ``` ### 三、OpenGL对象创建(initializeGL) ```cpp void MyGLWidget::initializeGL() { initializeOpenGLFunctions(); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 设置背景色 // 创建VBO和VAO glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); // 绑定VAO glBindVertexArray(VAO); // 填充VBO数据 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 填充EBO数据 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 设置顶点属性指针 // 位置属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 颜色属性 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); } ``` ### 四、绘制实现(paintGL) ```cpp void MyGLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 使用着色器程序 shaderProgram->bind(); // 绑定VAO glBindVertexArray(VAO); // 绘制线框模式 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDrawElements(GL_LINE_LOOP, sizeof(indices)/sizeof(GLuint), GL_UNSIGNED_INT, 0); shaderProgram->release(); } ``` ### 五、进阶技巧 1. **网格变换**:通过模型视图投影矩阵实现旋转/平移 ```cpp QMatrix4x4 model; model.rotate(45.0f, QVector3D(1,0,0)); shaderProgram->setUniformValue("model", model); ``` 2. **材质系统**:添加法线向量实现光照计算 3. **LOD控制**:根据视距动态调整网格细分程度[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值