mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 04:35:28 +08:00
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*/._.*
|
||||
*.DS_Store
|
129
01 Getting started/01 OpenGL.md
Normal file
129
01 Getting started/01 OpenGL.md
Normal file
@@ -0,0 +1,129 @@
|
||||
本文作者JoeyDeVries,由gjy_1992翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/OpenGL)
|
||||
|
||||
##OpenGL
|
||||
|
||||
未翻译完成...
|
||||
---
|
||||
|
||||
在进行这段旅程之前我们先解释下OpenGL到底是什么。它一般被认为是一个**应用程序编程接口**(API),包含了一系列可以操作图形、画像的方法。然而,OpenGL本身并不是一个API,仅仅是一个规范,由[Khronos组织](http://www.khronos.org/)制定并维护。
|
||||
|
||||
OpenGL规范严格规定了每个函数该如何执行,以及它们该如何返回。至于内部具体每个函数是如何实现的,将由openGL库的开发者自行决定(注:这里开发者是指编写OpenGL库的人)。因为OpenGL规范并没有规定实现的细节,具体的OpenGL库允许使用不同的实现,只要其功能和结果与规范相匹配(亦即,作为用户不会感受到功能上的差异)。
|
||||
|
||||
实际的OpenGL库的开发者通常是显卡的生产商。每个你购买的显卡都会支持特定版本的OpenGL,通常是为一个系列的显卡专门开发的。当你使用苹果系统的时候,OpenGL库是由苹果自身维护的。在Linux下,有显卡生产商提供的OpenGL库,也有一些爱好者改编的版本。这也意味着任何时候OpenGL库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。(快甩锅)
|
||||
|
||||
|
||||
<div style="border:solid #AFDFAF;border-radius:10px;background-color:#D8F5D8;margin:10px 10px 10px 0px;padding:10px">
|
||||
由于大多数实现都是由显卡厂商编写的,当产生一个bug时通常可以通过升级显卡驱动来解决。这些驱动会包括你的显卡能支持的最新版本的OpenGL,这也是为什么总是建议你偶尔更新一下显卡驱动。
|
||||
</div>
|
||||
|
||||
|
||||
Khronos公开主持所有版本的OpenGL规范书的制定。有兴趣的读者可以找到OpenGL3.3(我们将要提到的版本)的[规范书](https://www.opengl.org/registry/doc/glspec33.core.20100311.withchanges.pdf)。如果你想深入到OpenGL的细节(注意只描述函数的功能而不管实现),这是个很好的选择。该规范还提供一个强大的可以寻找到每个函数具体功能的参考。
|
||||
|
||||
##对比核心模式和立即渲染模式
|
||||
|
||||
早期的OpenGL使用立即渲染模式(固定渲染管线),这个模式下绘制图形很方便。OpenGl的大多数功能都被隐藏起来,开发者很少能控制OpenGL如何进行计算。开发者最终希望更多的灵活性。随着时间推移,规范越来越灵活,开发者也能更多的控制绘图细节。立即渲染模式确实容易使用和理解,但是效率太低。因此从OpenGL3.2开始,规范书开始废弃立即渲染模式,推行核心模式,这个模式完全移除了旧的特性。
|
||||
|
||||
当使用核心模式时,OpenGL迫使我们使用现代的做法。当我们试图使用一个废弃的函数时,OpenGL会抛出一个错误并终止绘图。现代做法的优势是更高的灵活性和效率,然而也更难于学习。立即渲染模式从OpenGL实际操作中抽象掉了很多细节,因而它易于学习的同时,也很难去把握OpenGL具体是如何操作的。现代做法要求使用者真正理解OpenGL和图形编程,它有一些难度,然而提供了更多的灵活性,更高的效率,更重要的可以更深入的理解图形编程。
|
||||
|
||||
这也是为什么我们的教程面向OpenGL3.3的核心模式。虽然上手更困难,但是值得去努力。
|
||||
|
||||
现今更高版本的OpenGL已经发布(目前最新是4.5),你可能会问:为什么我们还要学习3.3?答案很简单,所有OpenGL的更高的版本都是在3.3的基础上,添加了额外的功能,并不更改核心架构。新版本只是引入了一些更有效率或更有用的方式去完成同样的功能。因此所有的概念和技术在现代OpenGL版本里都保持一致。当你的经验足够,你可以轻松使用来自更高版本OpenGL的新特性。
|
||||
|
||||
|
||||
<div style="border:solid #E1B3B3;border-radius:10px;background-color:#FFD2D2;margin:10px 10px 10px 0px;padding:10px">
|
||||
当使用新版本OpenGL的新特性时,只有新一代的显卡能够支持你的应用程序。这也是为什么大多数开发者基于较低版本的OpenGL编写程序,并有选择的启用新特性。
|
||||
</div>
|
||||
|
||||
|
||||
在有些教程里你会发现像如下方式注明的新特性。
|
||||
|
||||
##扩展
|
||||
|
||||
OpenGL的一大特性就是对扩展的支持,当一个显卡公司提出一个新特性或者渲染上的大优化,通常会以扩展的方式在驱动中实现。如果一个程序在支持这个扩展的显卡上运行,开发者可以使用这个扩展提供的一些更先进更有效的图形功能。通过这种方式,开发者不必等待一个新的OpenGL规范面世,就可以方便的检查显卡是否支持此扩展。通常,当一个扩展非常流行或有用的时候,它将最终成为未来的OpenGL规范的一部分。
|
||||
|
||||
使用扩展的代码大多看上去如下:
|
||||
|
||||
```c++
|
||||
if(GL_ARB_extension_name)
|
||||
{
|
||||
// 使用一些新的特性
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不支持此扩展: 用旧的方式去做
|
||||
}
|
||||
```
|
||||
|
||||
使用OpenGL3.3时,我们很少需要使用扩展来完成大多数功能,但是掌握这种方式是必须的。
|
||||
|
||||
##状态机
|
||||
|
||||
OpenGL自身是一个巨大的状态机:一个描述OpenGL该如何操作的所有变量的大集合。OpenGL的状态通常被称为OpenGL**上下文**(context)。我们通常使用如下途径去更改OpenGL状态:设置一些选项,操作一些缓存。最后,我们使用当前OpenGl上下文来渲染。
|
||||
|
||||
假设当我们想告诉OpenGL去画线而不是三角形的时候,我们通过改变一些上下文变量来改变OpenGL状态,从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会画出线段而不是三角形。
|
||||
|
||||
用OpenGL工作时,我们会遇到一些**改变OpenGL工作状态的函数**(state-changing function),以及一些在这些状态的基础上**执行操作的函数**(state-using function)。只要你记住OpenGL本质上是个大状态机,就能更容易理解它的大部分特性。
|
||||
|
||||
##对象(Object)
|
||||
|
||||
OpenGL库是用C写的,同时也支持多种语言的派生,但是核心是一个C库。一些C语言的结构不易被翻译到其他高层语言,因此OpenGL设计的时候引入了一些抽象概念。“对象”就是其中一个。
|
||||
|
||||
在OpenGL中一个对象是指一些选项的集合,代表OpenGL状态的一个子集。比如,我们可以有一个对象来代表绘图窗口的设置,可以设置它的大小、支持的颜色位数等等。可以把对象看做一个C风格的结构体:
|
||||
|
||||
```c++
|
||||
struct object_name {
|
||||
GLfloat option1;
|
||||
GLuint option2;
|
||||
GLchar[] name;
|
||||
};
|
||||
```
|
||||
|
||||
<div style="border:solid #AFDFAF;border-radius:10px;background-color:#D8F5D8;margin:10px 10px 10px 0px;padding:10px">
|
||||
<span style="font-size:20px"><b>原始类型</b></span>
|
||||
<br/>
|
||||
使用OpenGL时,建议使用OpenGL定义的原始类型。比如使用float时我们加上前缀GL(因此写作GLfloat)。int,uint,char,bool等等类似。OpenGL定义了这些GL原始类型的平台无关的内存排列方式。而int等原始类型在不同平台上可能有不同的内存排列方式。使用GL原始类型可以保证你的程序在不同的平台上工作一致。
|
||||
</div>
|
||||
|
||||
|
||||
当我们使用一个对象时,通常看起来像如下一样(把OpenGL上下文比作一个大的结构体):
|
||||
|
||||
```c++
|
||||
// OpenGL状态
|
||||
struct OpenGL_Context {
|
||||
...
|
||||
object* object_Window_Target;
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
```c++
|
||||
// 创建一个对象
|
||||
GLuint objectId = 0;
|
||||
glGenObject(1, &objectId);
|
||||
// 绑定对象至上下文
|
||||
glBindObject(GL_WINDOW_TARGET, objectId);
|
||||
// 设置GL_WINDOW_TARGET对象的一些选项
|
||||
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
|
||||
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
|
||||
// 将上下文的GL_WINDOW_TARGET对象设回默认
|
||||
glBindObject(GL_WINDOW_TARGET, 0);
|
||||
```
|
||||
|
||||
这一小片代码将会是以后使用OpenGl时常见的工作流。我们首先创建一个对象,然后用一个id保存它的引用(实际数据被储存在后台)。然后我们将对象绑定至上下文的目标位置(例子里窗口对象的目标位置被定义成GL\_WINDOW\_TARGET)。接下来我们设置窗口的选项,最后我们解绑这个对象,通过将目标位置的对象id设回0。我们设置的选项被保存在objectId代表的对象中,一旦我们重新绑定这个对象到GL\_WINDOW\_TARGET位置,这些选项就会重新生效。
|
||||
|
||||
|
||||
<div style="border:solid #E1B3B3;border-radius:10px;background-color:#FFD2D2;margin:10px 10px 10px 0px;padding:10px">
|
||||
目前提供的示例代码只是OpenGL如何操作的一个大致描述,通过教程你会遇到很多实际的例子。
|
||||
</div>
|
||||
|
||||
|
||||
使用对象的一个好事是我们在程序中可以定义不止一个对象,设置他们的选项,当我们需要进行一个操作的时候,只需要绑定预设了需要的状态的对象。比如,有作为3D模型数据(一栋房子或一个人物)容器对象的对象,任何时候我们想绘制其中一个3D模型的时候,只需绑定相应的含模型数据的对象(我们预先创建并设置好了它们的选项)。拥有数个这样的对象允许我们指定多个模型,在想画其中任何一个的时候,简单的将相应的对象绑定上去,不需要进行重复的设置选项的操作。
|
||||
|
||||
##让我们开始吧
|
||||
|
||||
你现在已经知道一些OpenGL的相关知识了,包括OpenGL作为规范和库,OpenGL大致的操作流程,以及一些使用扩展的小技巧。不要担心没有完全消化它们,通过这个教程我们会仔细讲解每一步,你会通过足够的例子来把握OpenGL。如果你已经做好了下一步的准备,我们可以开始建立OpenGL上下文以及我们的第一个窗口了。[点击这里](http://www.learnopengl.com/#!Getting-started/Creating-a-window)(第一章第二节)
|
||||
|
||||
##额外的资源
|
||||
|
||||
- [opengl.org](https://www.opengl.org/): OpenGL官方网站.
|
||||
- [OpenGL registry](https://www.opengl.org/registry/): OpenGL各版本的规范和扩展的主站。
|
18
01 Getting started/03 Hello Window.md
Normal file
18
01 Getting started/03 Hello Window.md
Normal file
@@ -0,0 +1,18 @@
|
||||
本文作者JoeyDeVries,由Geequlim翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/Hello-Window)
|
||||
|
||||
上一节中我们获取到了GLFW和GLEW这两个开源库,现在我们就可以使用它们来我们创建一个OpenGL绘图窗口了。首先,新建一个 .cpp文件,然后把下面的代码粘贴到该文件的最前面。注意为之所以定义GLEW_STATIC宏,是因为我们使用GLEW的静态链接库。
|
||||
|
||||
// GLEW
|
||||
#define GLEW_STATIC
|
||||
#include <GL/glew.h>
|
||||
// GLFW
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
|
||||
|
||||
> Be sure to include GLEW before GLFW. The include file for GLEW contains the correct OpenGL header includes (like GL/gl.h) so including GLEW before other header files that require OpenGL does the trick.
|
||||
|
||||
> 请确认在包含GLFW头文件之前包含了GLEW,
|
||||
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
本文作者JoeyDeVries,由Geequlim翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/Hello-Triangle)
|
||||
本文作者JoeyDeVries,由Cocooshu翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/Hello-Triangle)
|
||||
|
||||
##你好,三角形
|
||||
|
||||
@@ -6,11 +6,31 @@
|
||||
---
|
||||
|
||||
由于OpenGL使用的是三维坐标系而我们的屏幕和窗口都是由二维像素组成的,所以OpenGL的一项重要工作就是将三维坐标系转换到二维像素使得我们的屏幕能够显示显示这些数据。将三维坐标转换为二维坐标的过程使用OpenGL的图形渲染管线来管理。图形渲染管线可以被分为两大部分:
|
||||
1. 将三维坐标系转换为二维的屏幕坐标
|
||||
2. 将转换得到的二维坐标中的每一个点转换为具体的像素颜色
|
||||
|
||||
|
||||
* 将三维坐标系转换为二维的屏幕坐标
|
||||
* 将转换得到的二维坐标中的每一个点转换为具体的像素颜色
|
||||
|
||||
|
||||
在本教程中我们将简单的讨论OpenGL的渲染管线并使用它来创建一些给力的像素图。
|
||||
二维坐标系与像素坐标系有一点不同,一个二维坐标描述的是在二维空间中一个确切点的位置,而一个二维像素是一个由二维坐标和屏幕的分辨率所决定的近似值.
|
||||
|
||||
小提示
|
||||
|
||||
二维坐标系与像素坐标系有一点不同,一个二维坐标描述的是在二维空间中一个确切点的位置,而一个二维像素是一个由二维坐标和屏幕的分辨率所决定的近似值。
|
||||
|
||||
图形渲染管线将一系列的三维坐标转化为你屏幕中的像素颜色。图形渲染管线可以被分为若干个部分,每一个部分都需要上一部分的输出数据作为其输入数据。所有部分都是高效运行并且可以是同时进行的,由于这一并行的机制,现代的图形处理器(GPU)拥有成千上万个核心通过快速地执行一些小程序来处理渲染管线中每一部分的数据。这些小程序被称为着色器(Shader)。
|
||||
许多着色器都允许开发者进行配置,OpenGL允许我们写自己的着色器来替换已经存在的默认着色器。由于着色器程序是使用GPU执行的,这样我们就可以通过书写自己的着色器程序来对渲染进行更加细微的控制,也能减轻CPU的运算量。OpenGL的着色器程序使用GLSL(OpenGL Shading Language)语言编写,我们将在接下来的教程中探索如何使用它。
|
||||
下图是对渲染管线流程简介,其中蓝色的部分允许我们编写自己的着色器程序进行控制。
|
||||
(img)
|
||||
|
||||
许多着色器都允许开发者进行配置,OpenGL允许我们写自己的着色器来替换已经存在的默认着色器。由于着色器程序是使用GPU执行的,这样我们就可以通过书写自己的着色器程序来对渲染进行更加细微的控制,也能减轻CPU的运算压力。OpenGL的着色器程序使用GLSL(OpenGL Shading Language)语言编写,我们将在接下来的教程中探索如何使用它。
|
||||
下图是对渲染管线流程简介,其中蓝色的部分允许我们编写自己的着色器程序。
|
||||
|
||||

|
||||
|
||||
正如你所看到的,图形渲染管线把顶点数据最终转化为了像素图形。图形渲染管线中包含了许多个部分,每一个部分处理这个转化过程中的一部分特定的工作。我们将简要地为您解释图形渲染管线中重要部分是如何运作的。
|
||||
|
||||
我们将一组能构成一个三角形的三个三维坐标的数组作为图形渲染管线的输入数据,这组输入数据被称之为顶点数据(Vertex Data),它是许多顶点的集合,每一个顶点都表示三维空间中都一个点。这个顶点数据不止用于表示空间坐标,它还能代表你想输入渲染管线的一切信息。简单起见,我们让每个顶点包含一个顶点的三维坐标和一些颜色值。
|
||||
|
||||
小提示:
|
||||
|
||||
为了让OpenGL知道你的顶点坐标和颜色数据是用来做什么的,OpenGL要求你告诉他你需要用这些顶点数据构成什么图形。我们是想要将传入的顶点数据呈现为一些点还是一些三角形或者只是一组线段?这些给出的渲染类型提示被称之为原型(Primitive),让OpenGL执行任何绘制操作时都需要告诉它我们想要的原型。例如其中一些原型:GL_POINTS, GL_TRIANGLES and GL_LINE_STRIP。
|
||||
|
||||
渲染管线的第一部分是顶点着色器(Vertex Shader),它每次将一个顶点数据作为输入源。使用顶点着色器的主要目的是将输入的三维空间坐标转换为另一个三维的空间坐标(看似很傻逼的,我们后面再详细讨论这个问题),同时顶点着色器也允许我们对顶点数据数据做一些其他的处理操作。
|
6
01 Getting started/05 Shaders.md
Normal file
6
01 Getting started/05 Shaders.md
Normal file
@@ -0,0 +1,6 @@
|
||||
本文作者JoeyDeVries,由codeman001翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/Shaders)
|
||||
|
||||
##Shaders
|
||||
|
||||
未翻译完成...
|
||||
---
|
44
01 Getting started/07 Transformations.md
Normal file
44
01 Getting started/07 Transformations.md
Normal file
@@ -0,0 +1,44 @@
|
||||
本文作者JoeyDeVries,由Meow J翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/Shaders)
|
||||
|
||||
##变换(Transformations)
|
||||
|
||||
尽管我们现在已经知道了如何创建物体,着色,与加入纹理,他们仍然是静态的,还不是那么有趣. 我们可以尝试着在每一帧改变物体的顶点并且重设缓冲区从而使他们移动,但这太繁琐了,而且会消耗很多的处理时间. 然而,我们现在有一个更好的解决方案,那就是使用(多个)矩阵来对这个物体进行**变换**(Transform).
|
||||
|
||||
**矩阵**(Matrix)是一种非常有用的数学工具,尽管听起来可能有些吓人. 不过一旦你适应了使用它们,你会发现它们非常的有用. 在讨论矩阵的过程中,我们必须使用到一些数学知识,如果你对这块的数学非常感兴趣,我会提供给你提供额外的拓展资源.
|
||||
|
||||
为了深入了解变换,我们需要在讨论矩阵之前先来深入探讨**矢量**(Vector). 这一节的目标是让你拥有将来需要的最基础的数学背景知识. 如果你发现这节十分困难,尽量去理解吧. 你以后遇见问题也可以回来复习这节的概念.
|
||||
|
||||
##矢量(Vectors)
|
||||
|
||||
最简单的来定义,矢量表示一个方向. 更正式的说,矢量同时拥有**方向** (Direction)和**大小** (Magnitude). 你可以想象一下,把矢量当做藏宝图的指示:左走10步,之后向北走3步,再向右走5步. 在这个例子当中,“左”就是方向,“10步”就是这个矢量的大小. 你可以发现,这个藏宝图的指示一共有三个矢量. 矢量可以在任意**维度**(Dimension)上,但我们最常使用的是从二维到四维. 如果一个矢量有两个维度,它就表示在平面上的一个方向;如果一个矢量有三个维度,他就能表示在3D世界中的一个方向.
|
||||
|
||||
下图中你可以看到三个矢量,每一个矢量都由一个箭头表示,并且记为(x,y). 我们在2D图片中展示这些矢量,因为这样子会更直观. 你仍然可以把这些2D矢量当做z坐标为0的3D矢量. 因为矢量代表着一个方向,矢量原点的不同**并不会**改变它的值. 在图像中,矢量和是**相等**的,尽管他们的原点不一样.
|
||||
|
||||

|
||||
|
||||
当表示一个矢量的时候,通常我们使用在字符上面加一道杠的方法来表示,比如. 在公式中是这样表示的:
|
||||
|
||||

|
||||
|
||||
因为矢量是方向,所以有些时候会很难形象地用位置(Position)表示出来. 我们通常设定这个方向的原点为(0,0,0),然后指向对应坐标的点,使其变为位置矢量(Position Vector)来表示(我们也可以定义一个不同的原点并认为其从该原点的空间指向那个点). 位置矢量(3,5)将在图像中从原点(0,0)指向点(3,5). 因此,我们可以使用矢量在2D或3D空间中表示方向**与**位置.
|
||||
|
||||
|
||||
就像普通的数字一样,我们也可以给矢量定义一些运算(你可能已经知道一些了).
|
||||
|
||||
### 矢量与标量运算(Scalar Vector Operations)
|
||||
|
||||
**标量**(Scalar)仅仅是一个数字(或者说是仅有一个分量的矢量). 当使用标量对矢量进行加/减/乘/除运算时,我们可以简单地对该矢量的每一个元素进行该运算. 对于加法来说会像这样:
|
||||
|
||||

|
||||
|
||||
其中的+可以是+/-/×/÷,注意-和÷运算时不能颠倒,颠倒的运算是没有定义的(标量-/÷矢量)
|
||||
|
||||
### 矢量的取反(Vector Negation)
|
||||
|
||||
对一个矢量取反会将其方向逆转,比如说一个指向东北方向的矢量取反后将会指向西南方向. 我们在一个矢量的每个分量前加负号从而实现取反(或者说用-1数乘该矢量):
|
||||
|
||||

|
||||
|
||||
### 矢量的加与减(Addition and Subtraction)
|
||||
|
||||
#WIP
|
3
01 Getting started/08 Coordinate Systems.md
Normal file
3
01 Getting started/08 Coordinate Systems.md
Normal file
@@ -0,0 +1,3 @@
|
||||
本文作者JoeyDeVries,由linkoln翻译自[http://learnopengl.com](http://learnopengl.com/#!Getting-started/Coordinate-Systems)
|
||||
|
||||
##Coordinate Systems
|
59
03 Model Loading/01 Assimp.md
Normal file
59
03 Model Loading/01 Assimp.md
Normal file
@@ -0,0 +1,59 @@
|
||||
<style>
|
||||
.centerHV{text-align:center}
|
||||
</style>
|
||||
|
||||
本文作者JoeyDeVries,由Cocoonshu翻译自[http://learnopengl.com](http://learnopengl.com/#!Model-Loading/Assimp)
|
||||
|
||||
## Assimp (Assimp开源模型导入库)
|
||||
|
||||
到目前为止,我们已经在所有的场景中大面积滥用了我们的容器盒小盆友,但就是容器盒是我们的好朋友,时间久了我们也会喜新厌旧。一些图形应用里经常会使用很多复杂且好玩儿的模型,它们看起来比静态的容器盒可爱多了。但是,我们无法像定义容器盒一样手动地去指定房子、货车或人形角色这些复杂模型的顶点、法线和纹理坐标。我们需要做的也是应该要做的,是把这些模型导入到应用程序中,而设计制作这些3D模型的工作应该交给像Blender、3DS Max或者Maya这样的工具软件。
|
||||
|
||||
那些3D建模工具,可以让美工们构建一些复杂的形状,并将贴图应用到形状上去,即纹理映射。然后,在导出模型文件时,建模工具会自己生成所有的顶点坐标、顶点法线和纹理坐标。这样,美工们可以不用了解大量的图像技术细节,就能有大量的工具集去随心地构建高品质的模型。所有的技术细节内容都隐藏在里导出的模型文件里。而我们,这些图形开发者,就必须得去关注这些技术细节了。
|
||||
|
||||
因此,我们的工作就是去解析这些导出的模型文件,并将其中的模型数据存储为OpenGL能够使用的数据。一个常见的问题是,导出的模型文件通常有几十种格式,不同的工具会根据不同的文件协议把模型数据导出到不同格式的模型文件中。有的模型文件格式只包含模型的静态形状数据和颜色、漫反射贴图、高光贴图这些基本的材质信息,比如Wavefront的.obj文件。而有的模型文件则采用XML来记录数据,且包含了丰富的模型、光照、各种材质、动画、摄像机信息和完整的场景信息等,比如Collada文件格式。Wavefront的obj格式是为了考虑到通用性而设计的一种便于解析的模型格式。建议去Wavefront的Wiki上看看obj文件格式是如何封装的。这会给你形成一个对模型文件格式的一个基本概念和印象。
|
||||
|
||||
## 模型加载库
|
||||
|
||||
现在市面上有一个很流行的模型加载库,叫做Assimp,全称为Open Asset Import Library。Assimp可以导入几十种不同格式的模型文件(同样也可以导出部分模型格式)。只要Assimp加载完了模型文件,我们就可以从Assimp上获取所有我们需要的模型数据。Assimp把不同的模型文件都转换为一个统一的数据结构,所有无论我们导入何种格式的模型文件,都可以用同一个方式去访问我们需要的模型数据。
|
||||
|
||||
当导入一个模型文件时,即Assimp加载一整个包含所有模型和场景数据的模型文件到一个scene对象时,Assimp会为这个模型文件中的所有场景节点、模型节点都生成一个具有对应关系的数据结构,且将这些场景中的各种元素与模型数据对应起来。下图展示了一个简化的Assimp生成的模型文件数据结构:
|
||||
|
||||
<div class="centerHV">
|
||||
<img src="http://learnopengl.com/img/model_loading/assimp_structure.png"/>
|
||||
</div>
|
||||
|
||||
- 所有的模型、场景数据都包含在scene对象中,如所有的材质和模型点云。同样,场景的根节点引用也包含在这个scene对象中
|
||||
- 场景的根节点可能也会包含很多子节点和一个指向保存模型点云数据mMeshes[]的索引集合。根节点上的mMeshes[]里保存了实际了Mesh对象,而每个子节点上的mMesshes[]都只是指向根节点中的mMeshes[]的一个引用(译者注:C/C++称为指针,Java/C#称为引用)
|
||||
- 一个Mesh(模型点云)对象本身包含渲染所需的所有相关数据,比如顶点位置、法线向量、纹理坐标、面片及物体的材质
|
||||
- 一个Mesh(模型点云)会包含多个面片。一个Face(面片)表示渲染中的一个最基本的形状单位,即图元(基本图元有点、线、三角面片、矩形面片)。一个面片记录了一个图元的顶点索引,通过这个索引,可以在mMeshes[]中寻找到对应的顶点位置数据。顶点数据和索引分开存放,可以便于我们使用缓存(VBO、NBO、TBO、IBO)来高速渲染物体。(详见[Hello Triangle](http://www.learnopengl.com/#!Getting-started/Hello-Triangle))
|
||||
- 一个Mesh(模型点云)还会包含一个Material(材质)对象用于指定物体的一些材质属性。如颜色、纹理贴图(漫反射贴图、高光贴图等)
|
||||
|
||||
所以我们要做的第一件事,就是加载一个模型文件为scene对象,然后获取每个节点对应的Mesh对象(我们需要递归搜索每个节点的子节点来获取所有的节点),并处理每个Mesh对象对应的顶点数据、索引以及它的材质属性。最终我们得到一个只包含我们需要的数据的Mesh集合。
|
||||
|
||||
> ##Mesh(模型点云)
|
||||
> 用建模工具构建物体时,美工通常不会直接使用单个形状来构建一个完整的模型。一般来说,一个模型会由几个子模型/形状组合拼接而成。而模型中的那些子模型/形状就是我们所说的一个Mesh。例如一个人形模型,美工通常会把头、四肢、衣服、武器这些组件都分别构建出来,然后在把所有的组件拼合在一起,形成最终的完整模型。一个Mesh(包含顶点、索引和材质属性)是我们在OpenGL中绘制物体的最小单位。一个模型通常有多个Mesh组成。
|
||||
|
||||
下一节教程中,我们将用上述描述的数据结构来创建我们自己的Model类和Mesh类,用于加载和保存那些导入的模型。如果我们想要绘制一个模型,我们不会去渲染整个模型,而是去渲染这个模型所包含的所有独立的Mesh。不管怎样,我们开始导入模型之前,我们需要先把Assimp导入到我们的工程中。
|
||||
|
||||
## 构建Assimp
|
||||
|
||||
你可以在[Assimp的下载页面](http://assimp.sourceforge.net/main_downloads.html)选择一个想要的版本去下载Assimp库。到目前为止,Assimp可用的最新版本是3.1.1。我们建议你自己编译Assimp库,因为Assimp官方的已编译库不能很好地覆盖在所有平台上运行。如果你忘记怎样使用CMake编译一个库,请详见[Creating a window(创建一个窗口)](http://www.learnopengl.com/#!Getting-started/Creating-a-window)教程。
|
||||
|
||||
这里我们列出一些编译Assimp时可能遇到的问题,以便大家参考和排除:
|
||||
|
||||
- CMake在读取配置列表时,报出与DirectX库丢失相关的一些错误。报错如下:
|
||||
<pre> Could not locate DirecX
|
||||
CMake Error at cmake-modules/FindPkgMacros.cmake:110 (message):
|
||||
Required library DirectX not found! Install the library (including dev packages) and try again. If the library is already installed, set the missing variables manually in cmake. </pre>
|
||||
这个问题的解决方案:如果你之前没有安装过DirectX SDK,那么请安装。下载地址:[DirectX SDK](http://www.microsoft.com/en-us/download/details.aspx?id=6812)
|
||||
- 安装DirectX SDK时,可以遇到一个错误码为<b>S1023</b>的错误。遇到这个问题,请在安装DirectX SDK前,先安装C++ Redistributable package(s)。
|
||||
问题解释:[已知问题:DirectX SDK (June 2010) 安装及S1023错误](Known Issue: DirectX SDK (June 2010) Setup and the S1023 error)
|
||||
- 一旦配置完成,你就可以生成解决方案文件了,打开解决方案文件并编译Assimp库(编译为Debug版本还是Release版本,根据你的需要和心情来定吧)
|
||||
- 使用默认配置构建的Assimp是一个动态库,所以我们需要把编译出来的assimp.dll文件拷贝到我们自己程序的可执行文件的同一目录里
|
||||
- 编译出来的Assimp的LIB文件和DLL文件可以在code/Debug或者code/Release里找到
|
||||
- 把编译好的LIB文件和DLL文件拷贝到工程的相应目录下,并链接到你的解决方案中。同时还好记得把Assimp的头文件也拷贝到工程里去(Assimp的头文件可以在include目录里找到)
|
||||
|
||||
如果你还遇到了其他问题,可以在下面给出的链接里获取帮助。
|
||||
>如果你想要让Assimp使用多线程支持来提高性能,你可以使用<b>Boost</b>库来编译 Assimp。在[Boost安装页面](http://assimp.sourceforge.net/lib_html/install.html),你能找到关于Boost的完整安装介绍。
|
||||
|
||||
现在,你应该已经能够编译Assimp库,并链接Assimp到你的工程里去了。下一节内容:[导入完美的3D物件!](http://www.learnopengl.com/#!Model-Loading/Mesh)
|
3
04 Advanced OpenGL/01 Depth testing.md
Normal file
3
04 Advanced OpenGL/01 Depth testing.md
Normal file
@@ -0,0 +1,3 @@
|
||||
本文作者JoeyDeVries,由unnamed翻译自[http://learnopengl.com](http://learnopengl.com/#!Advanced-OpenGL/Depth-testing)
|
||||
|
||||
##深度测试
|
22
README.md
22
README.md
@@ -1,22 +1,8 @@
|
||||
# LearnOpenGL-CN
|
||||
learnopengl.com系列教程的中文翻译,目前正在翻译中。
|
||||
|
||||
##此项目已经搬家到 [LearnOpenGL-CN组织](https://github.com/LearnOpenGL-CN/LearnOpenGL-CN)
|
||||
####为了方便大家提交文章我们华丽丽地升级成了组织,加入翻译组后不在需要无限地fork提交合并啦!
|
||||
|
||||
欢迎志愿者们加入翻译组交流QQ群:383745868
|
||||
---
|
||||
###翻译组成员请注意
|
||||
|
||||
####关于如何认领翻译?
|
||||
|
||||
由于我们的志愿者来自五湖四海,
|
||||
为了避免冲突。请志愿者们先fork Geequlim/LearnOpenGL-CN。同步到本地后找到要翻译的文章,创建一个如下所示的只包含作者、翻译者和原文链接信息的markdown文件并提交发布请求:
|
||||
|
||||
本文作者JoeyDeVries,由Geequlim翻译自http://learnopengl.com
|
||||
|
||||
译文的文件命名统一规范为:
|
||||
|
||||
<两位数的章序列> <章名称>/<两位数节序列> 节名称/<两位数小节序列> 小节名称.md
|
||||
|
||||
例如:
|
||||
|
||||
05 Advanced Lighting/03 Shadows/02 Point Shadows.md
|
||||
|
||||
欢迎志愿者们加入翻译组交流QQ群:383745868
|
Reference in New Issue
Block a user