日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UE自带重定向原理

發布時間:2023/12/13 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UE自带重定向原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

UE自帶重定向方法驗證

核心源碼在VS的解決方案中的位置:

  • UE4\Source\Developer\AssetTools\Private\AssetTypeActions\AnimSequence.cpp中第3237行RemapTracksToNewSkeleton函數

跳轉方法

  • AssetTypeActions_AnimationAsset.cpp的RetargetNonSkeletonAnimationHandler函數調用了RetargetAnimationHandler
  • 跳轉到EditorAnimUtils.cpp的RetargetAnimations函數
  • 跳轉到AnimationAsset.cpp的ReplaceSkeleton函數
  • 最后跳轉到AnimSequence.cpp的RemapTracksToNewSkeleton函數

核心源碼理論

  • 獲取全局(Component Space)齊次矩陣的方法
    源碼第3277行,有一個FillUpTransformBasedOnRig函數,各種跳轉以后,可以找到AnimationRuntime.cpp中的FillUpComponentSpaceTransforms函數,其中一行代碼:

    ComponentSpaceTransforms[Index] = BoneSpaceTransforms[Index] * ComponentSpaceTransforms[ParentIndex];

    普通但是特別,因為以前寫代碼時候的正常操作是
    當前關節的全局旋轉=父關節全局旋轉?子關節局部旋轉當前關節的全局旋轉 = 父關節全局旋轉*子關節局部旋轉 =?
    但是UE是反過來的,為:
    當前關節的全局旋轉=子關節局部旋轉?父關節全局旋轉當前關節的全局旋轉 = 子關節局部旋轉 * 父關節全局旋轉 =?
    這一點要注意,寫代碼時候區分好

  • 新舊骨骼依據世界坐標系遷移位移
    源碼第3311行計算了比率:

    float OldTranslationSize = OldTranslation.Size(); float NewTranslationSize = NewTranslation.Size();OldToNewTranslationRatio[NodeIndex] = (FMath::IsNearlyZero(OldTranslationSize)) ? 1.f/*do not touch new translation size*/ : NewTranslationSize / OldTranslationSize;

    在第3372行把比率應用到新的動畫軌跡上:

    AnimatedLocalKey.ScaleTranslation(OldToNewTranslationRatio[NodeIndex]);
  • 新舊骨骼依據refPose(Tpose/Apose)計算旋轉數據的遷移矩陣

    源碼第3271行的注釋內容:

    first calculate component space ref pose to get the relative transform betweentwo ref poses. It is very important update ref pose before getting here

    源碼第3299行的注釋和3414行的實現內容:

    // theta (RelativeToNewTransform) = (P1*R1)^(-1) * P2*R2 where theta => P1*R1*theta = P2*R2 RelativeToNewSpaceBases[NodeIndex] = NewSpaceBases[NodeIndex].GetRelativeTransform(OldSpaceBases[NodeIndex]);

    結合UE的重定向操作,可以大概推測出新舊骨骼都有一個refpose(一般為Tpose或者Apose),然后由于建模流程中綁骨操作可能導致每個模型的局部坐標系不一樣,所以需要依據refPose計算舊骨骼到新骨骼的遷移矩陣。

  • 動畫數據遷移
    源碼第3413的注釋:

    now convert to the new space and save to local spaces

    就是將舊的動畫數據遷移到新的動畫數據的全局空間(component space)中,然后再轉化為局部旋轉

    所以在第3414行代碼:

    ConvertedSpaceAnimations[SrcTrackIndex][Key] = RelativeToNewSpaceBases[NodeIndex] * ComponentSpaceAnimations[SrcTrackIndex][Key];

    將遷移矩陣施加到舊的骨骼全局矩陣上,就得到了新骨骼的全局矩陣。

    最后計算局部旋轉即可:

    • 對于非根關節:

      ConvertedSpaceAnimations[RotParentTrackIndex][Key].GetRotation().Inverse() * ConvertedSpaceAnimations[SrcTrackIndex][Key].GetRotation()
    • 對于根關節

      ConvertedSpaceAnimations[SrcTrackIndex][Key].GetRotation()

數值驗證

先預備幾個函數:

  • UE4的歐拉角到旋轉矩陣的轉換:

    # UE的歐拉角轉旋轉矩陣 def euler_to_rotMat(yaw, pitch, roll):yaw = np.deg2rad(yaw)pitch = np.deg2rad(pitch)roll = np.deg2rad(roll)Rz_yaw = np.array([[np.cos(yaw), -np.sin(yaw), 0],[np.sin(yaw), np.cos(yaw), 0],[ 0, 0, 1]])Ry_pitch = np.array([[ np.cos(pitch), 0, np.sin(pitch)],[ 0, 1, 0],[-np.sin(pitch), 0, np.cos(pitch)]])Rx_roll = np.array([[1, 0, 0],[0, np.cos(roll), -np.sin(roll)],[0, np.sin(roll), np.cos(roll)]])rotMat = np.dot(Rz_yaw, np.dot(Ry_pitch, Rx_roll))return rotMat
  • 兩個向量的夾角

    def anglebetween(v1,v2):v1 = v1/np.linalg.norm(v1)v2 = v2/np.linalg.norm(v2)dot_product = np.dot(v1, v2)angle = np.arccos(dot_product)return np.rad2deg(angle)

