模型的骨骼動(dòng)畫技術(shù)講解_第1頁
模型的骨骼動(dòng)畫技術(shù)講解_第2頁
模型的骨骼動(dòng)畫技術(shù)講解_第3頁
模型的骨骼動(dòng)畫技術(shù)講解_第4頁
模型的骨骼動(dòng)畫技術(shù)講解_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、模型的骨骼動(dòng)畫技術(shù)講解骨骼動(dòng)畫實(shí)際上是兩部分的過程。第一個(gè)由美術(shù)執(zhí)行,第二個(gè)由程序員(或者你寫的引擎)執(zhí)行。第一部分發(fā)生在建模軟件中,稱為建模。這里發(fā)生的是術(shù)定義了網(wǎng)格下面骨骼的骨架。網(wǎng)格代表物體(無論是人類,怪物還是其他物體)的皮膚,骨骼用于移動(dòng)網(wǎng)格物體,以模擬現(xiàn)實(shí)世界中的實(shí)際運(yùn)動(dòng),這通過將每個(gè)頂點(diǎn)分配給一個(gè)或多個(gè)骨頭來完成。當(dāng)頂點(diǎn)被分配給骨骼時(shí),定義了權(quán)重,該權(quán)重確定骨骼在移動(dòng)時(shí)對(duì)頂點(diǎn)的影響量。通常的做法是使所有權(quán)重的總和1(每個(gè)頂點(diǎn))。例如,如果一個(gè)頂點(diǎn)位于兩個(gè)骨骼之間,我們可能希望將每個(gè)骨骼的權(quán)重分配為0.5,因?yàn)槲覀兿M趋涝陧旤c(diǎn)上的影響相等。然而,如果頂點(diǎn)完全在單個(gè)骨骼的影響之內(nèi),

2、那么權(quán)重將為1(這意味著骨骼自主地控制頂點(diǎn)的運(yùn)動(dòng))。這是一個(gè)在混合器中創(chuàng)建的骨骼結(jié)構(gòu)的例子:我們上面看到的是動(dòng)畫的重要組成部分, 美術(shù)將骨骼結(jié)構(gòu)組合在一起,并為每個(gè)動(dòng)畫類型(“步行”,“跑步”,“死亡”等)定義了一組關(guān)鍵幀。 關(guān)鍵幀包含沿著動(dòng)畫路徑的關(guān)鍵點(diǎn)的所有骨骼的變換。 圖形引擎在關(guān)鍵幀的變換之間進(jìn)行插值,并在它們之間創(chuàng)建平滑的運(yùn)動(dòng)。用于骨骼動(dòng)畫的骨骼結(jié)構(gòu)通常是繼承的, 這意味著骨骼有一個(gè)孩子/父母關(guān)系,所以創(chuàng)建了一根骨頭。 除了根骨之外,每個(gè)骨骼都有一個(gè)父母。 例如,在人體的情況下,您可以將后骨分配為具有諸如手臂和腿部以及手指骨的兒童骨骼的根部。 當(dāng)父骨骼移動(dòng)時(shí),它也移動(dòng)其所有的孩子,但

3、是當(dāng)孩子的骨骼移動(dòng)時(shí),它不會(huì)移動(dòng)它的父母(我們的手指可以移動(dòng)而不移動(dòng)手,但是當(dāng)手移動(dòng)它移動(dòng)所有的手指)。 從實(shí)踐的角度來看,這意味著當(dāng)我們處理骨骼的變換時(shí),我們需要將它與從它引導(dǎo)到根的所有父骨骼的轉(zhuǎn)換結(jié)合起來。我們不會(huì)再進(jìn)一步討論裝備, 它是一個(gè)復(fù)雜的主題,并且在圖形程序員的領(lǐng)域之外。 建模軟件有先進(jìn)的工具來幫助美術(shù)做這項(xiàng)工作,你需要成為一個(gè)很好的美術(shù)來創(chuàng)造一個(gè)好看的網(wǎng)格和骨架。 讓我們看看圖形引擎需要做什么才能制作骨架動(dòng)畫。第一階段是用頂點(diǎn)骨骼信息來提取頂點(diǎn)緩沖區(qū)。 有幾個(gè)選項(xiàng)可用,但我們將要做的很簡(jiǎn)單。 對(duì)于每個(gè)頂點(diǎn),我們將添加一個(gè)插槽陣列,其中每個(gè)插槽包含骨骼ID和權(quán)重。 為了使我們的生

