From 4ece309afdb6d9547587f89614cc22d567cff802 Mon Sep 17 00:00:00 2001 From: Kang Lin Date: Fri, 13 May 2022 13:43:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=A1=E5=AF=B9=2001-04=20=E4=BD=A0=E5=A5=BD?= =?UTF-8?q?=EF=BC=8C=E4=B8=89=E8=A7=92=E5=BD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/01 Getting started/04 Hello Triangle.md | 34 +++++++++++--------- docs/01 Getting started/10 Review.md | 2 +- glossary.md | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/docs/01 Getting started/04 Hello Triangle.md b/docs/01 Getting started/04 Hello Triangle.md index 956f8ad..70b1e28 100644 --- a/docs/01 Getting started/04 Hello Triangle.md +++ b/docs/01 Getting started/04 Hello Triangle.md @@ -4,7 +4,7 @@ ---|--- 作者 | JoeyDeVries 翻译 | [Django](http://bullteacher.com/), Krasjet, Geequlim -校对 | 暂未校对 +校对 | Kang Lin !!! note "译注" @@ -12,7 +12,7 @@ - 顶点数组对象:Vertex Array Object,VAO - 顶点缓冲对象:Vertex Buffer Object,VBO - - 索引缓冲对象:Element Buffer Object,EBO或Index Buffer Object,IBO + - 元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO 当指代这三个东西的时候,可能使用的是全称,也可能用的是英文缩写,翻译的时候和原文保持的一致。由于没有英文那样的分词间隔,中文全称的部分可能不太容易注意。但请记住,缩写和中文全称指代的是一个东西。 @@ -24,7 +24,7 @@ 图形渲染管线接受一组3D坐标,然后把它们转变为你屏幕上的有色2D像素输出。图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序叫做着色器(Shader)。 -有些着色器允许开发者自己配置,这就允许我们用自己写的着色器来替换默认的。这样我们就可以更细致地控制图形渲染管线中的特定部分了,而且因为它们运行在GPU上,所以它们可以给我们节约宝贵的CPU时间。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的,在下一节中我们再花更多时间研究它。 +有些着色器可以由开发者配置,因为允许用自己写的着色器来代替默认的,所以能够更细致地控制图形渲染管线中的特定部分了。因为它们运行在GPU上,所以节省了宝贵的CPU时间。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的,在下一节中我们再花更多时间研究它。 下面,你会看到一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。 @@ -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的范围内时才处理它。所有在所谓的标准化设备坐标(Normalized Device Coordinates)范围内的坐标才会最终呈现在屏幕上(在这个范围以外的坐标都不会显示)。 +开始绘制图形之前,我们需要先给OpenGL输入一些顶点数据。OpenGL是一个3D图形库,所以在OpenGL中我们指定的所有坐标都是3D坐标(x、y和z)。OpenGL不是简单地把**所有的**3D坐标变换为屏幕上的2D像素;OpenGL仅当3D坐标在3个轴(x、y和z)上-1.0到1.0的范围内时才处理它。所有在这个范围内的坐标叫做标准化设备坐标(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)坐标是这个图像的中心,而不是左上角。最终你希望所有(变换过的)坐标都在这个坐标空间中,否则它们就不可见了。 - 你的标准化设备坐标接着会变换为屏幕空间坐标(Screen-space Coordinates),这是使用你通过glViewport函数提供的数据,进行视口变换(Viewport Transform)完成的。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。 + 通过使用由glViewport函数提供的数据,进行视口变换(Viewport Transform),标准化设备坐标(Normalized Device Coordinates)会变换为屏幕空间坐标(Screen-space Coordinates)。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。 定义这样的顶点数据以后,我们会把它作为输入发送给图形渲染管线的第一个处理阶段:顶点着色器。它会在GPU上创建内存用于储存我们的顶点数据,还要配置OpenGL如何解释这些内存,并且指定其如何发送给显卡。顶点着色器接着会处理我们在内存中指定数量的顶点。 @@ -127,7 +127,7 @@ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 三角形的位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型最好是GL_STATIC_DRAW。如果,比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAWGL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分。 -现在我们已经把顶点数据储存在显卡的内存中,用VBO这个顶点缓冲对象管理。下面我们会创建一个顶点和片段着色器来真正处理这些数据。现在我们开始着手创建它们吧。 +现在我们已经把顶点数据储存在显卡的内存中,用VBO这个顶点缓冲对象管理。下面我们会创建一个顶点着色器和片段着色器来真正处理这些数据。现在我们开始着手创建它们吧。 ## 顶点着色器 @@ -407,9 +407,9 @@ glDrawArrays(GL_TRIANGLES, 0, 3); 如果你的输出和这个看起来不一样,你可能做错了什么。去查看一下源码,检查你是否遗漏了什么东西,或者你也可以在评论区提问。 -## 索引缓冲对象 +## 元素缓冲对象 -在渲染顶点这一话题上我们还有最后一个需要讨论的东西——索引缓冲对象(Element Buffer Object,EBO,也叫Index Buffer Object,IBO)。要解释索引缓冲对象的工作方式最好还是举个例子:假设我们不再绘制一个三角形而是绘制一个矩形。我们可以绘制两个三角形来组成一个矩形(OpenGL主要处理三角形)。这会生成下面的顶点的集合: +在渲染顶点这一话题上我们还有最后一个需要讨论的东西——元素缓冲对象(Element Buffer Object,EBO),也叫索引缓冲对象(Index Buffer Object,IBO)。要解释元素缓冲对象的工作方式最好还是举个例子:假设我们不再绘制一个三角形而是绘制一个矩形。我们可以绘制两个三角形来组成一个矩形(OpenGL主要处理三角形)。这会生成下面的顶点的集合: ```c++ float vertices[] = { @@ -426,7 +426,7 @@ float vertices[] = { 可以看到,有几个顶点叠加了。我们指定了`右下角`和`左上角`两次!一个矩形只有4个而不是6个顶点,这样就产生50%的额外开销。当我们有包括上千个三角形的模型之后这个问题会更糟糕,这会产生一大堆浪费。更好的解决方案是只储存不同的顶点,并设定绘制这些顶点的顺序。这样子我们只要储存4个顶点就能绘制矩形了,之后只要指定绘制的顺序就行了。如果OpenGL提供这个功能就好了,对吧? -很幸运,索引缓冲对象的工作方式正是这样的。和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。所谓的索引绘制(Indexed Drawing)正是我们问题的解决方案。首先,我们先要定义(不重复的)顶点,和绘制出矩形所需的索引: +值得庆幸的是,元素缓冲区对象的工作方式正是如此。 EBO是一个缓冲区,就像一个顶点缓冲区对象一样,它存储 OpenGL 用来决定要绘制哪些顶点的索引。这种所谓的索引绘制(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); ``` -要注意的是,我们传递了GL_ELEMENT_ARRAY_BUFFER当作缓冲目标。最后一件要做的事是用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染。使用glDrawElements时,我们会使用当前绑定的索引缓冲对象中的索引进行绘制: +注意:我们传递了GL_ELEMENT_ARRAY_BUFFER当作缓冲目标。最后一件要做的事是用glDrawElements来替换glDrawArrays函数,表示我们要从索引缓冲区渲染三角形。使用glDrawElements时,我们会使用当前绑定的索引缓冲对象中的索引进行绘制: ```c++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); @@ -465,7 +469,7 @@ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 第一个参数指定了我们绘制的模式,这个和glDrawArrays的一样。第二个参数是我们打算绘制顶点的个数,这里填6,也就是说我们一共需要绘制6个顶点。第三个参数是索引的类型,这里是GL_UNSIGNED_INT。最后一个参数里我们可以指定EBO中的偏移量(或者传递一个索引数组,但是这是当你不在使用索引缓冲对象的时候),但是我们会在这里填写0。 -glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取索引。这意味着我们必须在每次要用索引渲染一个物体时绑定相应的EBO,这还是有点麻烦。不过顶点数组对象同样可以保存索引缓冲对象的绑定状态。VAO绑定时正在绑定的索引缓冲对象会被保存为VAO的元素缓冲对象。绑定VAO的同时也会自动绑定EBO。 +glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的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); ``` diff --git a/docs/01 Getting started/10 Review.md b/docs/01 Getting started/10 Review.md index a2890c5..a791868 100644 --- a/docs/01 Getting started/10 Review.md +++ b/docs/01 Getting started/10 Review.md @@ -20,7 +20,7 @@ - **标准化设备坐标(Normalized Device Coordinates, NDC)**: 顶点在通过在剪裁坐标系中剪裁与透视除法后最终呈现在的坐标系。所有位置在NDC下-1.0到1.0的顶点将不会被丢弃并且可见。 - **顶点缓冲对象(Vertex Buffer Object)**: 一个调用显存并存储所有顶点数据供显卡使用的缓冲对象。 - **顶点数组对象(Vertex Array Object)**: 存储缓冲区和顶点属性状态。 -- **索引缓冲对象(Element Buffer Object)**: 一个存储索引供索引化绘制使用的缓冲对象。 +- **元素缓冲对象(Element Buffer Object,EBO),也叫索引缓冲对象(Index Buffer Object,IBO)**: 一个存储元素索引供索引化绘制使用的缓冲对象。 - **Uniform**: 一个特殊类型的GLSL变量。它是全局的(在一个着色器程序中每一个着色器都能够访问uniform变量),并且只需要被设定一次。 - **纹理(Texture)**: 一种包裹着物体的特殊类型图像,给物体精细的视觉效果。 - **纹理缠绕(Texture Wrapping)**: 定义了一种当纹理顶点超出范围(0, 1)时指定OpenGL如何采样纹理的模式。 diff --git a/glossary.md b/glossary.md index 9f9993b..9adaf53 100644 --- a/glossary.md +++ b/glossary.md @@ -83,7 +83,7 @@ - Stride:步长 - Offset:偏移量 - VAO:顶点数组对象,Vertex Array Object -- Element Buffer Object,Index Buffer Object:EBO,IBO,索引缓冲对象 +- Element Buffer Object:元素缓冲对象(EBO) 或 Index Buffer Object:索引缓冲对象(IBO) - Wireframe Mode:线框模式 - Stage:阶段