• 3D Computer Grapihcs Using OpenGL


    在上一节的案例中,我们使用了四个Buffer Object,立方体的VertexBuffer,立方体的索引Buffer,四面体的VertexBuffer,四面体的索引Buffer。

    我们这节尝试把两个图形的Vertex Buffer结合,两个图形的索引Buffer结合,形成两个Buffer,让程序更加简化。

    先看最终代码: MyGlWindow.cpp:

      1 #include <glglew.h>
      2 #include "MyGlWindow.h"
      3 #include <iostream>
      4 #include <fstream>
      5 #include <glmgtcmatrix_transform.hpp>
      6 #include <glmgtx	ransform.hpp>
      7 #include <ShapeGenerator.h>
      8 #include <Qt3DInputqmouseevent.h>
      9 #include <Qt3DInputqkeyevent.h>
     10 
     11 
     12 GLuint programID;
     13 
     14 
     15 GLuint VertexBufferID;
     16 GLuint IndexBufferID;
     17 
     18 GLuint cubeVertexArrayObjectID;
     19 //立方体的索引数组长度
     20 GLuint cubeNumIndices;
     21 
     22 GLuint tetraVertexArrayObjectID;
     23 //四面体的索引数组长度
     24 GLuint tetraNumIndices;
     25 
     26 GLuint tetraIndexByteOffset;
     27 
     28 GLuint fullTransformUniformLocation;
     29 
     30 
     31 void MyGlWindow::sendDataToOpenGL()
     32 {
     33     //创建Cube
     34     ShapeData cube = ShapeGenerator::makeCube();
     35     //创建四面体
     36     ShapeData tetra = ShapeGenerator::makeTetrahedron();
     37 
     38     //创建和设置VertexBuffer
     39     glGenBuffers(1, &VertexBufferID);
     40     glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
     41     glBufferData(GL_ARRAY_BUFFER, cube.vertexBufferSize() + tetra.vertexBufferSize(),0, GL_STATIC_DRAW);
     42     glBufferSubData(GL_ARRAY_BUFFER, 0, cube.vertexBufferSize(), cube.vertices);
     43     glBufferSubData(GL_ARRAY_BUFFER, cube.vertexBufferSize(), tetra.vertexBufferSize(), tetra.vertices);
     44 
     45     //创建和设置IndexBuffer
     46     glGenBuffers(1, &IndexBufferID);
     47     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
     48     glBufferData(GL_ELEMENT_ARRAY_BUFFER, cube.indexBufferSize() + tetra.indexBufferSize() , 0, GL_STATIC_DRAW);
     49     glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, cube.indexBufferSize(), cube.indices);
     50     glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, cube.indexBufferSize(), tetra.indexBufferSize(), tetra.indices);
     51 
     52 
     53     cubeNumIndices = cube.numIndices;
     54     tetraNumIndices = tetra.numIndices;
     55 
     56 
     57 
     58     //设置绘制Cube的VAO
     59     //生成VAO
     60     glGenVertexArrays(1, &cubeVertexArrayObjectID);
     61     //绑定VAO,后续的一系列状态和设置都会存储在这个VAO里。
     62     glBindVertexArray(cubeVertexArrayObjectID);
     63     //开启通道1(位置)
     64     glEnableVertexAttribArray(0);
     65     //开启通道2(颜色)
     66     glEnableVertexAttribArray(1);
     67     //绑定顶点数据ID到绑定点
     68     glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
     69     //设置通道1如何获取数据
     70     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
     71     //设置通道2如何获取数据
     72     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)(sizeof(float) * 3));    
     73     //绑定索引数据ID到绑定点
     74     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
     75 
     76 
     77     //设置绘制四面体的VAO
     78     glGenVertexArrays(1, &tetraVertexArrayObjectID);
     79     glBindVertexArray(tetraVertexArrayObjectID);
     80     //开启通道1(位置)
     81     glEnableVertexAttribArray(0);
     82     //开启通道2(颜色)
     83     glEnableVertexAttribArray(1);
     84     //绑定顶点数据ID到绑定点
     85     glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
     86     //设置通道1如何获取数据
     87     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)(cube.vertexBufferSize()));
     88     //设置通道2如何获取数据
     89     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)(cube.vertexBufferSize() +sizeof(float) * 3));
     90     //绑定索引数据ID到绑定点
     91     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
     92 
     93     tetraIndexByteOffset = cube.indexBufferSize();
     94 
     95     cube.cleanUp();
     96     tetra.cleanUp();
     97 }
     98 
     99 void MyGlWindow::installShaders()
    100 {
    101     //...
    102 }
    103 
    104 void MyGlWindow::initializeGL()
    105 {
    106     glewInit();
    107     glEnable(GL_DEPTH_TEST);
    108     sendDataToOpenGL();
    109     installShaders();
    110 }
    111 
    112 void MyGlWindow::paintGL()
    113 {
    114     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    115     glViewport(0, 0, width(), height());
    116 
    117     //绑定cube的VAO,下面绘制的都是立方体--------------------------------------
    118     glBindVertexArray(cubeVertexArrayObjectID);
    119 
    120     glm::mat4 fullTransformMatrix;
    121     glm::mat4 viewToProjectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f);
    122     glm::mat4 worldToViewMatrix = camera.getWorldToViewMatrix();
    123     glm::mat4 worldToProjectionMatrix = viewToProjectionMatrix * worldToViewMatrix;
    124 
    125     //绘制Cube1
    126     glm::mat4 cube1ModelToWorldMatrix =
    127         glm::translate(glm::vec3(-1.0f, 0.0f, -3.0f))*
    128         glm::rotate(36.0f, glm::vec3(1.0f, 0.0f, 0.0f));
    129 
    130     fullTransformMatrix = worldToProjectionMatrix * cube1ModelToWorldMatrix;
    131     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
    132     glDrawElements(GL_TRIANGLES, cubeNumIndices, GL_UNSIGNED_SHORT, 0);
    133 
    134     //绘制Cube2
    135     glm::mat4 cube2ModelToWorldMatrix = 
    136         glm::translate(glm::vec3(1.0f, 0.0f, -3.75f))*
    137         glm::rotate(36.0f, glm::vec3(0.0f, 1.0f, 0.0f));
    138     fullTransformMatrix = worldToProjectionMatrix * cube2ModelToWorldMatrix;
    139     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
    140     glDrawElements(GL_TRIANGLES, cubeNumIndices, GL_UNSIGNED_SHORT, 0);
    141 
    142     //绑定Tetra的VAO,下面绘制的都是四面体--------------------------------------
    143     glBindVertexArray(tetraVertexArrayObjectID);
    144 
    145     //绘制Tetra1
    146     glm::mat4 tetra1ModelToWorldMatrix =
    147         glm::translate(glm::vec3(1.0f, -2.0f, -3.75f))*
    148         glm::rotate(36.0f, glm::vec3(0.0f, 1.0f, 0.0f));
    149     fullTransformMatrix = worldToProjectionMatrix * tetra1ModelToWorldMatrix;
    150     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
    151     glDrawElements(GL_TRIANGLES, tetraNumIndices, GL_UNSIGNED_SHORT, (void*)tetraIndexByteOffset);
    152 
    153     glm::mat4 tetra2ModelToWorldMatrix =
    154         glm::translate(glm::vec3(-3.0f, -2.0f, -3.75f))*
    155         glm::rotate(36.0f, glm::vec3(1.0f, 1.0f, 0.0f));
    156     fullTransformMatrix = worldToProjectionMatrix * tetra2ModelToWorldMatrix;
    157     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
    158     glDrawElements(GL_TRIANGLES, tetraNumIndices, GL_UNSIGNED_SHORT, (void*)tetraIndexByteOffset);
    159 }
    160 
    161 
    162 std::string MyGlWindow::ReadShaderCode(const char* fileName)
    163 {
    164     //...
    165 }
    166 
    167 void MyGlWindow::mouseMoveEvent(QMouseEvent * e)
    168 {
    169     //...
    170 }
    171 
    172 void MyGlWindow::keyPressEvent(QKeyEvent * e)
    173 {
    174     //...
    175 }

    注意:为了方便起见,我们把原先setupVertexArray函数的内容全部移动到sendDataToOpenGL函数中了。

    下面详解改动的过程:

    • 首先合并了两个顶点数组和两个索引数组, cubeVertexBufferID + tetraVertexBufferID -> VertexBufferID, cubeIndexBufferID + tetraIndexBufferID -> IndexBufferID。
    • 在sendDataToOpenGL()函数的一开始,我们先定义好两个图形。
    • 合并了顶点数组和索引数组以后,在初始化时候就要初始化足够的长度,在41行我们指定VertexBuffer的长度为: cube.vertexBufferSize() + tetra.vertexBufferSize(),正好容纳两个顶点数组,而数组内容暂时为空(第三个参数为0)。
    • 然后使用两个glBufferSubData函数分块给顶点数组填充内容,42,43行分别给VertexBuffer的第一部分和第二部分填充了数据,注意第二个和第三个参数,分别指定了开始的偏移值和数组的长度。这也是为什么42行的第二个参数为0,而43行的第二个参数为cube.vertexBuffer()的原因。
    • 顶点数组设定好以后,下面就设定索引数组(46-50),其设定方法和顶点数组类似,就不赘述了。
    • 我们把sendDataToOpenGL函数和setupVertexArray函数合并,并且把cube.cleanUp()和tetra.cleanUp()放在了最后,原因是后面还需要使用到cube对象的相关属性
    • 58-91行中只改动了87和89行的最后一个参数。这里的改动主要是针对VertexBuffer的,因为VertexBuffer发生了变化,所以四面体的位置和颜色信息在数组中都要往后整体偏移cube.vertexBufferSize()个位置。
    • 93行我们定义了一个tetraIndexByteOffset成员,用来记录立方体的索引Buffer的长度(绘制的时候要用)
    • 在绘制函数中,内容同样基本没变,只是改变了151行和158行的最后一个参数,这里是针对IndexBuffer的,因为glDrawElements()函数的最后一个参数是说明索引Buffer数组的偏移值的(顶点Buffer在前面已经用glVertexAttribPointer函数设置过了),因此,在绘制四面体时候需要向后偏移上一步我们记录的tetraIndexByteOffset个单位。绘制立方体则不需要偏移,因为它们仍处在数组的开头。

    修改完成后,运行,结果和上节一样,但是逻辑上我们已经把四个数组简化成2个数组了。

    PS:我们在这里需要对之前的代码加个“补丁”,我们之前创建的Buffers都没有删除,这会造成内存泄露,所以我们给MyGlWindow类加一个析构函数.

    1 MyGlWindow::~MyGlWindow()
    2 {
    3     glDeleteBuffers(1, &VertexBufferID);
    4     glDeleteBuffers(1, &IndexBufferID);
    5     glUseProgram(0);
    6     glDeleteProgram(programID);
    7 }

    把之前的VertexBuffer,IndexBuffer都删除掉。另外也要删除掉Program,但是主要在删除Program 之前需要glUseProgram(0)。

  • 相关阅读:
    面向对象二 — — static、final、常量的初始化、this和super
    面向对象二 — — 继承、抽象类、接口
    面向对象一基础知识
    JDBC简介及其用Java连接数据库
    Java I/O 几个重要流的应用
    GUI Panel 容器以及布局管理器
    Oracle 数据库一
    Java I/O文件拷贝
    Java I/O文件的过滤 、读取、写入
    简单理解io与nio
  • 原文地址:https://www.cnblogs.com/AnKen/p/8417002.html
Copyright © 2020-2023  润新知