1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-23 04:35:28 +08:00

校对 01-04 你好,三角形

This commit is contained in:
Kang Lin
2022-05-13 13:43:24 +08:00
committed by Gary Wang
parent 6f5474c090
commit 4ece309afd
3 changed files with 21 additions and 17 deletions

View File

@@ -4,7 +4,7 @@
---|---
作者 | JoeyDeVries
翻译 | [Django](http://bullteacher.com/), Krasjet, Geequlim
校对 | 暂未校对
校对 | Kang Lin <kl222@126.com>
!!! note "译注"
@@ -12,7 +12,7 @@
- 顶点数组对象Vertex Array ObjectVAO
- 顶点缓冲对象Vertex Buffer ObjectVBO
- 索引缓冲对象Element Buffer ObjectEBOIndex Buffer ObjectIBO
- 元素缓冲对象Element Buffer ObjectEBO 或 索引缓冲对象 Index Buffer ObjectIBO
当指代这三个东西的时候,可能使用的是全称,也可能用的是英文缩写,翻译的时候和原文保持的一致。由于没有英文那样的分词间隔,中文全称的部分可能不太容易注意。但请记住,缩写和中文全称指代的是一个东西。
@@ -24,7 +24,7 @@
图形渲染管线接受一组3D坐标然后把它们转变为你屏幕上的有色2D像素输出。图形渲染管线可以被划分为几个阶段每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的它们都有一个特定的函数并且很容易并行执行。正是由于它们具有并行执行的特性当今大多数显卡都有成千上万的小处理核心它们在GPU上为每一个渲染管线阶段运行各自的小程序从而在图形渲染管线中快速处理你的数据。这些小程序叫做<def>着色器</def>(Shader)。
有些着色器允许开发者自己配置,这就允许我们用自己写的着色器来替默认的。这样我们就可以更细致地控制图形渲染管线中的特定部分了,而且因为它们运行在GPU上所以它们可以给我们节约宝贵的CPU时间。OpenGL着色器是用<def>OpenGL着色器语言</def>(OpenGL Shading Language, <def>GLSL</def>)写成的,在下一节中我们再花更多时间研究它。
有些着色器可以由开发者配置,因为允许用自己写的着色器来替默认的,所以能够更细致地控制图形渲染管线中的特定部分了因为它们运行在GPU上所以节省了宝贵的CPU时间。OpenGL着色器是用<def>OpenGL着色器语言</def>(OpenGL Shading Language, <def>GLSL</def>)写成的,在下一节中我们再花更多时间研究它。
下面,你会看到一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。
@@ -64,7 +64,7 @@
## 顶点输入
开始绘制图形之前,我们必须先给OpenGL输入一些顶点数据。OpenGL是一个3D图形库所以我们在OpenGL中指定的所有坐标都是3D坐标x、y和z。OpenGL不是简单地把**所有的**3D坐标变换为屏幕上的2D像素OpenGL仅当3D坐标在3个轴x、y和z都为-1.0到1.0的范围内时才处理它。所有在所谓的<def>标准化设备坐标</def>(Normalized Device Coordinates)范围内的坐标才会最终呈现在屏幕上(在这个范围以外的坐标不会显示)。
开始绘制图形之前,我们需要先给OpenGL输入一些顶点数据。OpenGL是一个3D图形库所以在OpenGL中我们指定的所有坐标都是3D坐标x、y和z。OpenGL不是简单地把**所有的**3D坐标变换为屏幕上的2D像素OpenGL仅当3D坐标在3个轴x、y和z上-1.0到1.0的范围内时才处理它。所有在这个范围内的坐标叫做<def>标准化设备坐标</def>(Normalized Device Coordinates),此范围内的坐标最终显示在屏幕上(在这个范围以外的坐标不会显示)。
由于我们希望渲染一个三角形我们一共要指定三个顶点每个顶点都有一个3D位置。我们会将它们以标准化设备坐标的形式OpenGL的可见区域定义为一个`float`数组。
@@ -76,7 +76,7 @@ float vertices[] = {
};
```
由于OpenGL是在3D空间中工作的而我们渲染的是一个2D三角形我们将它顶点的z坐标设置为0.0。这样子的话三角形每一点的**深度**(Depth译注2)都是一样的从而使它看上去像是2D的。
由于OpenGL是在3D空间中工作的而我们渲染的是一个2D三角形我们将它顶点的z坐标设置为0.0。这样子的话三角形每一点的*深度*(Depth译注2)都是一样的从而使它看上去像是2D的。
!!! note "译注2"
@@ -92,7 +92,7 @@ float vertices[] = {
与通常的屏幕坐标不同y轴正方向为向上(0, 0)坐标是这个图像的中心,而不是左上角。最终你希望所有(变换过的)坐标都在这个坐标空间中,否则它们就不可见了。
你的标准化设备坐标接着会变换为<def>屏幕空间坐标</def>(Screen-space Coordinates),这是使用你通过<fun>glViewport</fun>函数提供的数据,进行<def>视口变换</def>(Viewport Transform)完成的。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。
通过使用由<fun>glViewport</fun>函数提供的数据,进行<def>视口变换</def>(Viewport Transform)<def>标准化设备坐标</def>(Normalized Device Coordinates)会变换为<def>屏幕空间坐标</def>(Screen-space Coordinates)。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。
定义这样的顶点数据以后我们会把它作为输入发送给图形渲染管线的第一个处理阶段顶点着色器。它会在GPU上创建内存用于储存我们的顶点数据还要配置OpenGL如何解释这些内存并且指定其如何发送给显卡。顶点着色器接着会处理我们在内存中指定数量的顶点。
@@ -127,7 +127,7 @@ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
三角形的位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型最好是<var>GL_STATIC_DRAW</var>。如果,比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是<var>GL_DYNAMIC_DRAW</var>或<var>GL_STREAM_DRAW</var>,这样就能确保显卡把数据放在能够高速写入的内存部分。
现在我们已经把顶点数据储存在显卡的内存中,用<var>VBO</var>这个顶点缓冲对象管理。下面我们会创建一个顶点和片段着色器来真正处理这些数据。现在我们开始着手创建它们吧。
现在我们已经把顶点数据储存在显卡的内存中,用<var>VBO</var>这个顶点缓冲对象管理。下面我们会创建一个顶点着色器和片段着色器来真正处理这些数据。现在我们开始着手创建它们吧。
## 顶点着色器
@@ -407,9 +407,9 @@ glDrawArrays(GL_TRIANGLES, 0, 3);
如果你的输出和这个看起来不一样,你可能做错了什么。去查看一下源码,检查你是否遗漏了什么东西,或者你也可以在评论区提问。
## 索引缓冲对象
## 元素缓冲对象
在渲染顶点这一话题上我们还有最后一个需要讨论的东西——索引缓冲对象(Element Buffer ObjectEBO也叫Index Buffer ObjectIBO)。要解释索引缓冲对象的工作方式最好还是举个例子假设我们不再绘制一个三角形而是绘制一个矩形。我们可以绘制两个三角形来组成一个矩形OpenGL主要处理三角形。这会生成下面的顶点的集合
在渲染顶点这一话题上我们还有最后一个需要讨论的东西——元素缓冲对象(Element Buffer ObjectEBO),也叫索引缓冲对象(Index Buffer ObjectIBO)。要解释元素缓冲对象的工作方式最好还是举个例子假设我们不再绘制一个三角形而是绘制一个矩形。我们可以绘制两个三角形来组成一个矩形OpenGL主要处理三角形。这会生成下面的顶点的集合
```c++
float vertices[] = {
@@ -426,7 +426,7 @@ float vertices[] = {
可以看到,有几个顶点叠加了。我们指定了`右下角`和`左上角`两次一个矩形只有4个而不是6个顶点这样就产生50%的额外开销。当我们有包括上千个三角形的模型之后这个问题会更糟糕这会产生一大堆浪费。更好的解决方案是只储存不同的顶点并设定绘制这些顶点的顺序。这样子我们只要储存4个顶点就能绘制矩形了之后只要指定绘制的顺序就行了。如果OpenGL提供这个功能就好了对吧
很幸运,索引缓冲对象的工作方式正是这样的。和顶点缓冲对象一样,EBO是一个缓冲它专门储存索引OpenGL调用这些顶点的索引来决定绘制哪顶点所谓的<def>索引绘制</def>(Indexed Drawing)正是我们问题的解决方案。首先,我们先要定义(不重复的)顶点,和绘制出矩形所需的索引:
值得庆幸的是,元素缓冲对象的工作方式正是如此。 EBO是一个缓冲区,就像一个顶点缓冲区对象一样,它存储 OpenGL 用来决定绘制哪顶点的索引。这种所谓的<def>索引绘制</def>(Indexed Drawing)正是我们问题的解决方案。首先,我们先要定义(不重复的)顶点,和绘制出矩形所需的索引:
```c++
float vertices[] = {
@@ -436,13 +436,17 @@ float vertices[] = {
-0.5f, 0.5f, 0.0f // 左上角
};
unsigned int indices[] = { // 注意索引从0开始!
unsigned int indices[] = {
// 注意索引从0开始!
// 此例的索引(0,1,2,3)就是顶点数组vertices的下标
// 这样可以由下标代表顶点组合成矩形
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
```
你可以看到当使用索引的时候我们只定义了4个顶点而不是6个。下一步我们需要创建索引缓冲对象:
你可以看到当使用索引的时候我们只定义了4个顶点而不是6个。下一步我们需要创建元素缓冲对象:
```c++
unsigned int EBO;
@@ -456,7 +460,7 @@ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
```
注意的是,我们传递了<var>GL_ELEMENT_ARRAY_BUFFER</var>当作缓冲目标。最后一件要做的事是用<fun>glDrawElements</fun>来替换<fun>glDrawArrays</fun>函数,来指明我们从索引缓冲渲染。使用<fun>glDrawElements</fun>时,我们会使用当前绑定的索引缓冲对象中的索引进行绘制:
注意我们传递了<var>GL_ELEMENT_ARRAY_BUFFER</var>当作缓冲目标。最后一件要做的事是用<fun>glDrawElements</fun>来替换<fun>glDrawArrays</fun>函数,表示我们从索引缓冲渲染三角形。使用<fun>glDrawElements</fun>时,我们会使用当前绑定的索引缓冲对象中的索引进行绘制:
```c++
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
@@ -465,7 +469,7 @@ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
第一个参数指定了我们绘制的模式,这个和<fun>glDrawArrays</fun>的一样。第二个参数是我们打算绘制顶点的个数这里填6也就是说我们一共需要绘制6个顶点。第三个参数是索引的类型这里是<var>GL_UNSIGNED_INT</var>。最后一个参数里我们可以指定EBO中的偏移量或者传递一个索引数组但是这是当你不在使用索引缓冲对象的时候但是我们会在这里填写0。
<fun>glDrawElements</fun>函数从当前绑定到<var>GL_ELEMENT_ARRAY_BUFFER</var>目标的EBO中获取索引。这意味着我们必须在每次要用索引渲染一个物体时绑定相应的EBO还是有点麻烦。不过顶点数组对象同样可以保存索引缓冲对象绑定状态。VAO绑定时正在绑定的索引缓冲对象会被保存为VAO的元素缓冲对象。绑定VAO的同时也会自动绑定EBO。
<fun>glDrawElements</fun>函数从当前绑定到<var>GL_ELEMENT_ARRAY_BUFFER</var>目标的EBO中获取索引。这意味着我们每次想要使用索引渲染对象时都必须绑定相应的EBO有点麻烦。碰巧顶点数组对象也跟踪元素缓冲对象绑定。在绑定VAO时绑定的最后一个元素缓冲对象存为VAO的元素缓冲对象。然后,绑定VAO也会自动绑定EBO。
![](../img/01/04/vertex_array_objects_ebo.png)
@@ -494,7 +498,7 @@ glEnableVertexAttribArray(0);
// ..:: 绘制代码(渲染循环中) :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
```

View File

@@ -20,7 +20,7 @@
- **标准化设备坐标(Normalized Device Coordinates, NDC)** 顶点在通过在剪裁坐标系中剪裁与透视除法后最终呈现在的坐标系。所有位置在NDC下-1.0到1.0的顶点将不会被丢弃并且可见。
- **顶点缓冲对象(Vertex Buffer Object)** 一个调用显存并存储所有顶点数据供显卡使用的缓冲对象。
- **顶点数组对象(Vertex Array Object)** 存储缓冲区和顶点属性状态。
- **索引缓冲对象(Element Buffer Object)** 一个存储索引供索引化绘制使用的缓冲对象。
- **元素缓冲对象(Element Buffer ObjectEBO),也叫索引缓冲对象(Index Buffer ObjectIBO)** 一个存储元素索引供索引化绘制使用的缓冲对象。
- **Uniform** 一个特殊类型的GLSL变量。它是全局的在一个着色器程序中每一个着色器都能够访问uniform变量并且只需要被设定一次。
- **纹理(Texture)** 一种包裹着物体的特殊类型图像,给物体精细的视觉效果。
- **纹理缠绕(Texture Wrapping)** 定义了一种当纹理顶点超出范围(0, 1)时指定OpenGL如何采样纹理的模式。

View File

@@ -83,7 +83,7 @@
- Stride步长
- Offset偏移量
- VAO顶点数组对象Vertex Array Object
- Element Buffer ObjectIndex Buffer ObjectEBOIBO索引缓冲对象
- Element Buffer Object元素缓冲对象EBOIndex Buffer Object索引缓冲对象(IBO)
- Wireframe Mode线框模式
- Stage阶段