3D Computer Grapihcs Using OpenGL

本节我们将绘制一个3维物体,立方体。

如果要渲染3D物体,我们需要了解MVP(Model View Projection),它表示三个转换矩阵。实际上这个名字不够明确,更加确切的释义如下:

  • Model - Model to World  模型空间到世界空间
  • View - World to View      世界空间到视图空间
  • Projection - View to Projection   视图空间到投影空间

要实现这三个转换矩阵,我们需要借助glm数学库提供的一些方便的结构体和函数。

重构

我们先对程序结构进行修改,对工程右键>Add > New Filter, 创建一个Primitives 文件夹,在其中创建两个文件,一个Vertex.h,一个ShapeData.h

Vertex.h中定义了一个Vertex结构体,它包含两个glm::vec3成员,分别表示位置和颜色。

1 #pragma once
2 #include <glmglm.hpp>
3 
4 struct Vertex
5 {
6     glm::vec3 position;
7     glm::vec3 color;
8 };

ShapeData.h中定义了一个ShapeData结构体,包含四个成员变量,分别是

  • Vertex* 类型:顶点数组指针
  • Gluint类型:顶点数量
  • GLushort* 类型:索引数组指针
  • GLuint 类型:索引数组长度

另外还提供了构造函数,清理函数

 1 #pragma once
 2 #include <GLglew.h>
 3 #include "Vertex.h"
 4 
 5 struct ShapeData
 6 {
 7     ShapeData() :
 8         vertices(0), numVertices(0), indices(0), numIndices(0) {}
 9 
10     Vertex* vertices;
11     GLuint numVertices;
12     GLushort* indices;
13     GLuint numIndices;
14 
15     GLsizeiptr vertexBufferSize() const
16     {
17         return numVertices * sizeof(Vertex);
18     }
19     GLsizeiptr indexBufferSize() const
20     {
21         return numIndices * sizeof(GLushort);
22     }
23 
24     void cleanUp()
25     {
26         delete[] vertices;
27         delete[] indices;
28         numVertices = numIndices = 0;
29     }
30 };

此外还加入了一个新的类,ShapeGenerator

ShapeGenerator.h

1 #pragma once
2 #include <ShapeData.h>
3 
4 class ShapeGenerator
5 {
6 public:
7     static ShapeData makeCube();
8 };