假設我們有兩個關節,分別稱為父關節和子關節:

  • 源模型的Tpose下,子關節在父關節下的局部坐標和父關節全局旋轉量為:

    oriOffset = np.array([-0.000000,35.000000,-0.000000]) #子關節局部坐標 oriSkelT = euler_to_rotMat(0.000000, 0.000000, -89.996216)#父關節全局旋轉
  • 目標模型的Tpose下,子關節在父關節下的局部坐標和父關節全局旋轉量為:

    tarOffset = np.array([45.206524,-0.000000,-0.000002]) #子關節局部坐標 tarSkelT = euler_to_rotMat(-90.887756, 89.786018, 89.115906)#父關節全局旋轉
  • 由于兩個模型的Tpose可能有細微差距,所以先看看兩個Tpose在世界坐標系下的夾角:

    print(anglebetween(np.dot(oriSkelT, oriOffset), np.dot(tarSkelT, tarOffset))) #輸出:0.21776551527114438

接下來看骨骼動畫的重定向部分,重定向過程就不截圖了,按照官網說的,先全部遞歸選擇skeleton,然后把root和pelvis關節調整成Animation Scaled

然后我們先提取出某一幀的源模型和目標模型的關節數值:

  • 源模型的父關節全局旋轉量為:

    oriSkelAnim = euler_to_rotMat(-63.208164, 9.146088, -74.355972)
  • 目標模型的父關節全局旋轉量為:

    tarSkelAnim = euler_to_rotMat(56.725945, 72.118958, -58.820042)
  • 看看他倆在同一個世界坐標系下的角度差:

    print(anglebetween(np.dot(oriSkelAnim, oriOffset), np.dot(tarSkelAnim, tarOffset))) #0.21275403661014525

    發現重定向完畢以后,和重定向之前的角度差距不大

旋轉驗證

看看如何通過Tpose數據(oriSkelT、tarSkelT)和源模型的某幀數據(oriSkelAnim)計算得到新的模型幀數據(tarSkelAnim),經過源碼分析發現,UE的計算方法如下:
tarSkelAnim=oriSkelAnim?oriskelT?1?tarskelTtarSkelAnim = oriSkelAnim*oriskelT^{-1}*tarskelT tarSkelAnim=oriSkelAnim?oriskelT?1?tarskelT
所以可以利用一個緩存矩陣把oriskelT?1?tarskelToriskelT^{-1}*tarskelToriskelT?1?tarskelT存下來,這樣就不用每幀都算這一項了。

式子知道了,直接驗證:

tmpMat = np.dot(oriSkelAnim,np.matmul(np.linalg.inv(oriSkelT),tarSkelT))

打印出我們算的,和UE里面提取的看看:

print(tmpMat) print(tarSkelAnim) ''' [[ 0.16835118 -0.87957756 -0.44497325][ 0.25672688 -0.39671296 0.8813116 ][-0.95170856 -0.26260644 0.15902411]] [[ 0.1684567 -0.87956606 -0.44495605][ 0.25670405 -0.39668436 0.88133112][-0.95169605 -0.26268815 0.15896403]] '''

基本一模一樣,說明算法沒錯。

位移驗證

通過源碼分析,其實就是:
目標模型動畫幀全局坐標=目標模型Tpose全局位移長度源模型Tpose全局位移長度?源模型動畫幀全局坐標目標模型動畫幀全局坐標 = \frac{目標模型Tpose全局位移長度}{源模型Tpose全局位移長度}*源模型動畫幀全局坐標 =TposeTpose??
提取了3幀數據的全局位移量:

# Tpose下源模型和目標模型(world positon) tran1 = np.array([0.000000,-0.005569,83.999969]) tran2 = np.array([0.002693,0.000016,106.468102])# 第10幀源模型和目標模型(world positon) tran3 = np.array([-162.444809,-66.448837,83.561867]) tran4 = np.array([(-205.898224,-84.200836,105.923523)]) # 第1000幀源模型和目標模型(world positon) tran5 = np.array([-135.590073,-85.603470,81.906609]) tran6 = np.array([-171.862610,-108.480934,103.825958])

打印看看根關節位移量的長度比率:

print(np.linalg.norm(tran1)/np.linalg.norm(tran2)) print(np.linalg.norm(tran3)/np.linalg.norm(tran4)) print(np.linalg.norm(tran5)/np.linalg.norm(tran6)) ''' 0.7889684100664619 0.7889692163816936 0.7889695268366821 '''

幾乎一毛一樣,那再看看,x,y,zx,y,zx,y,z分別的比率

print(tran3/tran4) print(tran5/tran6) ``` [[0.78895682 0.78917075 0.78888867]] [0.78894457 0.78911074 0.78888373] ```

可以發現坐標各自的比率和長度比率其實是一致的,可能由于計算精度有少量偏差。

后記

本文主要針對UE4的動畫數據重定向原理做了計算方法的探索,當然后續還會繼續對源碼進行深究,包括IK、實時重定向之類的。

完整的python實現放在微信公眾號的簡介中描述的github中,有興趣可以去找找。同時文章也同步到微信公眾號中,有疑問或者興趣歡迎公眾號私信。

總結

以上是生活随笔為你收集整理的UE自带重定向原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。