4、活更簡(jiǎn)單,我們將使用具有四個(gè)插槽的數(shù)組,這意味著沒有頂點(diǎn)可以受到四個(gè)以上的骨骼的影響。 如果您要加載更多骨骼的模型,則需要調(diào)整陣列大小,但是對(duì)于作為本博文一部分的Doom 3模型,四個(gè)骨骼就足夠了。 所以我們的新頂點(diǎn)結(jié)構(gòu)將如下所示:骨骼ID是骨轉(zhuǎn)換數(shù)組的索引, 這些變換將被應(yīng)用在WVP矩陣之前的位置和正常(即它們將頂點(diǎn)從“骨空間”轉(zhuǎn)換成局部空間)。 權(quán)重將用于將幾個(gè)骨骼的變換組合成單個(gè)變換,并且在任何情況下,總權(quán)重必須正好為1(建模軟件的事情)。 通常,我們將在動(dòng)畫關(guān)鍵幀之間進(jìn)行插值,并在每個(gè)幀中更新骨骼變換數(shù)組。骨骼轉(zhuǎn)換陣列的創(chuàng)建方式通常是棘手的部分。 變換被設(shè)置在一個(gè)歷史結(jié)構(gòu)(即樹)中,通

5、常的做法是在樹中的每個(gè)節(jié)點(diǎn)中具有縮放向量,旋轉(zhuǎn)四元數(shù)和平移向量。 實(shí)際上,每個(gè)節(jié)點(diǎn)都包含這些項(xiàng)目的數(shù)組。 數(shù)組中的每個(gè)條目都必須有一個(gè)時(shí)間戳。 應(yīng)用時(shí)間與其中一個(gè)時(shí)間戳完全匹配的情況可能很少,因此我們的代碼必須能夠插值縮放/旋轉(zhuǎn)/轉(zhuǎn)換,以便在應(yīng)用程序的時(shí)間點(diǎn)獲得正確的轉(zhuǎn)換。 我們對(duì)每個(gè)節(jié)點(diǎn)從當(dāng)前骨到根進(jìn)行相同的過程,并將這個(gè)變換鏈相加在一起以獲得最終結(jié)果。 我們?yōu)槊總€(gè)骨骼做這些,然后更新著色器。到目前為止,我們談到的一切都是非常通用的。 但是這是一個(gè)關(guān)于使用Assimp的骨骼動(dòng)畫的博文,所以我們需要再次進(jìn)入該庫,讀者可以自行下載一個(gè)Assimp庫,看看如何使用它進(jìn)行皮膚化。 Assimp的好處

6、是它支持從多種格式加載骨骼信息。 不好的是,您仍然需要對(duì)其創(chuàng)建的數(shù)據(jù)結(jié)構(gòu)進(jìn)行相當(dāng)多的工作,以生成您為著色器所需的骨骼轉(zhuǎn)換。讓我們從根的骨骼信息開始吧, 以下是Assimp數(shù)據(jù)結(jié)構(gòu)中的相關(guān)內(nèi)容:后面給讀者介紹一下關(guān)于Assimp類的加載,一切都包含在aiScene類中(當(dāng)我們導(dǎo)入網(wǎng)格文件時(shí)我們得到的對(duì)象), aiScene包含一組aiMesh對(duì)象。 aiMesh是模型的一部分,并在頂點(diǎn)級(jí)別包含位置,法線,紋理坐標(biāo)等內(nèi)容。現(xiàn)在我們看到aiMesh還包含一個(gè)aiBone對(duì)象的數(shù)組。毫無疑問,aiBone代表網(wǎng)格骨架中的一個(gè)骨骼,每個(gè)骨骼都有一個(gè)名字,通過它可以在骨骼層級(jí)(見下文),頂點(diǎn)權(quán)重?cái)?shù)組和4x

7、4偏移矩陣中找到,我們需要這個(gè)矩陣的原因是因?yàn)轫旤c(diǎn)存儲(chǔ)在通常的本地空間中,這意味著即使沒有骨架動(dòng)畫,我們現(xiàn)有的代碼庫也可以加載模型并正確渲染。但是,骨干變化在骨骼空間中發(fā)揮作用(每個(gè)骨骼都有自己的空間,這就是為什么我們需要將變換加在一起)。因此,偏移矩陣的工作將頂點(diǎn)位置從網(wǎng)格的局部空間移動(dòng)到該特定骨骼的骨空間。頂點(diǎn)權(quán)重?cái)?shù)組是事物開始變得有趣的地方, 該數(shù)組中的每個(gè)條目都包含aiMesh中頂點(diǎn)數(shù)組的索引(請(qǐng)注意,頂點(diǎn)分布在幾個(gè)長(zhǎng)度相同的數(shù)組中)和權(quán)重。 所有頂點(diǎn)權(quán)重的總和必須為1,但是要找到它們,您需要遍歷所有骨骼,并將權(quán)重累加到每個(gè)特定頂點(diǎn)的列表中。在我們的頂點(diǎn)級(jí)別構(gòu)建骨骼信息之后,我們需要處