ShapeGenerator.cpp

 1 #include "ShapeGenerator.h"
 2 #include "Vertex.h"
 3 
 4 #define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(*a)
 5 
 6 ShapeData ShapeGenerator::makeCube()
 7 {
 8     ShapeData ret;
 9     Vertex stackVerts[]=
10     {
11         glm::vec3(-1.0f, +1.0f, +1.0f), //0
12         glm::vec3(+1.0f, 0.0f, 0.0f),   //Color
13         glm::vec3(+1.0f, +1.0f, +1.0f), //1
14         glm::vec3(0.0f, +1.0f, 0.0f),    //Color
15         glm::vec3(+1.0f, +1.0f, -1.0f), //2
16         glm::vec3(0.0f, 0.0f, +1.0f), //Color
17         glm::vec3(-1.0f, +1.0f, -1.0f), //3
18         glm::vec3(+1.0f, +1.0f, +1.0f), //Color
19 
20         glm::vec3(-1.0f, +1.0f, -1.0f), //4
21         glm::vec3(+1.0f, 0.0f, +1.0f),   //Color
22         glm::vec3(+1.0f, +1.0f, -1.0f), //5
23         glm::vec3(0.0f, 0.5f, 0.2f),    //Color
24         glm::vec3(+1.0f, -1.0f, -1.0f), //6
25         glm::vec3(0.8f, 0.6f, 0.4f), //Color
26         glm::vec3(-1.0f, -1.0f, -1.0f), //7
27         glm::vec3(0.3f, +1.0f, +0.5f), //Color
28 
29         glm::vec3(+1.0f, +1.0f, -1.0f), //8
30         glm::vec3(0.2f, 0.5f, 0.2f),  //Color
31         glm::vec3(+1.0f, +1.0f, +1.0f), //9
32         glm::vec3(0.9f, 0.3f, 0.7f),    //Color
33         glm::vec3(+1.0f, -1.0f, +1.0f), //10
34         glm::vec3(0.3f, 0.7f, 0.5f),    //Color
35         glm::vec3(+1.0f, -1.0f, -1.0f), //11
36         glm::vec3(0.5f, 0.7f, 0.5f),  //Color
37 
38         glm::vec3(-1.0f, +1.0f, +1.0f), //12
39         glm::vec3(0.7f, 0.8f, 0.2f),   //Color
40         glm::vec3(-1.0f, +1.0f, -1.0f), //13
41         glm::vec3(0.5f, 0.7f, 0.3f),    //Color
42         glm::vec3(-1.0f, -1.0f, -1.0f), //14
43         glm::vec3(0.8f, 0.6f, 0.4f), //Color
44         glm::vec3(-1.0f, -1.0f, +1.0f), //15
45         glm::vec3(0.3f, +1.0f, +0.5f), //Color
46 
47         glm::vec3(+1.0f, +1.0f, +1.0f), //16
48         glm::vec3(0.7f, 0.8f, 0.2f),   //Color
49         glm::vec3(-1.0f, +1.0f, +1.0f), //17
50         glm::vec3(0.5f, 0.7f, 0.3f),    //Color
51         glm::vec3(-1.0f, -1.0f, +1.0f), //18
52         glm::vec3(0.8f, 0.6f, 0.4f), //Color
53         glm::vec3(+1.0f, -1.0f, +1.0f), //19
54         glm::vec3(0.3f, +1.0f, +0.5f), //Color
55 
56         glm::vec3(+1.0f, -1.0f, -1.0f), //20
57         glm::vec3(0.7f, 0.8f, 0.2f),   //Color
58         glm::vec3(-1.0f, -1.0f, -1.0f), //21
59         glm::vec3(0.5f, 0.7f, 0.3f),    //Color
60         glm::vec3(-1.0f, -1.0f, +1.0f), //22
61         glm::vec3(0.8f, 0.6f, 0.4f), //Color
62         glm::vec3(+1.0f, -1.0f, +1.0f), //23
63         glm::vec3(0.3f, +1.0f, +0.5f), //Color
64     };
65 
66     ret.numVertices = NUM_ARRAY_ELEMENTS(stackVerts);
67     ret.vertices = new Vertex[ret.numVertices];
68     memcpy(ret.vertices, stackVerts, sizeof(stackVerts));
69 
70     unsigned short stackIndices[] = 
71     {
72         0,1,2,0,2,3,
73         4,5,6,4,6,7,
74         8,9,10,8,10,11,
75         12,13,14,12,14,15,
76         16,17,18,16,18,19,
77         20,22,21,20,23,22,
78     };
79 
80     ret.numIndices = NUM_ARRAY_ELEMENTS(stackIndices);
81     ret.indices = new GLushort[ret.numIndices];
82     memcpy(ret.indices, stackIndices, sizeof(stackIndices));
83     return ret;
84 }

主要作用是提供了一个静态方法 makeCube,返回一个立方体的数据。

