QT+OpenGL五之绘制不同的模型(vao,vbo机制)

前言:上几张我们做好了一个立方体模型(三角形图元)和第一视角,但是我们会发现内容单一,且绘制不成模式(没有系统的管理绘制步骤),效率也不是特别高(采用vbo的方式)。可能我们还不明白vbo的机制。所以我们无法绘制多个不同模型。

原因:

1.我们不知道是采用每个模型对象都对应一个shader程序,还是所有模型采用同一个shader程序。
2.vao和vbo的机制是怎样的,我们也不清楚。

分析:

1.很显然大部分的模型采用同一个shader程序效率会更高,因为在同一场景shader程序中所需要的矩阵基本都是一致的,比如在同一种光源下的阴影算法会基本相同,模型视口矩阵也基本相同。只有在部份特殊需求的情况才会单独给模型建立shader程序。比如一些特殊地点需要特殊的光照算法。
2.虽然我们明白了要使用同一个shader程序,但是这又会引入困惑,我们需要在每次绘制时绑定不同的顶点属性。因为是同一个shader程序,所以设置的时候如果不了解vbo的机制,就容易导致错误绘制,很可能模型会只认最后一次设置的顶点属性。
3.如果明白vbo机制和会使用的话,我们也会发现每一次的绘制都会对当前上下文(当前的vbo缓冲区)vbo进行绑定、设置顶点属性才能进行绘制。因此这样会降低效率,这时vao就能解决这种情况,vao会记录顶点属性的设置,能很轻松的管理顶点属性,绘制时就不需要管vbo了,统一使用vao来进行绘制。

注意:因为传统的OpenGL的名词(上下文、状态机)可能很难帮助我们理解OpenGL的一些知识,我会尽量把语言通俗化。

vbo:

image.png

首先我们可以把红线当成吸管或者通讯线路。它默认是断开的。在OpenGL原生代码中

....
glGenBuBuffers(1,vbo);//创建一个vbo对象,并分配缓冲区;
....
// 绑定VBO,设置VBO中的数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 1. 设置顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);  
// 2. 使用Shader程序
glUseProgram(shaderProgram);
// 3. 绘制
 glDrawArrays(GL_QUADS, 0, 24);

可以看见glBindBuffer(GL_ARRAY_BUFFER, VBO);原文说的绑定,其实可以理解成告诉vbo缓冲区将有吸管插入进来,准备进行数据传输。glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);指的是当前将吸管1插入分配的缓冲区中,按指定的大小和数据是否会改变的形式把数据吐到vbo缓冲区中。glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);将吸管2插入到着色器程序中告诉他我要用这种方式(每次取3个值,自动偏移1个位置)去取顶点数据。glEnableVertexAttribArray(0); 把吸管3从着色器插入到vbo缓冲区,在调用draw函数时一个点一个点的把数据吸到着色器中。大家可以观察到这套步骤时一个流程,中途不能打断,如果打断就会出现绘制错误,导致着色器不知道去哪个缓冲区吸数据,他就会默认从最后一次设置去吸取数据。当然在同一个函数中不会出现被打断的情况。

出错原因:

我们使用同一个shader程序,在同一个模型对象中平时会写一个init()函数和一个draw函数。这时代码会被分配到不同函数中,所以每个模型的吸管3会一直指向最后一次的vbo缓冲区,为了避免出错,我们会在init调用一次glBindBuffer把数据分配给vbo缓冲区,在绘制时调用glBindBuffer告诉缓冲区我们当前吸管3重新连接,这是就能重新插入到当前使用的vbo缓冲区。所以我们每次绘制都会调用glBindBuffer和重新设置顶点属性。这样会影响效率所以vao就诞生了。

vao:

vao不需要画图了,因为他就是一个记录和管理功能(把吸管1,2,3的步骤都记录下来),作为桥梁我们就只需要使用vao来绘制。这样就可以把之前的vbo的操作全部写到init函数中,在draw中使用vao就可以了,这样也能避免vbo被分开出错和效率底的问题。
vao原生代码:

