mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 04:35:28 +08:00
Fix error in chapter 03
This commit is contained in:
@@ -53,13 +53,18 @@ 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.
|
||||
```
|
||||
|
||||
这个问题的解决方案:如果你之前没有安装过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错误](https://blogs.msdn.microsoft.com/chuckw/2011/12/09/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目录里找到)
|
||||
这个问题的解决方案:如果你之前没有安装过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错误](https://blogs.msdn.microsoft.com/chuckw/2011/12/09/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目录里找到)。
|
||||
|
||||
如果你还遇到了其他问题,可以在下面给出的链接里获取帮助。
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
先来复习一点目前学到知识,考虑一个网格最少需要哪些数据。一个网格应该至少需要一组顶点,每个顶点包含一个位置向量,一个法线向量,一个纹理坐标向量。一个网格也应该包含一个索引绘制用的索引,以纹理(diffuse/specular map)形式表现的材质数据。
|
||||
|
||||
为了在OpenGL中定义一个顶点,现在我们设置有最少需求一个网格类:
|
||||
现在,为了在OpenGL中设置一个满足最低需求的网格类,我们定义一个顶点:
|
||||
|
||||
|
||||
```c++
|
||||
@@ -33,7 +33,7 @@ struct Texture
|
||||
};
|
||||
```
|
||||
|
||||
我们储存纹理的id和它的类型,比如`diffuse`纹理或者`specular`纹理。
|
||||
我们储存纹理的id和它的类型,比如漫反射贴图或者镜面贴图。
|
||||
|
||||
知道了顶点和纹理的实际表达,我们可以开始定义网格类的结构:
|
||||
|
||||
@@ -75,7 +75,7 @@ Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures)
|
||||
|
||||
## 初始化
|
||||
|
||||
现在我们有一大列的网格数据可用于渲染,这要感谢构造函数。我们确实需要设置合适的缓冲,通过顶点属性指针(vertex attribute pointers)定义顶点着色器layout。现在你应该对这些概念很熟悉,但是我们我们通过介绍了结构体中使用顶点数据,所以稍微有点不一样:
|
||||
现在我们有一大列的网格数据可用于渲染,这要感谢构造函数。我们确实需要设置合适的缓冲,通过顶点属性指针(vertex attribute pointers)定义顶点着色器layout。现在除了将顶点数据传入结构体以外你应该对其它概念很熟悉:
|
||||
|
||||
|
||||
```c++
|
||||
@@ -112,7 +112,7 @@ void setupMesh()
|
||||
}
|
||||
```
|
||||
|
||||
如你所想代码没什么特别不同的地方,在`Vertex`结构体的帮助下有了一些小把戏。
|
||||
这里的代码和你设想的没什么特别不同的地方,但是向`Vertex`结构体传入数据需要有一些小技巧。
|
||||
|
||||
C++的结构体有一个重要的属性,那就是在内存中它们是连续的。如果我们用结构体表示一列数据,这个结构体只包含结构体的连续的变量,它就会直接转变为一个`float`(实际上是byte)数组,我们就能用于一个数组缓冲(array buffer)中了。比如,如果我们填充一个`Vertex`结构体,它在内存中的排布等于:
|
||||
|
||||
@@ -150,7 +150,7 @@ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
|
||||
我们需要为`Mesh`类定义的最后一个函数,是它的Draw函数。在真正渲染前我们希望绑定合适的纹理,然后调用`glDrawElements`。可因为我们从一开始不知道这个网格有多少纹理以及它们应该是什么类型的,所以这件事变得很困难。所以我们该怎样在着色器中设置纹理单元和采样器呢?
|
||||
|
||||
解决这个问题,我们需要假设一个特定的名称惯例:每个`diffuse`纹理被命名为`texture_diffuseN`,每个`specular`纹理应该被命名为`texture_specularN`。N是一个从1到纹理才抢其允许使用的最大值之间的数。可以说,在一个网格中我们有3个`diffuse`纹理和2个`specular`纹理,它们的纹理采样器应该这样被调用:
|
||||
解决这个问题,我们需要假设一个特定的名称惯例:每个漫反射贴图被命名为`texture_diffuseN`,每个镜面贴图应该被命名为`texture_specularN`。N是一个从1到纹理采样器允许使用的最大值之间的数。比如说,在一个网格中我们有3个漫反射贴图和2个镜面贴图,它们的纹理采样器应该在这之后被调用:
|
||||
|
||||
|
||||
```c++
|
||||
@@ -197,7 +197,7 @@ void Draw(Shader shader)
|
||||
}
|
||||
```
|
||||
|
||||
这不是最漂亮的代码,但是这主要归咎于C++转换类型时的丑陋,比如`int`转`string`时。我们首先计算N-元素每个纹理类型,把它链接到纹理类型字符串来获取合适的uniform名。然后查找合适的采样器位置,给它位置值对应当前激活纹理单元,绑定纹理。这也是我们需要在`Draw`方法是用`shader`的原因。我们添加`material.`到作为结果的uniform名,因为我们通常把纹理储存进材质结构体(对于每个实现也许会有不同)。
|
||||
这不是最漂亮的代码,但是这部分归咎于C++转换类型时的丑陋,比如`int`转`string`时。我们首先计算N-元素每个纹理类型,把它链接到纹理类型字符串来获取合适的uniform名。然后查找合适的采样器位置,给它位置值对应当前激活纹理单元,绑定纹理。这也是我们需要在`Draw`方法是用`shader`的原因。我们添加`material.`到作为结果的uniform名,因为我们通常把纹理储存进材质结构体(对于每个实现也许会有不同)。
|
||||
|
||||
!!! Important
|
||||
|
||||
@@ -205,4 +205,4 @@ void Draw(Shader shader)
|
||||
|
||||
你可以从这里得到[Mesh类的源码](http://learnopengl.com/code_viewer.php?code=mesh&type=header)。
|
||||
|
||||
Mesh类是对我们前面的教程里讨论的很多话题的的简洁的抽象在下面的教程里,我们会创建一个模型,它用作乘放多个网格物体的容器,真正的实现Assimp的加载接口。
|
||||
Mesh类是对我们前面教程里讨论过的很多话题的简洁抽象。在下面的教程里,我们会创建一个用作盛放多个网格物体的容器模型,真正的实现Assimp的加载接口。
|
@@ -70,7 +70,7 @@ const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess
|
||||
我们先来声明一个`Importer`对象,它的名字空间是`Assimp`,然后调用它的`ReadFile`函数。这个函数需要一个文件路径,第二个参数是后处理(post-processing)选项。除了可以简单加载文件外,Assimp允许我们定义几个选项来强制Assimp去对导入数据做一些额外的计算或操作。通过设置`aiProcess_Triangulate`,我们告诉Assimp如果模型不是(全部)由三角形组成,应该转换所有的模型的原始几何形状为三角形。`aiProcess_FlipUVs`基于y轴翻转纹理坐标,在处理的时候是必须的(你可能记得,我们在纹理教程中,我们说过在OpenGL大多数图像会被沿着y轴反转,所以这个小小的后处理选项会为我们修正这个)。一少部分其他有用的选项如下:
|
||||
|
||||
* `aiProcess_GenNormals` : 如果模型没有包含法线向量,就为每个顶点创建法线。
|
||||
* `aiProcess_SplitLargeMeshes` : 把大的网格成几个小的的下级网格,当你渲染有一个最大数量顶点的限制时或者只能处理小块网格时很有用。
|
||||
* `aiProcess_SplitLargeMeshes` : 把大的网格分成几个小的的下级网格,当你渲染有一个最大数量顶点的限制时或者只能处理小块网格时很有用。
|
||||
* `aiProcess_OptimizeMeshes` : 和上个选项相反,它把几个网格结合为一个更大的网格。以减少绘制函数调用的次数的方式来优化。
|
||||
|
||||
Assimp提供了后处理说明,你可以从这里找到所有内容。事实上通过Assimp加载一个模型超级简单。困难的是使用返回的场景对象把加载的数据变换到一个Mesh对象的数组。
|
||||
@@ -245,7 +245,7 @@ if(mesh->mMaterialIndex >= 0)
|
||||
}
|
||||
```
|
||||
|
||||
我么先从场景的`mMaterials`数组获取`aimaterial`对象,然后,我们希望加载网格的diffuse或/和specular纹理。一个材质储存了一个数组,这个数组为每个纹理类型提供纹理位置。不同的纹理类型都以`aiTextureType_`为前缀。我们使用一个帮助函数:`loadMaterialTextures`来从材质获取纹理。这个函数返回一个`Texture`结构体的向量,我们在之后储存在模型的`textures`坐标的后面。
|
||||
我么先从场景的`mMaterials`数组获取`aimaterial`对象,然后,我们希望加载网格的漫反射贴图和(或者)镜面贴图。一个材质储存了一个数组,这个数组为每个纹理类型提供纹理位置。不同的纹理类型都以`aiTextureType_`为前缀。我们使用一个帮助函数:`loadMaterialTextures`来从材质获取纹理。这个函数返回一个`Texture`结构体的向量,我们在之后储存在模型的`textures`坐标的后面。
|
||||
|
||||
`loadMaterialTextures`函数遍历所有给定纹理类型的纹理位置,获取纹理的文件位置,然后加载生成纹理,把信息储存到`Vertex`结构体。看起来像这样:
|
||||
|
||||
@@ -345,23 +345,23 @@ vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string
|
||||
|
||||
# 和箱子模型告别
|
||||
|
||||
现在给我们导入一个天才艺术家创建的模型看看效果,不是我这个天才做的(你不得不承认,这个箱子也许是你见过的最漂亮的立体图形)。因为我不想过于自夸,所以我会时不时的给其他艺术家进入这个行列的机会,这次我们会加载Crytek原版的孤岛危机游戏中的纳米铠甲。这个模型被输出为obj和mtl文件,mtl包含模型的diffuse和specular以及法线贴图(后面会讲)。你可以下载这个模型,注意,所有的纹理和模型文件都应该放在同一个目录,以便载入纹理。
|
||||
现在给我们导入一个天才艺术家创建的模型看看效果,不是我这个天才做的(你不得不承认,这个箱子也许是你见过的最漂亮的立方体)。因为我不想过于自夸,所以我会时不时的给其他艺术家进入这个行列的机会,这次我们会加载Crytek的原版孤岛危机游戏中的纳米铠甲。这个模型被输出为obj和mtl文件,mtl包含模型的漫反射贴图,镜面贴图以及法线贴图(后面会讲)。你可以[下载这个模型](http://learnopengl.com/data/models/nanosuit.rar),注意,所有的纹理和模型文件都应该放在同一个目录,以便载入纹理。
|
||||
|
||||
!!! Important
|
||||
|
||||
你从这个站点下载的版本是修改过的版本,每个纹理文件路径已经修改改为本地相对目录,原来的资源是绝对目录。
|
||||
|
||||
现在在代码中,声明一个Model对象,把它模型的文件位置传递给它。模型应该自动加载(如果没有错误的话)在游戏循环中使用它的Draw函数绘制这个对象。没有更多的缓冲配置,属性指针和渲染命令,仅仅简单的一行。如果你创建几个简单的着色器,像素着色器只输出对象的diffuse纹理颜色,结果看上去会有点像这样:
|
||||
现在在代码中,声明一个Model对象,把它模型的文件位置传递给它。模型应该自动加载(如果没有错误的话)在游戏循环中使用它的Draw函数绘制这个对象。没有更多的缓冲配置,属性指针和渲染命令,仅仅简单的一行。如果你创建几个简单的着色器,像素着色器只输出对象的漫反射贴图颜色,结果看上去会有点像这样:
|
||||
|
||||

|
||||
|
||||
你可以从这里找到带有[顶点](http://learnopengl.com/code_viewer.php?code=model_loading/model&type=vertex)和[片段](http://learnopengl.com/code_viewer.php?code=model_loading/model&type=fragment)着色器的[完整的源码](http://learnopengl.com/code_viewer.php?code=model_loading/model_diffuse)。
|
||||
|
||||
我们也可以变得更加有创造力,引入两个点光源到我们之前从光照教程学过的渲染等式,结合高光贴图获得惊艳效果:
|
||||
因为我们之前学习过光照教程,可以更加富有创造性的引入两个点光源渲染方程,结合镜面贴图获得惊艳效果:
|
||||
|
||||

|
||||
|
||||
虽然我不得不承认这个相比之前用过的容器也太炫了。使用Assimp,你可以载入无数在互联网上找到的模型。只有很少的资源网站提供多种格式的免费3D模型给你下载。一定注意,有些模型仍然不能很好的载入,纹理路径无效或者这种格式Assimp不能读。
|
||||
甚至我不得不承认这个相比之前用过的容器酷炫多了。使用Assimp,你可以载入无数在互联网上找到的模型。有相当多可以以多种文件格式下载免费3D模型的资源网站。一定注意,有些模型仍然不能很好的载入,纹理路径无效或者这种格式Assimp不能读取。
|
||||
|
||||
## 练习
|
||||
|
||||
|
Reference in New Issue
Block a user