修改MyGlWindow类

  1 #include <glglew.h>
  2 #include "MyGlWindow.h"
  3 #include <iostream>
  4 #include <fstream>
  5 #include <glmgtcmatrix_transform.hpp>
  6 #include <ShapeGenerator.h>
  7 
  8 
  9 
 10 GLuint programID;
 11 GLuint numIndices;
 12 
 13 void MyGlWindow::sendDataToOpenGL()
 14 {
 15     
 16     ShapeData shape = ShapeGenerator::makeCube();
 17 
 18     GLuint vertexBufferID;
 19     glGenBuffers(1, &vertexBufferID);
 20     glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
 21     glBufferData(GL_ARRAY_BUFFER, shape.vertexBufferSize(), shape.vertices, GL_STATIC_DRAW);
 22 
 23     GLuint indexBufferID;
 24     glGenBuffers(1, &indexBufferID);
 25     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
 26     glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape.indexBufferSize(), shape.indices, GL_STATIC_DRAW);
 27 
 28     glEnableVertexAttribArray(0);
 29     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
 30 
 31     glEnableVertexAttribArray(1);
 32     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (char*)(sizeof(GLfloat) * 3));
 33 
 34     numIndices = shape.numIndices;
 35     shape.cleanUp();
 36 
 37 }
 38 
 39 void MyGlWindow::installShaders()
 40 {
 41     GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
 42     GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
 43 
 44     std::string tmp = ReadShaderCode("VertexShaderCode2.glsl");
 45     const char* vertexShaderCode = tmp.c_str();
 46     glShaderSource(vertexShaderID, 1, &vertexShaderCode, 0);
 47 
 48     tmp = ReadShaderCode("FragmentShaderCode2.glsl");
 49     const char* fragmentShaderCode = tmp.c_str();    
 50     glShaderSource(fragmentShaderID, 1, &fragmentShaderCode, 0);
 51 
 52     glCompileShader(vertexShaderID);
 53     glCompileShader(fragmentShaderID);
 54 
 55     programID = glCreateProgram();
 56     glAttachShader(programID, vertexShaderID);
 57     glAttachShader(programID, fragmentShaderID);
 58 
 59     glLinkProgram(programID);
 60 
 61     glUseProgram(programID);
 62 }
 63 
 64 void MyGlWindow::initializeGL()
 65 {
 66     glewInit();
 67     glEnable(GL_DEPTH_TEST);
 68     sendDataToOpenGL();
 69     installShaders();
 70 }
 71 
 72 void MyGlWindow::paintGL()
 73 {
 74     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
 75     glViewport(0, 0, width(), height());
 76 
 77     //更新:最新版本的glm中,glm::mat4()生成的是不是单位矩阵,而是零矩阵,这里要使用glm::mat4(1.0f)才可以
 78     glm::mat4 modelTransformMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f,-3.0f));
 79     glm::mat4 projectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f);
 80 
 81     GLint modelTransformUniformLocation = glGetUniformLocation(programID, "modelMatrix");
 82     GLint projectionMatrixUniformLocation = glGetUniformLocation(programID, "projectionMatrix");
 83 
 84     glUniformMatrix4fv(modelTransformUniformLocation, 1, GL_FALSE, &modelTransformMatrix[0][0]);
 85     glUniformMatrix4fv(projectionMatrixUniformLocation, 1, GL_FALSE, &projectionMatrix[0][0]);
 86 
 87     glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
 88 
 89 }
 90 
 91 
 92 std::string MyGlWindow::ReadShaderCode(const char* fileName)
 93 {
 94     std::ifstream myInput(fileName);
 95     if (!myInput.good())
 96     {
 97         std::cout << "File failed to load..." << fileName;
 98         exit(1);
 99     }
100     return std::string(
101         std::istreambuf_iterator<char>(myInput),
102         std::istreambuf_iterator<char>());
103 }

Vertex Shader :

 1 #version 430                           
 2                                        
 3 in layout(location=0) vec3 position;   
 4 in layout(location=1) vec3 vertexColor;                                       
 5                                       
 6 uniform mat4 modelMatrix;
 7 uniform mat4 projectionMatrix; 
 8 
 9 out vec3 passingColor;
10                                        
11 void main()                            
12 {                                      
13   vec4 v =  vec4(position,1.0);
14   vec4 newPosition = modelMatrix * v;
15   gl_Position = projectionMatrix * newPosition;
16   passingColor= vertexColor;           
17 }

Fragment Shader:

 1 #version 430                                         
 2                                       
 3 in vec3 passingColor;                 
 4 out vec4 finalColor;                  
 5 
 6                          
 7 void main()                           
 8 {                                     
 9   finalColor = vec4(passingColor,1.0);
10 }                            

注意MyGlWindow的78-85行,是使用Uniform 变量的通用方法,使用的是Vertex Shader中第6-7行的两个uniform。

使用Uniform变量的步骤总结:

  1. 使用glGetUniformLocation获取Uniform变量的ID,并储存在一个GLint 变量中
  2. 使用glUnifomxxxx()类的函数和刚才得到的ID给Uniform赋值。

另外要注意85-86行,函数的最后一参数需要一个const GLfloat * 类型的变量,所以我们使用[0][0]获取矩阵的第一个元素,它是个GLfloat类型的,再对他使用取地址符&得到它的地址。

编译运行以后得到一个平面(实际上是立方体的一个面):

我们在最开始提到了3个矩阵,但是这里只用到了两个,实际上少了第二个矩阵,World to View矩阵,这也正是为什么我们现在无法移动观察视角的原因,我们的相机被假设在世界原点,朝向-z的方向看去,这是默认的设置。后面我们会学习world to view的转换矩阵。

原文地址:https://www.cnblogs.com/AnKen/p/8350165.html