8、理骨骼變換層級(jí)并生成將加載到著色器中的最終轉(zhuǎn)換,下圖顯示相關(guān)數(shù)據(jù)結(jié)構(gòu):再次,我們從aiScene開始, aiScene對(duì)象包含一個(gè)指向aiNode類對(duì)象的指針,該對(duì)象是一個(gè)節(jié)點(diǎn)層級(jí)的根(換句話說 -一棵樹), 樹中的每個(gè)節(jié)點(diǎn)都有一個(gè)指向其父項(xiàng)的指針以及指向其子節(jié)點(diǎn)的數(shù)組, 這樣我們可以方便地來回遍歷樹。 另外,節(jié)點(diǎn)執(zhí)行從節(jié)點(diǎn)空間變換到其父節(jié)點(diǎn)空間的變換矩陣。 最后,節(jié)點(diǎn)可能有也可能沒有一個(gè)名字。 如果一個(gè)節(jié)點(diǎn)表示父進(jìn)制中的骨骼,則節(jié)點(diǎn)名稱必須與骨骼名稱相匹配。 但是有時(shí)節(jié)點(diǎn)沒有名稱(這意味著沒有相應(yīng)的骨骼),而且他們的工作只是幫助模型分解模型并且沿著一些中間變換。最后一塊拼圖是aiAnimat

9、ion數(shù)組,它也存儲(chǔ)在aiScene對(duì)象中, 單個(gè)aiAnimation對(duì)象表示一系列動(dòng)畫幀,例如“walk”,“run”,“shoot”等。通過在幀之間進(jìn)行內(nèi)插,我們得到與動(dòng)畫名稱相匹配的所需視覺效果。 動(dòng)畫的持續(xù)時(shí)間為每秒鐘的秒數(shù)(例如每秒100個(gè)刻度和25個(gè)刻度,代表4秒動(dòng)畫),這有助于我們對(duì)進(jìn)程進(jìn)行時(shí)間調(diào)整,以使動(dòng)畫在每個(gè)硬件上看起來相同。 另外,動(dòng)畫還有一個(gè)名為通道的aiNodeAnim對(duì)象的數(shù)組。 每個(gè)通道實(shí)際上都是骨骼,全部是它的轉(zhuǎn)變。 該通道包含一個(gè)名稱,該名稱必須與其他一個(gè)節(jié)點(diǎn)在層級(jí)和三個(gè)轉(zhuǎn)換數(shù)組中匹配。為了計(jì)算特定時(shí)間點(diǎn)的最終骨骼變換,我們需要在這三個(gè)陣列中的每一個(gè)中找到與

10、時(shí)間匹配的兩個(gè)入口,并在它們之間插值。 那么我們需要將轉(zhuǎn)換組合成一個(gè)矩陣。 做完之后,我們需要在根中找到相應(yīng)的節(jié)點(diǎn)。 然后我們需要相應(yīng)的通道為父,并進(jìn)行相同的插值過程。 我們把這兩個(gè)變化相乘合起來,直到我們達(dá)到根的層級(jí)。加載模型的源代碼實(shí)現(xiàn)如下:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片bool Mesh:LoadMesh(const string& Filename) / Release the previously loaded mesh (if it exists) Clear(); / Create the VAO glGenVertexArrays

