Zenn
🦍

[UE5][Tips] マテリアルでオブジェクトのRotationを取得する方法

2025/01/28に公開

オブジェクトのScale値を取得する関数はありますが(「ObjectScale」ノード)、オブジェクトのRotation値を取得する関数はなかったので、自作してみました。

結果動画

今回自作したMF_ObjectRotationのノードの返り値である float3(x=Roll, y=Pitch, z=Yaw) を、「DebugFloat3Values」ノードで可視化してみます。

↓のGIF動画のように、TransformのRotationに入力した値(= Cubeにかけた回転値)と同じ値が、オブジェクト表面に表示されています。

No GIF
1
2

全体図

こちらが今回自作したMF_ObjectRotationノードの全体図です。

コアの部分はCustomノードで、HLSLで書いています。

float pitchRad = asin(ForwardVec.z);
float pitch = degrees(pitchRad);

float roll = 0.0f;
float yaw = 0.0f;

if (cos(pitchRad) != 0.0f)
{
    float rollRad = atan2(-RightVec.z, UpVec.z);
    roll = degrees(rollRad);

    float yawRad = atan2(ForwardVec.y, ForwardVec.x);
    yaw = degrees(yawRad);
}
else
{
    roll = 0.0f;

    float yawRad = atan2(-RightVec.x, RightVec.y);
    yaw = degrees(yawRad);
}

return float3(roll, pitch, yaw);

解説

Transformからカスタム座標系の算出

最初に、オブジェクトにかかっているTransformから、カスタム座標系を算出します。

カスタム座標系からオイラー角を算出する式を構築

次に、カスタム座標系からオイラー角(Roll, Pitch, Yaw)を算出する式を構築します。
ここが今回の本丸部分になります。


まず、回転行列RRの各要素を次のように表すとします。

R=[m00m01m02m10m11m12m20m21m22] R = \begin{bmatrix} m_{00} & m_{01} & m_{02} \\ m_{10} & m_{11} & m_{12} \\ m_{20} & m_{21} & m_{22} \end{bmatrix}

また、各軸が[Forward,Right,Up][Forward, Right, Up]のカスタム座標系は、行列の形では次のように表せます。

R=[Forward.xRight.xUp.xForward.yRight.yUp.yForward.zRight.zUp.z] R = \begin{bmatrix} Forward.x & Right.x & Up.x \\ Forward.y & Right.y & Up.y \\ Forward.z & Right.z & Up.z \end{bmatrix}

Roll,Pitch,YawRoll, Pitch, Yawそれぞれの回転行列は次のようになります。

RRoll=[1000cosθRollsinθRoll0sinθRollcosθRoll] R_{Roll} = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos\theta_{Roll} & -\sin\theta_{Roll} \\ 0 & \sin\theta_{Roll} & \cos\theta_{Roll} \end{bmatrix}
RPitch=[cosθPitch0sinθPitch010sinθPitch0cosθPitch] R_{Pitch} = \begin{bmatrix} \cos\theta_{Pitch} & 0 & \sin\theta_{Pitch} \\ 0 & 1 & 0 \\ -\sin\theta_{Pitch} & 0 & \cos\theta_{Pitch} \end{bmatrix}
RYaw=[cosθYawsinθYaw0sinθYawcosθYaw0001] R_{Yaw} = \begin{bmatrix} \cos\theta_{Yaw} & -\sin\theta_{Yaw} & 0 \\ \sin\theta_{Yaw} & \cos\theta_{Yaw} & 0 \\ 0 & 0 & 1 \end{bmatrix}

オイラー角では回転順が最終結果に影響します。
UnrealEngineではRoll,Pitch,YawRoll, Pitch, Yawの順で回転がかけられますが、これは
YawPitchRollYaw → Pitch → Roll
のように左からかけるので、合成後の回転行列は
RYawRPitchRRollR_{Yaw}R_{Pitch}R_{Roll}
で求まります。

RYaw,Pitch,Roll=[cosθPitchcosθYawsinθRollsinθPitchcosθYawcosθRollsinθYawcosθRollsinθPitchcosθYaw+sinθRollsinθYawcosθPitchsinθYawsinθRollsinθPitchsinθYaw+cosθRollcosθYawcosθRollsinθPitchsinθYawsinθRollcosθYawsinθPitchsinθRollcosθPitchcosθRollcosθPitch] R_{Yaw,Pitch,Roll} = \begin{bmatrix} \cos\theta_{Pitch}\cos\theta_{Yaw} & \sin\theta_{Roll}\sin\theta_{Pitch}\cos\theta_{Yaw} - \cos\theta_{Roll}\sin\theta_{Yaw} & \cos\theta_{Roll}\sin\theta_{Pitch}\cos\theta_{Yaw} + \sin\theta_{Roll}\sin\theta_{Yaw} \\ \cos\theta_{Pitch}\sin\theta_{Yaw} & \sin\theta_{Roll}\sin\theta_{Pitch}\sin\theta_{Yaw} + \cos\theta_{Roll}\cos\theta_{Yaw} & \cos\theta_{Roll}\sin\theta_{Pitch}\sin\theta_{Yaw} -\sin\theta_{Roll}\cos\theta_{Yaw} \\ -\sin\theta_{Pitch} & \sin\theta_{Roll}\cos\theta_{Pitch} & \cos\theta_{Roll}\cos\theta_{Pitch} \end{bmatrix}

上記より、

m20=Forward.z=sinθPitch m_{20} = Forward.z = -\sin\theta_{Pitch}
θPitch=arcsin(Forward.z) ★ \enspace \theta_{Pitch} = \arcsin(-Forward.z)

