Farlanki.

Learn OpenGL第一章学习体会

字数统计: 984阅读时长: 3 min
2018/09/16 Share

前言

今天是超强台风‘山竹’袭来的日子,我便安心的宅在家,看完了Learn OpenGL的第一章教程。在此写下一点理解。

环境搭建

依照教程,我使用了 GLAD + GLFW,参考的是这篇文章.不同的是,我并没有将 GLAD 和 GLFW 库加入到系统的头文件目录中,而是直接拖进了工程.编译时,会出现头文件找不到的错误,这时候需要修改头文件的名称.

VBO

VBO 的全称是 vertex buffer object,使用 VBO 可以将顶点信息存储在 GPU 上.

VBO 的生成方式如下:

1
2
3
4
5
6
7
unsigned int VBO;
//生成一个缓冲区名称
glGenBuffers(1, &VBO);
//在绑定的时候, VBO 被真正的创建出来.之后关于 GL_ARRAY_BUFFER 的调用都会作用在当前绑定的 VBO 上.
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//将数据拷贝到 VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

VAO

VAO 的全称是 vertex array object.这篇文章解释了 VAO 出现的原因. VBO 的作用是储存数据,而 VAO 的作用是储存状态信息.

在没有 VAO 的时候, OpenGL 代码可能是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 初始化
GLfloat vertices[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f
}

GLuint vbo;
glGenBuffer(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW);

...

// 绘制
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2, (void*)0);
glDrawArray(GL_TRIANGLES, 0, 3);

...

引用上面文章的一段话:

重看一遍上面的渲染阶段代码,如果我有两份不同的绘制代码,那就需要频繁的重复 glBindBuffer()-glEnableVertexAttribArray()-glVertexAttribPointer-glDrawArray()一套流程,那么本着偷懒的原则,优化方案来了——把这些绘制需要的信息状态在初始化的时候就完整记录下来,真正绘制时只需简单切换一下状态记录。

当 VAO 出现之后,代码变成了这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 初始化
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

...

// 绘制
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glBindVertexArray(0);

当真正需要绘制的时候,只需要选择使用哪个 VAO 就可以了. 所以说, VAO 的作用是简化代码.

VAO 的使用

Learn OpenGL 上是这样说的

The function glVertexAttribPointer specifies how OpenGL should interpret the vertex buffer data whenever a drawing cell is made.The interpretation specified is stored in the current bound vertex array object saving us all quite some work.

可见,在使用glVertexAttribPointer这个方法的时候, VAO 会被自动设置.

fragment shader 的插值

fragment shader 会自动对其输入变量进行插值.

坐标系统

以下是 OpenGL 的坐标系统,使用的是右手坐标系.介绍了何如从一个物体的本地坐标系转换到最终显示在屏幕上的坐标系

投影

OpenGL 有两种投影方式:正交投影和透视投影.透视投影遵循了近大远小的原则,更加贴合现实.

摄像机

在我的理解中, OpenGL 的摄像机是抽象出来的一个特性.我们可以假设场景中有一个摄像机,并且移动摄像机,就能从不同的角度和位置看到场景中的东西.其实,我们移动的不是摄像机,而是整个场景,这样子就模仿了摄像机的功能.

很多教程都说OpenGL 中的’摄像机’默认的位置是(0,0,0),而为什么我们能看到也在(0,0,0)的物体,是因为在默认的情况下,视锥等价于这样,参考这里:

Ortho(-1.0, 1.0, -1.0, 1.0, 1.0, -1.0)

所以,原点在视锥中,我们自然就能看到原点上的物体了.所以,我认为OpenGL 中的”摄像机”只是视锥的一个附带属性,表达了视锥的一些属性,例如方向,位置等.与其说是摄像机生成视锥,不如说是视锥生成了摄像机.

CATALOG
  1. 1. 前言
  2. 2. 环境搭建
  3. 3. VBO
  4. 4. VAO
    1. 4.1. VAO 的使用
  5. 5. fragment shader 的插值
  6. 6. 坐标系统
  7. 7. 投影
  8. 8. 摄像机