11、(1, &m_VAO); glBindVertexArray(m_VAO); / Create the buffers for the vertices attributes glGenBuffers(ARRAY_SIZE_IN_ELEMENTS(m_Buffers), m_Buffers); bool Ret = false; m_pScene = m_Importer.ReadFile(Filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs); if (m_pScene

12、) m_GlobalInverseTransform = m_pScene-mRootNode-mTransformation; m_GlobalInverseTransform.Inverse(); Ret = InitFromScene(m_pScene, Filename); else printf(Error parsing %s: %sn, Filename.c_str(), m_Importer.GetErrorString(); / Make sure the VAO is not changed from the outside glBindVertexArray(0); re

13、turn Ret; 這是更新到Mesh類的入口點(diǎn),更改標(biāo)記為粗體,有一些我們需要注意的變化。 一個(gè)是導(dǎo)入和aiScene對(duì)象現(xiàn)在是類成員,而不是堆棧變量。(關(guān)于阿Assimp模型的加載會(huì)在后面博客中講解) 原因是在運(yùn)行時(shí),我們將一次又一次地返回到aiScene對(duì)象,因此我們需要擴(kuò)展導(dǎo)入器和場(chǎng)景的范圍。 在一個(gè)真實(shí)的游戲中,您可能想要復(fù)制所需的東西,并以更優(yōu)化的格式存儲(chǔ)。第二個(gè)變化是提取,反轉(zhuǎn)和存儲(chǔ)了根的層級(jí)轉(zhuǎn)換矩陣, 我們繼續(xù)看下去。 請(qǐng)注意,矩陣逆的代碼已從Assimp庫復(fù)制到我們的Matrix4f類中。源代碼的實(shí)現(xiàn)如下所示:cpp view plain copy 在CODE上查看代碼片派生

14、到我的代碼片(mesh.h) struct VertexBoneData uint IDsNUM_BONES_PER_VEREX; float WeightsNUM_BONES_PER_VEREX; (mesh.cpp) bool Mesh:InitFromScene(const aiScene* pScene, const string& Filename) . vector Bones; . Bones.resize(NumVertices); . glBindBuffer(GL_ARRAY_BUFFER, m_BuffersBONE_VB); glBufferData(GL_ARRAY_

15、BUFFER, sizeof(Bones0) * Bones.size(), &Bones0, GL_STATIC_DRAW); glEnableVertexAttribArray(BONE_ID_LOCATION); glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0); glEnableVertexAttribArray(BONE_WEIGHT_LOCATION); glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4,

16、 GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16); . 上面的結(jié)構(gòu)包含了我們?cè)陧旤c(diǎn)級(jí)別所需要的一切, 默認(rèn)情況下,我們有足夠的存儲(chǔ)空間用于四個(gè)骨骼(每個(gè)骨骼的ID和權(quán)重)。 VertexBoneData的結(jié)構(gòu)就像這樣,使之簡(jiǎn)單的傳遞給著色器。 我們已經(jīng)分別在位置0,1和2處獲得了位置,紋理坐標(biāo)和法線。 因此,我們配置的VAO來綁定位置3處的骨骼ID和位置4處的權(quán)重。請(qǐng)注意,我們使用glVertexAttribIPointer而不是glVertexAttribPointer來綁定ID非常重要。 原因是ID是整數(shù)而不是浮