となり、まずPitchPitchが求まります。


続いて、RollRollUpUpを求めますが、cosθPitch0\cos\theta_{Pitch} \neq 0 のときと、cosθPitch=0\cos\theta_{Pitch} = 0 のときで場合分けします。


まず、cosθPitch0\cos\theta_{Pitch} \neq 0 のとき

m21=Right.z=sinθRollcosθPitchsinθRoll=Right.zcosθPitch m_{21} = Right.z = \sin\theta_{Roll}\cos\theta_{Pitch} \\ \sin\theta_{Roll} = \frac{Right.z}{\cos\theta_{Pitch}}
m22=Up.z=cosθRollcosθPitchcosθRoll=Up.zcosθPitch m_{22} = Up.z = \cos\theta_{Roll}\cos\theta_{Pitch} \\ \cos\theta_{Roll} = \frac{Up.z}{\cos\theta_{Pitch}}

より、

sinθRollcosθRoll=tanθRoll=Right.zUp.z \frac{\sin\theta_{Roll}}{\cos\theta_{Roll}} = \tan\theta_{Roll} = \frac{Right.z}{Up.z}
θRoll=arctan(Right.zUp.z) ★ \enspace \theta_{Roll} = \arctan(\frac{Right.z}{Up.z})

で、RollRollが求まります。
また、

m10=Forward.y=cosθPitchsinθYawsinθYaw=Forward.ycosθPitch m_{10} = Forward.y = \cos\theta_{Pitch}\sin\theta_{Yaw} \\ \sin\theta_{Yaw} = \frac{Forward.y}{\cos\theta_{Pitch}}
m00=Forward.x=cosθPitchcosθYawcosθYaw=Forward.xcosθPitch m_{00} = Forward.x = \cos\theta_{Pitch}\cos\theta_{Yaw} \\ \cos\theta_{Yaw} = \frac{Forward.x}{\cos\theta_{Pitch}}

より、

sinθYawcosθYaw=tanθYaw=Forward.yForward.x \frac{\sin\theta_{Yaw}}{\cos\theta_{Yaw}} = \tan\theta_{Yaw} = \frac{Forward.y}{Forward.x}
θYaw=arctan(Forward.yForward.x) ★ \enspace \theta_{Yaw} = \arctan(\frac{Forward.y}{Forward.x})

で、YawYawが求まります。


次に、cosθPitch=0\cos\theta_{Pitch} = 0 のとき
このときはジンバルロックが発生してForwardForward軸とUpUp軸の回転が同軸での回転となります。
ここで、θRoll=0\theta_{Roll} = 0 と仮定(すなわち、sinθRoll=0\sin\theta_{Roll} = 0cosθRoll=1\cos\theta_{Roll} = 1)すると、回転行列は、

RYaw,Pitch,Roll=[0sinθYawsinθPitchcosθYaw0cosθYawsinθPitchsinθYawsinθPitch00] R_{Yaw,Pitch,Roll} = \begin{bmatrix} 0 & -\sin\theta_{Yaw} & \sin\theta_{Pitch}\cos\theta_{Yaw} \\ 0 & \cos\theta_{Yaw} & \sin\theta_{Pitch}\sin\theta_{Yaw} \\ -\sin\theta_{Pitch} & 0 & 0 \end{bmatrix}

となります。
ここから、

tanθYaw=sinθYawcosθYaw=m01m11=Right.xRight.y \tan\theta_{Yaw} = \frac{\sin\theta_{Yaw}}{\cos\theta_{Yaw}} = -\frac{m_{01}}{m_{11}} = -\frac{Right.x}{Right.y}
θYaw=arctan(Right.xRight.y) ★ \enspace \theta_{Yaw} = \arctan(-\frac{Right.x}{Right.y})

で、YawYawが求まります。


以上より、

θRoll={arctan(Right.zUp.z)(cosθPitch0)0(cosθPitch=0)θPitch=arcsin(Forward.z)θYaw={arctan(Forward.yForward.x)(cosθPitch0)arctan(Right.xRight.y)(cosθPitch=0) \begin{aligned} &\theta_{Roll} = \begin{cases} \arctan(\frac{Right.z}{Up.z}) &\text{} (\cos\theta_{Pitch} \neq 0) \\ 0 &\text{} (\cos\theta_{Pitch} = 0) \end{cases} \\ &\theta_{Pitch} = \arcsin(-Forward.z) \\ &\theta_{Yaw} = \begin{cases} \arctan(\frac{Forward.y}{Forward.x}) &\text{} (\cos\theta_{Pitch} \neq 0) \\ \arctan(-\frac{Right.x}{Right.y}) &\text{} (\cos\theta_{Pitch} = 0) \end{cases} \\ \end{aligned}

CustomノードでHLSLの記述

上記までで求めたカスタム座標系とオイラー角算出の式を使って、オイラー角を返すCustomノードとしてHLSLコードを起こします。


Customノードの引数は、カスタム座標系のForwardベクトル、Rightベクトル、Upベクトルの3つです。
それら引数のベクトルをもとに、まずPitchから求めます。
RollとYawを求めるときには、ジンバルロックを考慮して、if (cos(pitchRad) != 0.0f)で場合分けします。
最終的には、float3(x=roll, y=pitch, z=yaw) として値を返しています。

参考ページ

回転行列⇔オイラー角の相互変換について、こちらのページを大いに参考にさせていただきました。
https://qiita.com/aa_debdeb/items/3d02e28fb9ebfa357eaf

Discussion

ログインするとコメントできます