void init(){
//创建VAO
GLuint VAO;
glGenVertexArrays(1, &VAO);//创建vao
glBindVertexArray(VAO);//启动vao
   glBindBuffer(GL_ARRAY_BUFFER, VBO); //设置了VBO
   glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//设置VBO中的数据
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); //设置顶点属性(索引为0的属性,与shader中的内容有交互)
    glEnableVertexAttribArray(0); //设置开启顶点属性(索引为0的属性,与shader中的内容有交互)
glBindVertexArray(0); //关闭vao,vao操作完成后要关闭,避免后面的代码影响它的设置
}
void draw(){
glUseProgram(shaderProgram);
glBindVertexArray(VAO); //启动vao,这时我们就开始使用它储存的属性了
 glDrawArrays(GL_QUADS, 0, 24);
glBindVertexArray(0);   //关闭vao
}

好了分析完毕,上qt代码:

我们建立一个skybox类绘制一个以正方形为图元的立方体,为后面的天空盒做准备。实际前几张的绘制代码都要这样改(以类和对象的形式)。
.h

#pragma once
#include 
#include "Camera.h"
class SkyBox : protected QOpenGLFunctions_4_3_Core
{
public:
    SkyBox();
    ~SkyBox();
    void init(QOpenGLShaderProgram* shaderProgram, int width, int height);
    void draw(Camera camera);
private:
    GLuint vPosition, uvlo;
    float cameraX, cameraY, cameraZ;
    float cubeLocX, cubeLocY, cubeLocZ;
    QOpenGLShaderProgram* shaderProgram;
    QOpenGLTexture* mTexture;
    GLuint mvLoc, projLoc, imgTexture;
    QMatrix4x4 pMat, mMat, mvMat, lookat;
    QOpenGLVertexArrayObject vao;//对应vao
    QOpenGLBuffer vbo, uvVbo;//对应vbo
    void setupVertices();
};

.cpp

#include "stdafx.h"
#include "SkyBox.h"
SkyBox::SkyBox() {
}
SkyBox::~SkyBox() {
    delete mTexture;
    vbo.destroy();
    uvVbo.destroy();
}
void SkyBox::init(QOpenGLShaderProgram* shaderProgram, int width, int height) {
    initializeOpenGLFunctions();
    this->shaderProgram = shaderProgram;
    cameraX = 0.0f; cameraY = 0.0f; cameraZ = 0.0f;
    cubeLocX = 0.0f; cubeLocY = 0.0f; cubeLocZ = 0.0f;
    setupVertices();
    mvLoc = shaderProgram->uniformLocation("mv_matrix");
    projLoc = shaderProgram->uniformLocation("proj_matrix");//获取程序的统一变量mv,投影矩阵
    //构建透视矩阵
    float aspect = (float)width / (float)height;
    pMat.perspective(60.0f, aspect, 0.1f, 1000.f);
    //构建视图矩阵
    QMatrix4x4 vMat;
    vMat.lookAt(QVector3D(cameraX, cameraY, cameraZ), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
    mMat.translate(cubeLocX, cubeLocY, cubeLocZ);
    mvMat = vMat * mMat;
    //将mv矩阵发送给对应的统一变量
    shaderProgram->setUniformValue(mvLoc, mvMat);
    shaderProgram->setUniformValue(projLoc, pMat);
    mTexture = new QOpenGLTexture(QImage(":/skyBox/Resources/skyBox/front.bmp").mirrored());
    mTexture->setMinificationFilter(QOpenGLTexture::Nearest);
    mTexture->setMagnificationFilter(QOpenGLTexture::Linear);
}
void SkyBox::setupVertices() {
    GLfloat vertexPositions[72] = {
       -0.5,-0.5,-0.5,0.5,-0.5,-0.5,0.5,0.5,-0.5,-0.5,0.5,-0.5,
       0.5,-0.5,0.5,-0.5,-0.5,0.5,-0.5,0.5,0.5,0.5, 0.5, 0.5,
       -0.5,-0.5,0.5,-0.5,-0.5,-0.5,-0.5,0.5, -0.5,-0.5, 0.5,0.5,
       0.5,-0.5,-0.5,0.5,-0.5,0.5,0.5, 0.5,0.5,0.5, 0.5, -0.5,
       -0.5,0.5,-0.5,0.5,0.5,-0.5,0.5, 0.5,0.5,-0.5, 0.5, 0.5,
       -0.5,-0.5f, 0.5,0.5,-0.5, 0.5,0.5,-0.5,-0.5,-0.5,-0.5,-0.5
    };
    GLfloat uv[48] = {
    0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.0f,1.0f,
    0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.0f,1.0f,
    0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.0f,1.0f,
    0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.0f,1.0f,
    0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.0f,1.0f,
    0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.0f,1.0f,
    };
    vao.create();
    vbo.create();
    uvVbo.create();
    vao.bind();
    vbo.bind();
    vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    vbo.allocate(vertexPositions, sizeof(vertexPositions));
    vPosition = shaderProgram->attributeLocation("vPosition");
    shaderProgram->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, 0);
    uvVbo.bind();
    uvVbo.allocate(uv, sizeof(uv));
    glEnableVertexAttribArray(vPosition);
    uvlo = shaderProgram->attributeLocation("inuv");
    shaderProgram->setAttributeBuffer(uvlo, GL_FLOAT, 0, 2, 0);
    glEnableVertexAttribArray(uvlo);
    vao.release();
}
void SkyBox::draw(Camera camera) {
    mTexture->bind(mTexture->textureId());
    shaderProgram->setUniformValue("imgTexture", mTexture->textureId());
    QMatrix4x4 v;
    v.lookAt(QVector3D(camera.location.x, camera.location.y, camera.location.z),
        QVector3D(camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z),
        QVector3D(camera.worldY.x, camera.worldY.y, camera.worldY.z));
    mvMat = v * mMat;
    shaderProgram->setUniformValue(mvLoc, mvMat);
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    vao.bind();
    glDrawArrays(GL_QUADS, 0, 24);
    vao.release();
    mTexture->release();
}