17、點(diǎn)。 注意這一點(diǎn),否則您將在著色器中收到損壞的數(shù)據(jù)。cpp view plain copy 在CODE上查看代碼片派生到我的代碼片(mesh.cpp) void Mesh:LoadBones(uint MeshIndex, const aiMesh* pMesh, vector& Bones) for (uint i = 0 ; i mNumBones ; i+) uint BoneIndex = 0; string BoneName(pMesh-mBonesi-mName.data); if (m_BoneMapping.find(BoneName) = m_BoneMapping.end(

18、) BoneIndex = m_NumBones; m_NumBones+; BoneInfo bi; m_BoneInfo.push_back(bi); else BoneIndex = m_BoneMappingBoneName; m_BoneMappingBoneName = BoneIndex; m_BoneInfoBoneIndex.BoneOffset = pMesh-mBonesi-mOffsetMatrix; for (uint j = 0 ; j mBonesi-mNumWeights ; j+) uint VertexID = m_EntriesMeshIndex.Base

19、Vertex + pMesh-mBonesi-mWeightsj.mVertexId; float Weight = pMesh-mBonesi-mWeightsj.mWeight; BonesVertexID.AddBoneData(BoneIndex, Weight); 上述函數(shù)加載單個(gè)aiMesh對(duì)象的頂點(diǎn)骨骼信息。 它由Mesh : InitMesh()調(diào)用。 除了填充VertexBoneData結(jié)構(gòu)之外,此功能還可以更新骨骼名稱和骨骼ID(由此功能管理的運(yùn)行索引)之間的映射,并將偏移矩陣存儲(chǔ)在基于骨骼ID的向量中。 注意如何計(jì)算頂點(diǎn)ID。 由于頂點(diǎn)ID與單個(gè)網(wǎng)格相關(guān),并且我們將所有網(wǎng)

20、格存儲(chǔ)在單個(gè)向量中,因此將當(dāng)前aiMesh的基本頂點(diǎn)ID從mWeights數(shù)組中添加到頂點(diǎn)ID以獲取絕對(duì)頂點(diǎn)ID。cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void Mesh:VertexBoneData:AddBoneData(uint BoneID, float Weight) for (uint i = 0 ; i ARRAY_SIZE_IN_ELEMENTS(IDs) ; i+) if (Weightsi = 0.0) IDsi = BoneID; Weightsi = Weight; return; / should never get here

21、- more bones than we have space for assert(0); 此功能函數(shù)在VertexBoneData結(jié)構(gòu)中找到一個(gè)空閑插槽,并將骨骼ID和權(quán)重放在其中。 某些頂點(diǎn)將受到少于四個(gè)骨骼的影響,但是由于非現(xiàn)有骨骼的權(quán)重保持為零,這意味著我們可以對(duì)任意數(shù)量的骨骼使用相同的權(quán)重計(jì)算。cpp view plain copy 在CODE上查看代碼片派生到我的代碼片Matrix4f Mesh:BoneTransform(float TimeInSeconds, vector& Transforms) Matrix4f Identity; Identity.InitIdenti

22、ty(); float TicksPerSecond = m_pScene-mAnimations0-mTicksPerSecond != 0 ? m_pScene-mAnimations0-mTicksPerSecond : 25.0f; float TimeInTicks = TimeInSeconds * TicksPerSecond; float AnimationTime = fmod(TimeInTicks, m_pScene-mAnimations0-mDuration); ReadNodeHeirarchy(AnimationTime, m_pScene-mRootNode,

23、Identity); Transforms.resize(m_NumBones); for (uint i = 0 ; i mName.data); const aiAnimation* pAnimation = m_pScene-mAnimations0; Matrix4f NodeTransformation(pNode-mTransformation); const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, NodeName); if (pNodeAnim) / Interpolate scaling and generate sc

24、aling transformation matrix aiVector3D Scaling; CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim); Matrix4f ScalingM; ScalingM.InitScaleTransform(Scaling.x, Scaling.y, Scaling.z); / Interpolate rotation and generate rotation transformation matrix aiQuaternion RotationQ; CalcInterpolatedRota

25、tion(RotationQ, AnimationTime, pNodeAnim); Matrix4f RotationM = Matrix4f(RotationQ.GetMatrix(); / Interpolate translation and generate translation transformation matrix aiVector3D Tation; CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim); Matrix4f TranslationM; TranslationM.InitTransla

26、tionTransform(Translation.x, Translation.y, Translation.z); / Combine the above transformations NodeTransformation = TranslationM * RotationM * ScalingM; Matrix4f GlobalTransformation = ParentTransform * NodeTransformation; if (m_BoneMapping.find(NodeName) != m_BoneMapping.end() uint BoneIndex = m_B

27、oneMappingNodeName; m_BoneInfoBoneIndex.FinalTransformation = m_GlobalInverseTransform * GlobalTransformation * m_BoneInfoBoneIndex.BoneOffset; for (uint i = 0 ; i mNumChildren ; i+) ReadNodeHeirarchy(AnimationTime, pNode-mChildreni, GlobalTransformation); 此函數(shù)遍歷節(jié)點(diǎn)樹,并根據(jù)指定的動(dòng)畫時(shí)間生成每個(gè)節(jié)點(diǎn)/骨骼的最終變換。 它的意義在于它假

28、定網(wǎng)格只有一個(gè)動(dòng)畫序列并且是有限的。 如果你想支持多個(gè)動(dòng)畫,你需要告訴它的動(dòng)畫名稱并在m_pScene- mAnimations 數(shù)組中搜索它, 上面的代碼對(duì)于我們使用的演示網(wǎng)格是足夠好的。從節(jié)點(diǎn)中的mTransformation成員初始化節(jié)點(diǎn)變換,如果節(jié)點(diǎn)不對(duì)應(yīng)于骨骼,那么這是其最終的轉(zhuǎn)換。 如果我們用生成的矩陣來覆蓋它, 這樣做如下:首先我們?cè)趧?dòng)畫的通道數(shù)組中搜索節(jié)點(diǎn)名稱, 然后我們基于動(dòng)畫時(shí)間內(nèi)插縮放矢量,旋轉(zhuǎn)四元數(shù)和平移矢量。 我們將它們組合成一個(gè)矩陣,并將其與我們得到的矩陣相乘(稱為GlobablTransformation), 此函數(shù)是遞歸的,并且以GlobalTransforma

