From c073b8f2d3fb0a3b3ee8ffe76f9ea0bff77fbb8e Mon Sep 17 00:00:00 2001 From: Meow J Date: Tue, 14 Jul 2015 23:18:47 +0800 Subject: [PATCH] Encoding --- 03 Model Loading/03 Model.md | 124 +++++++++++++------------ 04 Advanced OpenGL/01 Depth testing.md | 2 +- 2 files changed, 64 insertions(+), 62 deletions(-) diff --git a/03 Model Loading/03 Model.md b/03 Model Loading/03 Model.md index 8d57d8e..97e31d9 100644 --- a/03 Model Loading/03 Model.md +++ b/03 Model Loading/03 Model.md @@ -1,8 +1,10 @@ -本文作者JoeyDeVries,由[Django](http://bullteacher.com/)翻译自[http://learnopengl.com](http://learnopengl.com/) +鏈枃浣滆匤oeyDeVries锛岀敱[Django](http://bullteacher.com/)缈昏瘧鑷猍http://learnopengl.com](http://learnopengl.com/) -现在是时候着手启用Assimp,并开始创建实际的加载和转换代码了。本教程的目标是创建另一个类,这个类可以表达模型的全部。更确切的说,一个模型包含多个网格,一个网格可能带有多个对象。一个别墅,包含一个木制阳台,一个尖顶或许也有一个游泳池,它仍然被加载为一个单一模型。我们通过Assimp加载模型,把它们转变为多个网格(Mesh)对象,这些对象是是先前教程里创建的。 +# 妯″瀷(Model) -闲话少说,我把Model类的结构呈现给你: +鐜板湪鏄椂鍊欑潃鎵嬪惎鐢ˋssimp锛屽苟寮濮嬪垱寤哄疄闄呯殑鍔犺浇鍜岃浆鎹唬鐮佷簡銆傛湰鏁欑▼鐨勭洰鏍囨槸鍒涘缓鍙︿竴涓被锛岃繖涓被鍙互琛ㄨ揪妯″瀷鐨勫叏閮ㄣ傛洿纭垏鐨勮锛屼竴涓ā鍨嬪寘鍚涓綉鏍硷紝涓涓綉鏍煎彲鑳藉甫鏈夊涓璞°備竴涓埆澧咃紝鍖呭惈涓涓湪鍒堕槼鍙帮紝涓涓皷椤舵垨璁镐篃鏈変竴涓父娉虫睜锛屽畠浠嶇劧琚姞杞戒负涓涓崟涓妯″瀷銆傛垜浠氳繃Assimp鍔犺浇妯″瀷锛屾妸瀹冧滑杞彉涓哄涓綉鏍硷紙Mesh锛夊璞★紝杩欎簺瀵硅薄鏄槸鍏堝墠鏁欑▼閲屽垱寤虹殑銆 + +闂茶瘽灏戣锛屾垜鎶奙odel绫荤殑缁撴瀯鍛堢幇缁欎綘锛 ```c++ @@ -26,9 +28,9 @@ class Model vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName); }; ``` -Model类包含一个Mesh对象的向量,我们需要在构造函数中给出文件的位置。之后,在构造其中,它通过loadModel函数加载文件。私有方法都被设计为处理一部分的Assimp导入的常规动作,我们会简单讲讲它们。同样,我们储存文件路径的目录,这样稍后加载纹理的时候会用到。 +Model绫诲寘鍚竴涓狹esh瀵硅薄鐨勫悜閲忥紝鎴戜滑闇瑕佸湪鏋勯犲嚱鏁颁腑缁欏嚭鏂囦欢鐨勪綅缃備箣鍚庯紝鍦ㄦ瀯閫犲叾涓紝瀹冮氳繃loadModel鍑芥暟鍔犺浇鏂囦欢銆傜鏈夋柟娉曢兘琚璁′负澶勭悊涓閮ㄥ垎鐨凙ssimp瀵煎叆鐨勫父瑙勫姩浣滐紝鎴戜滑浼氱畝鍗曡璁插畠浠傚悓鏍凤紝鎴戜滑鍌ㄥ瓨鏂囦欢璺緞鐨勭洰褰曪紝杩欐牱绋嶅悗鍔犺浇绾圭悊鐨勬椂鍊欎細鐢ㄥ埌銆 -函数Draw没有什么特别之处,基本上是循环每个网格,调用各自的Draw函数。 +鍑芥暟Draw娌℃湁浠涔堢壒鍒箣澶勶紝鍩烘湰涓婃槸寰幆姣忎釜缃戞牸锛岃皟鐢ㄥ悇鑷殑Draw鍑芥暟銆 ```c++ @@ -39,9 +41,9 @@ void Draw(Shader shader) } ``` -##把一个3D模型导入到OpenGL +##鎶婁竴涓3D妯″瀷瀵煎叆鍒癘penGL -为了导入一个模型,并把它转换为我们自己的数据结构,第一件需要做的事是包含合适的Assimp头文件,这样编译器就不会对我们抱怨了。 +涓轰簡瀵煎叆涓涓ā鍨嬶紝骞舵妸瀹冭浆鎹负鎴戜滑鑷繁鐨勬暟鎹粨鏋勶紝绗竴浠堕渶瑕佸仛鐨勪簨鏄寘鍚悎閫傜殑Assimp澶存枃浠讹紝杩欐牱缂栬瘧鍣ㄥ氨涓嶄細瀵规垜浠姳鎬ㄤ簡銆 ```c++ @@ -49,23 +51,23 @@ void Draw(Shader shader) #include #include ``` -我们将要调用的第一个函数是loadModel,它被构造函数直接调用。在loadModel函数里面,我们使用Assimp加载模型到Assimp中被称为scene对象的数据结构。你可能还记得模型加载系列的第一个教程中,这是Assimp的数据结构的根对象。一旦我们有了场景对象,我们就能从已加载模型中获取所有所需数据了。 +鎴戜滑灏嗚璋冪敤鐨勭涓涓嚱鏁版槸loadModel,瀹冭鏋勯犲嚱鏁扮洿鎺ヨ皟鐢ㄣ傚湪loadModel鍑芥暟閲岄潰锛屾垜浠娇鐢ˋssimp鍔犺浇妯″瀷鍒癆ssimp涓绉颁负scene瀵硅薄鐨勬暟鎹粨鏋勩備綘鍙兘杩樿寰楁ā鍨嬪姞杞界郴鍒楃殑绗竴涓暀绋嬩腑锛岃繖鏄疉ssimp鐨勬暟鎹粨鏋勭殑鏍瑰璞°備竴鏃︽垜浠湁浜嗗満鏅璞★紝鎴戜滑灏辫兘浠庡凡鍔犺浇妯″瀷涓幏鍙栨墍鏈夋墍闇鏁版嵁浜嗐 -Assimp最大优点是,它简约的抽象了所加载所有不同格式文件的技术细节,用一行可以做到这一切: +Assimp鏈澶т紭鐐规槸锛屽畠绠绾︾殑鎶借薄浜嗘墍鍔犺浇鎵鏈変笉鍚屾牸寮忔枃浠剁殑鎶鏈粏鑺傦紝鐢ㄤ竴琛屽彲浠ュ仛鍒拌繖涓鍒囷細 ```c++ Assimp::Importer importer; const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); ``` -我们先来声明一个Importer对象,它的名字空间是Assimp,然后调用它的ReadFile函数。这个函数需要一个文件路径,第二个参数是后处理(post-processing)选项。除了可以简单加载文件外,Assimp允许我们定义几个选项来强制Assimp去对导入数据做一些额外的计算或操作。通过设置aiProcess_Triangulate,我们告诉Assimp如果模型不是(全部)由三角形组成,应该转换所有的模型的原始几何形状为三角形。aiProcess_FlipUVs基于y轴翻转纹理坐标,在处理的时候是必须的(你可能记得,我们在纹理教程中,我们说过在OpenGL大多数图像会被沿着y轴反转,所以这个小小的后处理选项会为我们修正这个)。一少部分其他有用的选项如下: +鎴戜滑鍏堟潵澹版槑涓涓狪mporter瀵硅薄锛屽畠鐨勫悕瀛楃┖闂存槸Assimp锛岀劧鍚庤皟鐢ㄥ畠鐨凴eadFile鍑芥暟銆傝繖涓嚱鏁伴渶瑕佷竴涓枃浠惰矾寰勶紝绗簩涓弬鏁版槸鍚庡鐞嗭紙post-processing锛夐夐」銆傞櫎浜嗗彲浠ョ畝鍗曞姞杞芥枃浠跺锛孉ssimp鍏佽鎴戜滑瀹氫箟鍑犱釜閫夐」鏉ュ己鍒禔ssimp鍘诲瀵煎叆鏁版嵁鍋氫竴浜涢澶栫殑璁$畻鎴栨搷浣溿傞氳繃璁剧疆aiProcess_Triangulate锛屾垜浠憡璇堿ssimp濡傛灉妯″瀷涓嶆槸锛堝叏閮級鐢变笁瑙掑舰缁勬垚锛屽簲璇ヨ浆鎹㈡墍鏈夌殑妯″瀷鐨勫師濮嬪嚑浣曞舰鐘朵负涓夎褰€俛iProcess_FlipUVs鍩轰簬y杞寸炕杞汗鐞嗗潗鏍囷紝鍦ㄥ鐞嗙殑鏃跺欐槸蹇呴』鐨勶紙浣犲彲鑳借寰楋紝鎴戜滑鍦ㄧ汗鐞嗘暀绋嬩腑锛屾垜浠杩囧湪OpenGL澶у鏁板浘鍍忎細琚部鐫y杞村弽杞紝鎵浠ヨ繖涓皬灏忕殑鍚庡鐞嗛夐」浼氫负鎴戜滑淇杩欎釜锛夈備竴灏戦儴鍒嗗叾浠栨湁鐢ㄧ殑閫夐」濡備笅锛 -aiProcess_GenNormals:如果模型没有包含法线向量,就为每个顶点创建法线。 -aiProcess_SplitLargeMeshes:把大的网格成几个小的的下级网格,当你渲染有一个最大数量顶点的限制时或者只能处理小块网格时很有用。 -aiProcess_OptimizeMeshes:和上个选项相反,它把几个网格结合为一个更大的网格。以减少绘制函数调用的次数的方式来优化。 -Assimp提供了后处理说明,你可以从这里找到所有内容。事实上通过Assimp加载一个模型超级简单。困难的是使用返回的场景对象把加载的数据变换到一个Mesh对象的数组。 +aiProcess_GenNormals:濡傛灉妯″瀷娌℃湁鍖呭惈娉曠嚎鍚戦噺锛屽氨涓烘瘡涓《鐐瑰垱寤烘硶绾裤 +aiProcess_SplitLargeMeshes:鎶婂ぇ鐨勭綉鏍兼垚鍑犱釜灏忕殑鐨勪笅绾х綉鏍硷紝褰撲綘娓叉煋鏈変竴涓渶澶ф暟閲忛《鐐圭殑闄愬埗鏃舵垨鑰呭彧鑳藉鐞嗗皬鍧楃綉鏍兼椂寰堟湁鐢ㄣ +aiProcess_OptimizeMeshes:鍜屼笂涓夐」鐩稿弽锛屽畠鎶婂嚑涓綉鏍肩粨鍚堜负涓涓洿澶х殑缃戞牸銆備互鍑忓皯缁樺埗鍑芥暟璋冪敤鐨勬鏁扮殑鏂瑰紡鏉ヤ紭鍖栥 +Assimp鎻愪緵浜嗗悗澶勭悊璇存槑锛屼綘鍙互浠庤繖閲屾壘鍒版墍鏈夊唴瀹广備簨瀹炰笂閫氳繃Assimp鍔犺浇涓涓ā鍨嬭秴绾х畝鍗曘傚洶闅剧殑鏄娇鐢ㄨ繑鍥炵殑鍦烘櫙瀵硅薄鎶婂姞杞界殑鏁版嵁鍙樻崲鍒颁竴涓狹esh瀵硅薄鐨勬暟缁勩 -完整的loadModel函数在这里列出: +瀹屾暣鐨刲oadModel鍑芥暟鍦ㄨ繖閲屽垪鍑猴細 ```c++ @@ -84,11 +86,11 @@ void loadModel(string path) this->processNode(scene->mRootNode, scene); } ``` -在我们加载了模型之后,我们检验是否场景和场景的根节点为空,查看这些标记中的一个来看看返回的数据是否完整。如果发生了任何一个错误,我们通过导入器(impoter)的GetErrorString函数返回错误报告。我们同样重新获取文件的目录路径。 +鍦ㄦ垜浠姞杞戒簡妯″瀷涔嬪悗锛屾垜浠楠屾槸鍚﹀満鏅拰鍦烘櫙鐨勬牴鑺傜偣涓虹┖锛屾煡鐪嬭繖浜涙爣璁颁腑鐨勪竴涓潵鐪嬬湅杩斿洖鐨勬暟鎹槸鍚﹀畬鏁淬傚鏋滃彂鐢熶簡浠讳綍涓涓敊璇紝鎴戜滑閫氳繃瀵煎叆鍣紙impoter锛夌殑GetErrorString鍑芥暟杩斿洖閿欒鎶ュ憡銆傛垜浠悓鏍烽噸鏂拌幏鍙栨枃浠剁殑鐩綍璺緞銆 -如果没什么错误发生,我们希望处理所有的场景节点,所以我们传递第一个节点(根节点)到递归函数processNode。因为每个节点(可能)包含多个子节点,我们希望先处理父节点再处理子节点,以此类推。这符合递归结构,所以我们定义一个递归函数。递归函数就是一个做一些什么处理之后,用不同的参数调用它自身的函数,此种循环不会停止,直到一个特定条件发生。在我们的例子里,特定条件是所有的节点都被处理。 +濡傛灉娌′粈涔堥敊璇彂鐢燂紝鎴戜滑甯屾湜澶勭悊鎵鏈夌殑鍦烘櫙鑺傜偣锛屾墍浠ユ垜浠紶閫掔涓涓妭鐐癸紙鏍硅妭鐐癸級鍒伴掑綊鍑芥暟processNode銆傚洜涓烘瘡涓妭鐐癸紙鍙兘锛夊寘鍚涓瓙鑺傜偣锛屾垜浠笇鏈涘厛澶勭悊鐖惰妭鐐瑰啀澶勭悊瀛愯妭鐐癸紝浠ユ绫绘帹銆傝繖绗﹀悎閫掑綊缁撴瀯锛屾墍浠ユ垜浠畾涔変竴涓掑綊鍑芥暟銆傞掑綊鍑芥暟灏辨槸涓涓仛涓浜涗粈涔堝鐞嗕箣鍚庯紝鐢ㄤ笉鍚岀殑鍙傛暟璋冪敤瀹冭嚜韬殑鍑芥暟锛屾绉嶅惊鐜笉浼氬仠姝紝鐩村埌涓涓壒瀹氭潯浠跺彂鐢熴傚湪鎴戜滑鐨勪緥瀛愰噷锛岀壒瀹氭潯浠舵槸鎵鏈夌殑鑺傜偣閮借澶勭悊銆 -也许你记得,Assimp的结构,每个节点包含一个网格集合的索引,每个索引指向一个在场景对象中特定的网格位置。我们希望获取这些网格索引,获取每个网格,处理每个网格,然后对其他的节点的子节点做同样的处理。processNode函数的内容如下: +涔熻浣犺寰楋紝Assimp鐨勭粨鏋勶紝姣忎釜鑺傜偣鍖呭惈涓涓綉鏍奸泦鍚堢殑绱㈠紩锛屾瘡涓储寮曟寚鍚戜竴涓湪鍦烘櫙瀵硅薄涓壒瀹氱殑缃戞牸浣嶇疆銆傛垜浠笇鏈涜幏鍙栬繖浜涚綉鏍肩储寮曪紝鑾峰彇姣忎釜缃戞牸锛屽鐞嗘瘡涓綉鏍硷紝鐒跺悗瀵瑰叾浠栫殑鑺傜偣鐨勫瓙鑺傜偣鍋氬悓鏍风殑澶勭悊銆俻rocessNode鍑芥暟鐨勫唴瀹瑰涓嬶細 ```c++ @@ -107,22 +109,22 @@ void processNode(aiNode* node, const aiScene* scene) } } ``` -我们首先利用场景的mMeshes数组来检查每个节点的网格索引以获取相应的网格。被返回的网格被传递给processMesh函数,它返回一个网格对象,我们可以把它储存在meshes的list或vector(STL里的两种实现链表的数据结构)中。 +鎴戜滑棣栧厛鍒╃敤鍦烘櫙鐨刴Meshes鏁扮粍鏉ユ鏌ユ瘡涓妭鐐圭殑缃戞牸绱㈠紩浠ヨ幏鍙栫浉搴旂殑缃戞牸銆傝杩斿洖鐨勭綉鏍艰浼犻掔粰processMesh鍑芥暟锛屽畠杩斿洖涓涓綉鏍煎璞★紝鎴戜滑鍙互鎶婂畠鍌ㄥ瓨鍦╩eshes鐨刲ist鎴杤ector锛圫TL閲岀殑涓ょ瀹炵幇閾捐〃鐨勬暟鎹粨鏋勶級涓 -一旦所有的网格都被处理,我们遍历所有子节点,同样调用processNode函数。一旦一个节点不再拥有任何子节点,函数就会停止执行。 +涓鏃︽墍鏈夌殑缃戞牸閮借澶勭悊锛屾垜浠亶鍘嗘墍鏈夊瓙鑺傜偣锛屽悓鏍疯皟鐢╬rocessNode鍑芥暟銆備竴鏃︿竴涓妭鐐逛笉鍐嶆嫢鏈変换浣曞瓙鑺傜偣锛屽嚱鏁板氨浼氬仠姝㈡墽琛屻 -认真的读者会注意到,我们可能基本忘记处理任何的节点,简单循环出场景所有的网格,而不是用索引做这件复杂的事。我们这么做的原因是,使用这种节点的原始的想法是,在网格之间定义一个父-子关系。通过递归遍历这些关系,我们可以真正定义特定的网格作为其他网格的父(节点)。 +璁ょ湡鐨勮鑰呬細娉ㄦ剰鍒帮紝鎴戜滑鍙兘鍩烘湰蹇樿澶勭悊浠讳綍鐨勮妭鐐癸紝绠鍗曞惊鐜嚭鍦烘櫙鎵鏈夌殑缃戞牸锛岃屼笉鏄敤绱㈠紩鍋氳繖浠跺鏉傜殑浜嬨傛垜浠繖涔堝仛鐨勫師鍥犳槸锛屼娇鐢ㄨ繖绉嶈妭鐐圭殑鍘熷鐨勬兂娉曟槸锛屽湪缃戞牸涔嬮棿瀹氫箟涓涓埗-瀛愬叧绯汇傞氳繃閫掑綊閬嶅巻杩欎簺鍏崇郴锛屾垜浠彲浠ョ湡姝e畾涔夌壒瀹氱殑缃戞牸浣滀负鍏朵粬缃戞牸鐨勭埗锛堣妭鐐癸級銆 -关于这个系统的一个有用的例子是,当你想要平移一个汽车网格需要确保把它的子(节点)比如,引擎网格,方向盘网格和轮胎网格都进行平移;使用父-子关系这样的系统很容易被创建出来。 +鍏充簬杩欎釜绯荤粺鐨勪竴涓湁鐢ㄧ殑渚嬪瓙鏄紝褰撲綘鎯宠骞崇Щ涓涓苯杞︾綉鏍奸渶瑕佺‘淇濇妸瀹冪殑瀛愶紙鑺傜偣锛夋瘮濡傦紝寮曟搸缃戞牸锛屾柟鍚戠洏缃戞牸鍜岃疆鑳庣綉鏍奸兘杩涜骞崇Щ锛涗娇鐢ㄧ埗-瀛愬叧绯昏繖鏍风殑绯荤粺寰堝鏄撹鍒涘缓鍑烘潵銆 -现在我们没用这种系统,但是无论何时你想要对你的网格数据进行额外的控制,这通常是一种坚持被推荐的做法。这些模型毕竟是那些定义了这些节点风格的关系的艺术家所创建的。 -下一步是用上个教程创建的Mesh类开始真正处理Assimp的数据。 +鐜板湪鎴戜滑娌$敤杩欑绯荤粺锛屼絾鏄棤璁轰綍鏃朵綘鎯宠瀵逛綘鐨勭綉鏍兼暟鎹繘琛岄澶栫殑鎺у埗锛岃繖閫氬父鏄竴绉嶅潥鎸佽鎺ㄨ崘鐨勫仛娉曘傝繖浜涙ā鍨嬫瘯绔熸槸閭d簺瀹氫箟浜嗚繖浜涜妭鐐归鏍肩殑鍏崇郴鐨勮壓鏈鎵鍒涘缓鐨勩 +涓嬩竴姝ユ槸鐢ㄤ笂涓暀绋嬪垱寤虹殑Mesh绫诲紑濮嬬湡姝e鐞咥ssimp鐨勬暟鎹 -##从Assimp到网格 +##浠嶢ssimp鍒扮綉鏍 -把一个aiMesh对象转换为一个我们自己定义的网格对象并不难。我们所要做的全部是获取每个网格相关的属性并把这些属性储存到我们自己的对象。通常processMesh函数的结构会是这样: +鎶婁竴涓猘iMesh瀵硅薄杞崲涓轰竴涓垜浠嚜宸卞畾涔夌殑缃戞牸瀵硅薄骞朵笉闅俱傛垜浠墍瑕佸仛鐨勫叏閮ㄦ槸鑾峰彇姣忎釜缃戞牸鐩稿叧鐨勫睘鎬у苟鎶婅繖浜涘睘鎬у偍瀛樺埌鎴戜滑鑷繁鐨勫璞°傞氬父processMesh鍑芥暟鐨勭粨鏋勪細鏄繖鏍凤細 ```c++ @@ -150,9 +152,9 @@ Mesh processMesh(aiMesh* mesh, const aiScene* scene) return Mesh(vertices, indices, textures); } ``` -处理一个网格基本由三部分组成:获取所有顶点数据,获取网格的索引,获取相关材质数据。处理过的数据被储存在3个向量其中之一里面,一个Mesh被以这些数据创建,返回到函数的调用者。 +澶勭悊涓涓綉鏍煎熀鏈敱涓夐儴鍒嗙粍鎴愶細鑾峰彇鎵鏈夐《鐐规暟鎹紝鑾峰彇缃戞牸鐨勭储寮曪紝鑾峰彇鐩稿叧鏉愯川鏁版嵁銆傚鐞嗚繃鐨勬暟鎹鍌ㄥ瓨鍦3涓悜閲忓叾涓箣涓閲岄潰锛屼竴涓狹esh琚互杩欎簺鏁版嵁鍒涘缓锛岃繑鍥炲埌鍑芥暟鐨勮皟鐢ㄨ呫 -获取顶点数据很简单:我们定义一个Vertex结构体,在每次遍历后我们把这个结构体添加到Vertices数组。我们为存在于网格中的众多顶点循环(通过mesh->mNumVertices获取)。在遍历的过程中,我们希望用所有相关数据填充这个结构体。每个顶点位置会像这样被处理: +鑾峰彇椤剁偣鏁版嵁寰堢畝鍗曪細鎴戜滑瀹氫箟涓涓猇ertex缁撴瀯浣擄紝鍦ㄦ瘡娆¢亶鍘嗗悗鎴戜滑鎶婅繖涓粨鏋勪綋娣诲姞鍒癡ertices鏁扮粍銆傛垜浠负瀛樺湪浜庣綉鏍间腑鐨勪紬澶氶《鐐瑰惊鐜紙閫氳繃mesh->mNumVertices鑾峰彇锛夈傚湪閬嶅巻鐨勮繃绋嬩腑锛屾垜浠笇鏈涚敤鎵鏈夌浉鍏虫暟鎹~鍏呰繖涓粨鏋勪綋銆傛瘡涓《鐐逛綅缃細鍍忚繖鏍疯澶勭悊锛 ```c++ @@ -162,10 +164,10 @@ vector.y = mesh->mVertices[i].y; vector.z = mesh->mVertices[i].z; vertex.Position = vector; ``` -注意,为了传输Assimp的数据,我们定义一个vec3的宿主,我们需要它是因为Assimp维持它自己的数据类型,这些类型用于向量、材质、字符串等。这些数据类型转换到glm的数据类型时通常效果不佳。 +娉ㄦ剰锛屼负浜嗕紶杈揂ssimp鐨勬暟鎹紝鎴戜滑瀹氫箟涓涓獀ec3鐨勫涓伙紝鎴戜滑闇瑕佸畠鏄洜涓篈ssimp缁存寔瀹冭嚜宸辩殑鏁版嵁绫诲瀷锛岃繖浜涚被鍨嬬敤浜庡悜閲忋佹潗璐ㄣ佸瓧绗︿覆绛夈傝繖浜涙暟鎹被鍨嬭浆鎹㈠埌glm鐨勬暟鎹被鍨嬫椂閫氬父鏁堟灉涓嶄匠銆 -Assimp调用他们的顶点位置数组mVertices真有点违反直觉。 -对应法线的步骤毫无疑问是这样的: +Assimp璋冪敤浠栦滑鐨勯《鐐逛綅缃暟缁刴Vertices鐪熸湁鐐硅繚鍙嶇洿瑙夈 +瀵瑰簲娉曠嚎鐨勬楠ゆ鏃犵枒闂槸杩欐牱鐨勶細 ```c++ @@ -174,7 +176,7 @@ vector.y = mesh->mNormals[i].y; vector.z = mesh->mNormals[i].z; vertex.Normal = vector; ``` -纹理坐标也基本一样,但是Assimp允许一个模型的每个顶点有8个不同的纹理坐标,我们可能用不到,所以我们只关系第一组纹理坐标。我们也希望检查网格是否真的包含纹理坐标(可能并不总是如此): +绾圭悊鍧愭爣涔熷熀鏈竴鏍凤紝浣嗘槸Assimp鍏佽涓涓ā鍨嬬殑姣忎釜椤剁偣鏈8涓笉鍚岀殑绾圭悊鍧愭爣锛屾垜浠彲鑳界敤涓嶅埌锛屾墍浠ユ垜浠彧鍏崇郴绗竴缁勭汗鐞嗗潗鏍囥傛垜浠篃甯屾湜妫鏌ョ綉鏍兼槸鍚︾湡鐨勫寘鍚汗鐞嗗潗鏍囷紙鍙兘骞朵笉鎬绘槸濡傛锛: ```c++ @@ -188,13 +190,13 @@ if(mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates? else vertex.TexCoords = glm::vec2(0.0f, 0.0f); ``` -Vertex结构体现在完全被所需的顶点属性填充了,我们能把它推到vertices向量的尾部。要对每个网格的顶点做相同的处理。 +Vertex缁撴瀯浣撶幇鍦ㄥ畬鍏ㄨ鎵闇鐨勯《鐐瑰睘鎬у~鍏呬簡锛屾垜浠兘鎶婂畠鎺ㄥ埌vertices鍚戦噺鐨勫熬閮ㄣ傝瀵规瘡涓綉鏍肩殑椤剁偣鍋氱浉鍚岀殑澶勭悊銆 -##顶点 +##椤剁偣 -Assimp的接口定义每个网格有一个以面(faces)为单位的数组,每个面代表一个单独的图元,在我们的例子中(由于aiProcess_Triangulate选项)总是三角形,一个面包含索引,这些索引定义我们需要绘制的顶点以在那样的顺序提供给每个图元,所以如果我们遍历所有面,把所有面的索引储存到indices向量,我们需要这么做: +Assimp鐨勬帴鍙e畾涔夋瘡涓綉鏍兼湁涓涓互闈紙faces锛変负鍗曚綅鐨勬暟缁勶紝姣忎釜闈唬琛ㄤ竴涓崟鐙殑鍥惧厓锛屽湪鎴戜滑鐨勪緥瀛愪腑锛堢敱浜巃iProcess_Triangulate閫夐」锛夋绘槸涓夎褰紝涓涓潰鍖呭惈绱㈠紩锛岃繖浜涚储寮曞畾涔夋垜浠渶瑕佺粯鍒剁殑椤剁偣浠ュ湪閭f牱鐨勯『搴忔彁渚涚粰姣忎釜鍥惧厓锛屾墍浠ュ鏋滄垜浠亶鍘嗘墍鏈夐潰锛屾妸鎵鏈夐潰鐨勭储寮曞偍瀛樺埌indices鍚戦噺锛屾垜浠渶瑕佽繖涔堝仛锛 ```c++ @@ -205,13 +207,13 @@ for(GLuint i = 0; i < mesh->mNumFaces; i++) indices.push_back(face.mIndices[j]); } ``` -所有外部循环结束后,我们现在有了一个完整点的顶点和索引数据来绘制网格,这要调用glDrawElements函数。可是,为了结束这个讨论,并向网格提供一些细节,我们同样希望处理网格的材质。 +鎵鏈夊閮ㄥ惊鐜粨鏉熷悗锛屾垜浠幇鍦ㄦ湁浜嗕竴涓畬鏁寸偣鐨勯《鐐瑰拰绱㈠紩鏁版嵁鏉ョ粯鍒剁綉鏍硷紝杩欒璋冪敤glDrawElements鍑芥暟銆傚彲鏄紝涓轰簡缁撴潫杩欎釜璁ㄨ锛屽苟鍚戠綉鏍兼彁渚涗竴浜涚粏鑺傦紝鎴戜滑鍚屾牱甯屾湜澶勭悊缃戞牸鐨勬潗璐ㄣ -##材质 +##鏉愯川 -如同节点,一个网格只有一个指向材质对象的索引,获取网格实际的材质,我们需要索引场景的mMaterials数组。网格的材质索引被设置在mMaterialIndex属性中,通过这个属性我们同样能够检验一个网格是否包含一个材质: +濡傚悓鑺傜偣锛屼竴涓綉鏍煎彧鏈変竴涓寚鍚戞潗璐ㄥ璞$殑绱㈠紩锛岃幏鍙栫綉鏍煎疄闄呯殑鏉愯川锛屾垜浠渶瑕佺储寮曞満鏅殑mMaterials鏁扮粍銆傜綉鏍肩殑鏉愯川绱㈠紩琚缃湪mMaterialIndex灞炴т腑锛岄氳繃杩欎釜灞炴ф垜浠悓鏍疯兘澶熸楠屼竴涓綉鏍兼槸鍚﹀寘鍚竴涓潗璐細 ```c++ @@ -226,9 +228,9 @@ if(mesh->mMaterialIndex >= 0) textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); } ``` -我么先从场景的mMaterials数组获取aimaterial对象,然后,我们希望加载网格的diffuse或/和specular纹理。一个材质储存了一个数组,这个数组为每个纹理类型提供纹理位置。不同的纹理类型都以aiTextureType_为前缀。我们使用一个帮助函数:loadMaterialTextures来从材质获取纹理。这个函数返回一个Texture结构体的向量,我们在之后储存在模型的textures坐标的后面。 +鎴戜箞鍏堜粠鍦烘櫙鐨刴Materials鏁扮粍鑾峰彇aimaterial瀵硅薄锛岀劧鍚庯紝鎴戜滑甯屾湜鍔犺浇缃戞牸鐨刣iffuse鎴/鍜宻pecular绾圭悊銆備竴涓潗璐ㄥ偍瀛樹簡涓涓暟缁勶紝杩欎釜鏁扮粍涓烘瘡涓汗鐞嗙被鍨嬫彁渚涚汗鐞嗕綅缃備笉鍚岀殑绾圭悊绫诲瀷閮戒互aiTextureType_涓哄墠缂銆傛垜浠娇鐢ㄤ竴涓府鍔╁嚱鏁帮細loadMaterialTextures鏉ヤ粠鏉愯川鑾峰彇绾圭悊銆傝繖涓嚱鏁拌繑鍥炰竴涓猅exture缁撴瀯浣撶殑鍚戦噺锛屾垜浠湪涔嬪悗鍌ㄥ瓨鍦ㄦā鍨嬬殑textures鍧愭爣鐨勫悗闈€ -loadMaterialTextures函数遍历所有给定纹理类型的纹理位置,获取纹理的文件位置,然后加载生成纹理,把信息储存到Vertex结构体。看起来像这样: +loadMaterialTextures鍑芥暟閬嶅巻鎵鏈夌粰瀹氱汗鐞嗙被鍨嬬殑绾圭悊浣嶇疆锛岃幏鍙栫汗鐞嗙殑鏂囦欢浣嶇疆锛岀劧鍚庡姞杞界敓鎴愮汗鐞嗭紝鎶婁俊鎭偍瀛樺埌Vertex缁撴瀯浣撱傜湅璧锋潵鍍忚繖鏍凤細 ```c++ @@ -248,20 +250,20 @@ vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, string return textures; } ``` -我们先通过GetTextureCount函数检验材质中储存的纹理,以期得到我们希望得到的纹理类型。然后我们通过GetTexture函数获取每个纹理的文件位置,这个位置以aiString类型储存。然后我们使用另一个帮助函数,它被命名为:TextureFromFile加载一个纹理(使用SOIL),返回纹理的ID。你可以查看列在最后的完整的代码,如果你不知道这个函数应该怎样写出来的话。 +鎴戜滑鍏堥氳繃GetTextureCount鍑芥暟妫楠屾潗璐ㄤ腑鍌ㄥ瓨鐨勭汗鐞嗭紝浠ユ湡寰楀埌鎴戜滑甯屾湜寰楀埌鐨勭汗鐞嗙被鍨嬨傜劧鍚庢垜浠氳繃GetTexture鍑芥暟鑾峰彇姣忎釜绾圭悊鐨勬枃浠朵綅缃紝杩欎釜浣嶇疆浠iString绫诲瀷鍌ㄥ瓨銆傜劧鍚庢垜浠娇鐢ㄥ彟涓涓府鍔╁嚱鏁帮紝瀹冭鍛藉悕涓猴細TextureFromFile鍔犺浇涓涓汗鐞嗭紙浣跨敤SOIL锛夛紝杩斿洖绾圭悊鐨処D銆備綘鍙互鏌ョ湅鍒楀湪鏈鍚庣殑瀹屾暣鐨勪唬鐮侊紝濡傛灉浣犱笉鐭ラ亾杩欎釜鍑芥暟搴旇鎬庢牱鍐欏嚭鏉ョ殑璇濄 -注意,我们假设纹理文件与模型是在相同的目录里。我们可以简单的链接纹理位置字符串和之前获取的目录字符串(在loadModel函数中得到的)来获得完整的纹理路径(这就是为什么GetTexture函数同样需要目录字符串)。 +娉ㄦ剰锛屾垜浠亣璁剧汗鐞嗘枃浠朵笌妯″瀷鏄湪鐩稿悓鐨勭洰褰曢噷銆傛垜浠彲浠ョ畝鍗曠殑閾炬帴绾圭悊浣嶇疆瀛楃涓插拰涔嬪墠鑾峰彇鐨勭洰褰曞瓧绗︿覆锛堝湪loadModel鍑芥暟涓緱鍒扮殑锛夋潵鑾峰緱瀹屾暣鐨勭汗鐞嗚矾寰勶紙杩欏氨鏄负浠涔圙etTexture鍑芥暟鍚屾牱闇瑕佺洰褰曞瓧绗︿覆锛夈 -有些在互联网上找到的模型使用绝对路径,它们的纹理位置就不会在每天机器上都有效了。例子里,你可能希望手工编辑这个文件来使用本地路径为纹理所使用(如果可能的话)。 -这就是使用Assimp来导入一个模型的全部了。你可以在这里找到Model类的全部。 +鏈変簺鍦ㄤ簰鑱旂綉涓婃壘鍒扮殑妯″瀷浣跨敤缁濆璺緞锛屽畠浠殑绾圭悊浣嶇疆灏变笉浼氬湪姣忓ぉ鏈哄櫒涓婇兘鏈夋晥浜嗐備緥瀛愰噷锛屼綘鍙兘甯屾湜鎵嬪伐缂栬緫杩欎釜鏂囦欢鏉ヤ娇鐢ㄦ湰鍦拌矾寰勪负绾圭悊鎵浣跨敤锛堝鏋滃彲鑳界殑璇濓級銆 +杩欏氨鏄娇鐢ˋssimp鏉ュ鍏ヤ竴涓ā鍨嬬殑鍏ㄩ儴浜嗐備綘鍙互鍦ㄨ繖閲屾壘鍒癕odel绫荤殑鍏ㄩ儴銆 -##重大优化 +##閲嶅ぇ浼樺寲 -我们现在还没做完。因为我们还想做一个重大的优化(但是不是必须的)。大多数场景重用若干纹理,把它们应用到网格;还是思考那个别墅,它有个花岗岩的纹理作为墙面。这个纹理也可能应用到地板、天花板,楼梯,或者一张桌子、一个附近的小物件。加载纹理需要不少操作,当前的实现中一个新的纹理被加载和生成,来为每个网格使用,即使同样的纹理之前已经被加载了好几次。这会很快转变为你的模型加载实现的瓶颈。 +鎴戜滑鐜板湪杩樻病鍋氬畬銆傚洜涓烘垜浠繕鎯冲仛涓涓噸澶х殑浼樺寲锛堜絾鏄笉鏄繀椤荤殑锛夈傚ぇ澶氭暟鍦烘櫙閲嶇敤鑻ュ共绾圭悊锛屾妸瀹冧滑搴旂敤鍒扮綉鏍硷紱杩樻槸鎬濊冮偅涓埆澧咃紝瀹冩湁涓姳宀楀博鐨勭汗鐞嗕綔涓哄闈€傝繖涓汗鐞嗕篃鍙兘搴旂敤鍒板湴鏉裤佸ぉ鑺辨澘锛屾ゼ姊紝鎴栬呬竴寮犳瀛愩佷竴涓檮杩戠殑灏忕墿浠躲傚姞杞界汗鐞嗛渶瑕佷笉灏戞搷浣滐紝褰撳墠鐨勫疄鐜颁腑涓涓柊鐨勭汗鐞嗚鍔犺浇鍜岀敓鎴愶紝鏉ヤ负姣忎釜缃戞牸浣跨敤锛屽嵆浣垮悓鏍风殑绾圭悊涔嬪墠宸茬粡琚姞杞戒簡濂藉嚑娆°傝繖浼氬緢蹇浆鍙樹负浣犵殑妯″瀷鍔犺浇瀹炵幇鐨勭摱棰堛 -所以我们打算添加一个小小的微调,把模型的代码改成,储存所有的已加载纹理到全局。无论在哪儿我们都要先检查这个纹理是否已经被加载过了。如果加载过了,我们就直接使用这个纹理并跳过整个加载流程来节省处理能力。为了对比纹理我们同样需要储存它们的路径: +鎵浠ユ垜浠墦绠楁坊鍔犱竴涓皬灏忕殑寰皟锛屾妸妯″瀷鐨勪唬鐮佹敼鎴愶紝鍌ㄥ瓨鎵鏈夌殑宸插姞杞界汗鐞嗗埌鍏ㄥ眬銆傛棤璁哄湪鍝効鎴戜滑閮借鍏堟鏌ヨ繖涓汗鐞嗘槸鍚﹀凡缁忚鍔犺浇杩囦簡銆傚鏋滃姞杞借繃浜嗭紝鎴戜滑灏辩洿鎺ヤ娇鐢ㄨ繖涓汗鐞嗗苟璺宠繃鏁翠釜鍔犺浇娴佺▼鏉ヨ妭鐪佸鐞嗚兘鍔涖備负浜嗗姣旂汗鐞嗘垜浠悓鏍烽渶瑕佸偍瀛樺畠浠殑璺緞锛 ```c++ @@ -271,13 +273,13 @@ struct Texture { aiString path; // We store the path of the texture to compare with other textures }; ``` -然后我们把所有家在过的纹理储存到另一个向量中,它是作为一个私有变量声明在模型类的顶部: +鐒跺悗鎴戜滑鎶婃墍鏈夊鍦ㄨ繃鐨勭汗鐞嗗偍瀛樺埌鍙︿竴涓悜閲忎腑锛屽畠鏄綔涓轰竴涓鏈夊彉閲忓0鏄庡湪妯″瀷绫荤殑椤堕儴锛 ```c++ vector textures_loaded; ``` -然后,在loadMaterialTextures函数中,我们希望把纹理路径和所有texture_loaded向量对比,看看是否当前纹理路径和其中任何一个是否相同,如果是,我们跳过纹理加载/生成部分,简单的使用已加载纹理结构体作为网格纹理。这个函数如下所示: +鐒跺悗锛屽湪loadMaterialTextures鍑芥暟涓紝鎴戜滑甯屾湜鎶婄汗鐞嗚矾寰勫拰鎵鏈塼exture_loaded鍚戦噺瀵规瘮锛岀湅鐪嬫槸鍚﹀綋鍓嶇汗鐞嗚矾寰勫拰鍏朵腑浠讳綍涓涓槸鍚︾浉鍚岋紝濡傛灉鏄紝鎴戜滑璺宠繃绾圭悊鍔犺浇/鐢熸垚閮ㄥ垎锛岀畝鍗曠殑浣跨敤宸插姞杞界汗鐞嗙粨鏋勪綋浣滀负缃戞牸绾圭悊銆傝繖涓嚱鏁板涓嬫墍绀猴細 ```c++ @@ -311,32 +313,32 @@ vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, string return textures; } ``` -所以现在我们不仅有了一个通用模型加载系统,同时我们也得到了一个能使加载对象更快的优化版本。 +鎵浠ョ幇鍦ㄦ垜浠笉浠呮湁浜嗕竴涓氱敤妯″瀷鍔犺浇绯荤粺锛屽悓鏃舵垜浠篃寰楀埌浜嗕竴涓兘浣垮姞杞藉璞℃洿蹇殑浼樺寲鐗堟湰銆 -有些版本的Assimp当使用调试版或/和使用你的IDE的调试模式时,模型加载模型实在慢,所以确保在当你加载得很慢的时候用发布版再测试。 -你可以从这里获得优化的Model类的完整源代码。 +鏈変簺鐗堟湰鐨凙ssimp褰撲娇鐢ㄨ皟璇曠増鎴/鍜屼娇鐢ㄤ綘鐨処DE鐨勮皟璇曟ā寮忔椂锛屾ā鍨嬪姞杞芥ā鍨嬪疄鍦ㄦ參锛屾墍浠ョ‘淇濆湪褰撲綘鍔犺浇寰楀緢鎱㈢殑鏃跺欑敤鍙戝竷鐗堝啀娴嬭瘯銆 +浣犲彲浠ヤ粠杩欓噷鑾峰緱浼樺寲鐨凪odel绫荤殑瀹屾暣婧愪唬鐮併 -##和箱子模型告别! +##鍜岀瀛愭ā鍨嬪憡鍒! -现在给我们导入一个天才艺术家创建的模型看看效果,不是我这个天才做的(你不得不承认,这个箱子也许是你见过的最漂亮的立体图形)。因为我不想过于自夸,所以我会时不时的给其他艺术家进入这个行列的机会,这次我们会加载Crytek原版的孤岛危机游戏中的纳米铠甲。这个模型被输出为obj和mtl文件,mtl包含模型的diffuse和specular以及法线贴图(后面会讲)。你可以下载这个模型,注意,所有的纹理和模型文件都应该放在同一个目录,以便载入纹理。 +鐜板湪缁欐垜浠鍏ヤ竴涓ぉ鎵嶈壓鏈鍒涘缓鐨勬ā鍨嬬湅鐪嬫晥鏋滐紝涓嶆槸鎴戣繖涓ぉ鎵嶅仛鐨勶紙浣犱笉寰椾笉鎵胯锛岃繖涓瀛愪篃璁告槸浣犺杩囩殑鏈婕備寒鐨勭珛浣撳浘褰級銆傚洜涓烘垜涓嶆兂杩囦簬鑷じ锛屾墍浠ユ垜浼氭椂涓嶆椂鐨勭粰鍏朵粬鑹烘湳瀹惰繘鍏ヨ繖涓鍒楃殑鏈轰細锛岃繖娆℃垜浠細鍔犺浇Crytek鍘熺増鐨勫宀涘嵄鏈烘父鎴忎腑鐨勭撼绫抽摖鐢层傝繖涓ā鍨嬭杈撳嚭涓簅bj鍜宮tl鏂囦欢锛宮tl鍖呭惈妯″瀷鐨刣iffuse鍜宻pecular浠ュ強娉曠嚎璐村浘锛堝悗闈細璁诧級銆備綘鍙互涓嬭浇杩欎釜妯″瀷锛屾敞鎰忥紝鎵鏈夌殑绾圭悊鍜屾ā鍨嬫枃浠堕兘搴旇鏀惧湪鍚屼竴涓洰褰曪紝浠ヤ究杞藉叆绾圭悊銆 -你从这个站点下载的版本是修改过的版本,每个纹理文件路径已经修改改为本地相对目录,原来的资源是绝对目录。 -现在在代码中,声明一个Model对象,把它模型的文件位置传递给它。模型应该自动加载(如果没有错误的话)在游戏循环中使用它的Draw函数绘制这个对象。没有更多的缓冲配置,属性指针和渲染命令,仅仅简单的一行。如果你创建几个简单的着色器,像素着色器只输出对象的diffuse纹理颜色,结果看上去会有点像这样: +浣犱粠杩欎釜绔欑偣涓嬭浇鐨勭増鏈槸淇敼杩囩殑鐗堟湰锛屾瘡涓汗鐞嗘枃浠惰矾寰勫凡缁忎慨鏀规敼涓烘湰鍦扮浉瀵圭洰褰曪紝鍘熸潵鐨勮祫婧愭槸缁濆鐩綍銆 +鐜板湪鍦ㄤ唬鐮佷腑锛屽0鏄庝竴涓狹odel瀵硅薄锛屾妸瀹冩ā鍨嬬殑鏂囦欢浣嶇疆浼犻掔粰瀹冦傛ā鍨嬪簲璇ヨ嚜鍔ㄥ姞杞斤紙濡傛灉娌℃湁閿欒鐨勮瘽锛夊湪娓告垙寰幆涓娇鐢ㄥ畠鐨凞raw鍑芥暟缁樺埗杩欎釜瀵硅薄銆傛病鏈夋洿澶氱殑缂撳啿閰嶇疆锛屽睘鎬ф寚閽堝拰娓叉煋鍛戒护锛屼粎浠呯畝鍗曠殑涓琛屻傚鏋滀綘鍒涘缓鍑犱釜绠鍗曠殑鐫鑹插櫒锛屽儚绱犵潃鑹插櫒鍙緭鍑哄璞$殑diffuse绾圭悊棰滆壊锛岀粨鏋滅湅涓婂幓浼氭湁鐐瑰儚杩欐牱锛 ![](http://www.learnopengl.com/img/model_loading/model_diffuse.png) -你可以从这里找到带有顶点和像素着色器的完整的源码。 +浣犲彲浠ヤ粠杩欓噷鎵惧埌甯︽湁椤剁偣鍜屽儚绱犵潃鑹插櫒鐨勫畬鏁寸殑婧愮爜銆 -我们也可以变得更加有创造力,引入两个点光源到我们之前从光照教程学过的渲染等式,结合高光贴图获得惊艳效果: +鎴戜滑涔熷彲浠ュ彉寰楁洿鍔犳湁鍒涢犲姏锛屽紩鍏ヤ袱涓偣鍏夋簮鍒版垜浠箣鍓嶄粠鍏夌収鏁欑▼瀛﹁繃鐨勬覆鏌撶瓑寮忥紝缁撳悎楂樺厜璐村浘鑾峰緱鎯婅壋鏁堟灉锛 ![](http://www.learnopengl.com/img/model_loading/model_lighting.png) -虽然我不得不承认这个相比之前用过的容器也太炫了。使用Assimp,你可以载入无数在互联网上找到的模型。只有很少的资源网站提供多种格式的免费3D模型给你下载。一定注意,有些模型仍然不能很好的载入,纹理路径无效或者这种格式Assimp不能读。 +铏界劧鎴戜笉寰椾笉鎵胯杩欎釜鐩告瘮涔嬪墠鐢ㄨ繃鐨勫鍣ㄤ篃澶偒浜嗐備娇鐢ˋssimp锛屼綘鍙互杞藉叆鏃犳暟鍦ㄤ簰鑱旂綉涓婃壘鍒扮殑妯″瀷銆傚彧鏈夊緢灏戠殑璧勬簮缃戠珯鎻愪緵澶氱鏍煎紡鐨勫厤璐3D妯″瀷缁欎綘涓嬭浇銆備竴瀹氭敞鎰忥紝鏈変簺妯″瀷浠嶇劧涓嶈兘寰堝ソ鐨勮浇鍏ワ紝绾圭悊璺緞鏃犳晥鎴栬呰繖绉嶆牸寮廇ssimp涓嶈兘璇汇 -##练习 +##缁冧範 -你可以使用两个点光源重建上个场景吗?方案,着色器。 \ No newline at end of file +浣犲彲浠ヤ娇鐢ㄤ袱涓偣鍏夋簮閲嶅缓涓婁釜鍦烘櫙鍚楋紵鏂规锛岀潃鑹插櫒銆 \ No newline at end of file diff --git a/04 Advanced OpenGL/01 Depth testing.md b/04 Advanced OpenGL/01 Depth testing.md index c37bf74..6e67441 100644 --- a/04 Advanced OpenGL/01 Depth testing.md +++ b/04 Advanced OpenGL/01 Depth testing.md @@ -1,4 +1,4 @@ -## **娣卞害娴嬭瘯** ## +# 娣卞害娴嬭瘯 鍦╗鍧愭爣绯荤殑鏁欑▼](http://www.learnopengl.com/#!Getting-started/Coordinate-Systems)涓垜浠憟鐜颁簡涓涓3D瀹瑰櫒,浣跨敤*娣卞害缂撳啿*,浠ラ槻姝㈣鍏朵粬闈㈤伄鎸$殑闈㈡覆鏌撳埌鍓嶉潰銆傚湪鏈暀绋嬩腑鎴戜滑灏嗙粏鑷村湴璁ㄨ琚繁搴︾紦鍐插尯(depth-buffer鎴杬-buffer)鎵瀛樺偍鐨*娣卞害鍊*鍜屽畠濡備綍纭畾鏄惁涓涓墖娈垫槸鍚﹁鍏朵粬鐗囨閬尅銆