OpenglWegdit

#include "stdafx.h"
#include "MyOpenglWegdit.h"
MyShader shader;
SkyBox sky;
Cube cube;
MyOpenglWegdit::MyOpenglWegdit(QWidget*parent)
    : QOpenGLWidget(parent)
{
    this->grabKeyboard();
}

MyOpenglWegdit::~MyOpenglWegdit()
{
 
}
void MyOpenglWegdit::initializeGL()
{
    initializeOpenGLFunctions();
    shader.creatShader(":/QtGuiApplication1/Resources/config/shader.vs", ":/QtGuiApplication1/Resources/config/shader.fs");
    glShadeModel(GL_SMOOTH);//设置阴影平滑模式
    glClearColor(0.98, 0.625, 0.12, 0.5);//改变窗口的背景颜色
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//进行透视校正
    sky.init(shader.getShader(), width(), height());
    cube.init(shader.getShader(), width(), height());
}

void MyOpenglWegdit::resizeGL()
{
    glViewport(0, 0, width(), height());
}

void MyOpenglWegdit::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    updataDetlaTime();
    sky.draw(camera);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    cube.draw(camera);
    //glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
   QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
}

效果图:


image.png

目录

VSC++2019+QT+OpenGL
QT+OpenGL一之绘制立方体(三角形图元)
QT+OpenGL二之纹理贴图
QT+OpenGL三之矩阵简解
QT+OpenGL四之相机的移动和旋转
QT+OpenGL五之绘制不同的模型(vao,vbo机制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型准备
QT+OPenGL九之模型解码
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和镜面反射贴图
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的点光源和聚光灯
QT+OPenGL十四之多光源混合的问题
QT+OPenGL十五之深度缓冲区
QT+OPenGL十六之模板缓冲区
QT+OPenGL十七帧缓冲区(离屏渲染)
QT+OPenGL十八抗锯齿
QT+OPenGL十九镜面反射效率调整
QT+OPenGL二十Gamma校正

你可能感兴趣的:(QT+OpenGL五之绘制不同的模型(vao,vbo机制))