29、tion參數(shù)為單位矩陣為根節(jié)點(diǎn)進(jìn)行調(diào)用。 每個(gè)節(jié)點(diǎn)遞歸地為其所有子節(jié)點(diǎn)調(diào)用此函數(shù),并將其自身的變換作為GlobalTransformation傳遞。 我們從頂部開始會(huì)得到每個(gè)節(jié)點(diǎn)的組合轉(zhuǎn)換鏈。m_BoneMapping數(shù)組將節(jié)點(diǎn)名稱映射到我們生成的索引中,我們將該索引用作存儲(chǔ)m_BoneInfo數(shù)組, 最后的變換計(jì)算如下:我們從節(jié)點(diǎn)偏移矩陣開始,將頂點(diǎn)從其局部空間位置引入其節(jié)點(diǎn)空間, 然后,我們將所有節(jié)點(diǎn)父節(jié)點(diǎn)的組合變換加上我們根據(jù)動(dòng)畫時(shí)間為節(jié)點(diǎn)計(jì)算的特定變換進(jìn)行多次迭代。請(qǐng)注意,我們?cè)谶@里使用Assimp代碼處理數(shù)學(xué)的東西, 我沒有看到將其復(fù)制到我們自己的代碼庫中,所以我只是使用Assimp

30、。cpp view plain copy 在CODE上查看代碼片派生到我的代碼片void Mesh:CalcInterpolatedRotation(aiQuaternion& Out, float AnimationTime, const aiNodeAnim* pNodeAnim) / we need at least two values to interpolate. if (pNodeAnim-mNumRotationKeys = 1) Out = pNodeAnim-mRotationKeys0.mValue; return; uint RotationIndex = FindRo

31、tation(AnimationTime, pNodeAnim); uint NextRotationIndex = (RotationIndex + 1); assert(NextRotationIndex mNumRotationKeys); float DeltaTime = pNodeAnim-mRotationKeysNextRotationIndex.mTime - pNodeAnim-mRotationKeysRIndex.mTime; float Factor = (AnimationTime - (float)pNodeAnim-mRotationKeysRotationIn

32、dex.mTime) / DeltaTime; assert(Factor = 0.0f & Factor mRotationKeysRotationIndex.mValue; const aiQuaternion& EndRotationQ = pNodeAnim-mRotationKeysNextRotationIndex.mValue; aiQuaternion:Interpolate(Out, StartRotationQ, EndRotationQ, Factor); Out = Out.Normalize(); 該方法基于動(dòng)畫時(shí)間插入指定頻道的旋轉(zhuǎn)四元數(shù)(請(qǐng)記住,頻道包含關(guān)鍵四元數(shù)

33、組), 首先,我們找到正好在所需動(dòng)畫時(shí)間之前的關(guān)鍵四元數(shù)的索引。 我們計(jì)算從動(dòng)畫時(shí)間到它之前的鍵的距離與該鍵和下一個(gè)鍵之間的距離之間的比率。 我們需要使用這個(gè)系數(shù)在這兩個(gè)鍵之間插值, 我們使用Assimp代碼進(jìn)行插值并對(duì)結(jié)果進(jìn)行歸一化, 相應(yīng)的位置和縮放方法非常相似,所以在這里沒有引用。cpp view plain copy 在CODE上查看代碼片派生到我的代碼片uint Mesh:FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim) assert(pNodeAnim-mNumRotationKeys 0); for (u

34、int i = 0 ; i mNumRotationKeys - 1 ; i+) if (AnimationTime mRotationKeysi + 1.mTime) return i; assert(0); 此實(shí)用程序方法找到緊接在動(dòng)畫時(shí)間之前的按鍵旋轉(zhuǎn), 如果我們有N個(gè)鍵旋轉(zhuǎn),結(jié)果可以是0到N-2, 動(dòng)畫時(shí)間總是包含在頻道的持續(xù)時(shí)間內(nèi),所以最后一個(gè)鍵(N-1)永遠(yuǎn)不會(huì)是一個(gè)有效的結(jié)果。下面展示的是蒙皮Shader 代碼如下:cpp view plain copy 在CODE上查看代碼片派生到我的代碼片(skinning.vs) #version 330 layout (location = 0) in vec3 Position; layout (location = 1) in vec2 TexCoord; layout (location = 2) i

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論