行列のかける順序やオイラー角への変換など、よく使う行列計算のメモ書き。
DirectXMathを使った行列計算
float rx = 0;//x軸回転(オイラー角)
float rz = 0;//y軸
float ry = 0;//z軸
XMMATRIX mtx;
//オイラー角(ピッチ(rx)、ヨー(ry)、ロール(rz))から行列
mtx = XMMatrixRotationRollPitchYaw(rx, ry, rz);
//行列の掛け算でXMMatrixRotationRollPitchYaw
//ZXYの順番で回転
mtx = XMMatrixRotationZ(rz);
mtx = XMMatrixMultiply(mtx, XMMatrixRotationX(rx));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationY(ry));
//階層構造(階層構造の行列合成)
XMMATRIX mtx_parent = 親階層の行列;
XMMATRIX mtx_child = 子階層の行列;
mtx = XMMatrixMultiply(mtx_child, mtx_parent);
//スケール、回転、移動の行列合成
XMVECTOR scale = スケールXYZ;
XMVECTOR rotate = 回転(クォータニオン);
XMVECTOR translate = 移動XYZ;
mtx = XMMatrixMultiply(
XMMatrixMultiply(
XMMatrixScalingFromVector( scale),
XMMatrixRotationQuaternion( rotate ) ),
XMMatrixTranslationFromVector( translate ) );
//頂点の座標変換
XMVECTOR Vl = ローカル座標;
XMMATRIX mtx_local_world = ローカル⇒ワールド変換行列;
XMVECTOR Vw = XMVector4Transform(Vl, mtx_local_world);
// ボーン差分行列の作成、頂点シェーダ等での計算用
XMMATRIX mtx_init_bone = 初期ボーン行列;
XMMATRIX mtx_now_bone = 現在のボーン行列;
XMMATRIX mtx_diff_bone = XMMatrixMultiply(XMMatrixInverse(nullptr, mtx_init_bone), mtx_now_bone);
XMVECTOR Vbone;// ボーン変形後
XMVECTOR Vskin = 変形前頂点;
Vbone = XMVector4Transform(Vskin, mtx_diff_bone);
//行列からオイラー角
//ZXY回転 XMMatrixRotationRollPitchYawで作成した行列
//mtx = XMMatrixRotationRollPitchYaw(rx,ry,rz);
//mtxからオイラー角を求める
//ただし -π/2 < rx < +π/2
rx = -asinf(XMVectorGetY(mtx.r[2]));
ry = atan2f(XMVectorGetX(mtx.r[2]), XMVectorGetZ(mtx.r[2]));
rz = atan2f(XMVectorGetY(mtx.r[0]), XMVectorGetY(mtx.r[1]));
//ZYX回転
//mtx = XMMatrixRotationZ(rz);
//mtx = XMMatrixMultiply(mtx, XMMatrixRotationY( ry ));
//mtx = XMMatrixMultiply(mtx, XMMatrixRotationX( rx ));
//mtxからオイラー角を求める
//ただし -π/2 < ry < +π/2
rx = -atan2f(XMVectorGetY(mtx.r[2]), XMVectorGetZ(mtx.r[2]));
ry = asinf(XMVectorGetX(mtx.r[2]));
rz = -atan2f(XMVectorGetX(mtx.r[1]), XMVectorGetX(mtx.r[0]));
//XYZ回転
//mtx = XMMatrixRotationX(rx);
//mtx = XMMatrixMultiply(mtx, XMMatrixRotationY( ry ));
//mtx = XMMatrixMultiply(mtx, XMMatrixRotationZ( rz ));
//mtxからオイラー角を求める
//ただし -π/2 < ry < +π/2
rx = atan2f(XMVectorGetZ(mtx.r[1]), XMVectorGetZ(mtx.r[2]));
ry = -asinf(XMVectorGetZ(mtx.r[0]));
rz = atan2f(XMVectorGetY(mtx.r[0]), XMVectorGetX(mtx.r[0]));
// クォータニオンからオイラー角
// 行列に変換して求める
XMVECTOR quat = XMQuaternionIdentity();
mtx = XMMatrixRotationQuaternion(quat);
XMVectorATan2などの三角関数の計算があるけど、記述が複雑になるので標準ライブラリのatan2fなどを使用。
float(通常のレジスタ)とベクトル計算用レジスタの変換は効率が悪いので、すべての計算をXMVECTORとXMMATRIXだけで行えば効率がいい(たぶん、CPUによって違うかも)
おまけ 行列からオイラー角の計算テスト
float sub_angle(float a0, float a1)
{
float sub = a1-a0;
if(sub > XM_PI)sub -= XM_2PI;
if(sub < -XM_PI)sub += XM_2PI;
return sub;
}
bool test_matrix_to_euler()
{
srand(3939);
for(int i=0;i<2000000;++i){
{//ZXY回転
float rx0 = float((double)rand()/double(RAND_MAX))*XM_PI - XM_PIDIV2;//-π/2~+π/2
float ry0 = float((double)rand()/double(RAND_MAX))*XM_2PI - XM_PI;//-π~+π
float rz0 = float((double)rand()/double(RAND_MAX))*XM_2PI - XM_PI;//-π~+π
XMMATRIX mtx = XMMatrixIdentity();
mtx = XMMatrixMultiply(mtx, XMMatrixRotationZ(rz0));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationX(rx0));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationY(ry0));
//同じ
mtx = XMMatrixRotationRollPitchYaw(rx0,ry0,rz0);
float rx,ry,rz;
float sin_rx = -XMVectorGetY(mtx.r[2]);
rx = asinf(sin_rx);
if( sin_rx > 0.999999f){
//計算不可
ry = ry0;
rz = rz0;
}else if( sin_rx < -0.999999f){
//計算不可
ry = ry0;
rz = rz0;
}else{
ry = atan2f(XMVectorGetX(mtx.r[2]),XMVectorGetZ(mtx.r[2]));
rz = atan2f(XMVectorGetY(mtx.r[0]),XMVectorGetY(mtx.r[1]));
}
if( fabs( sub_angle(rx,rx0) ) > 0.001f
|| fabs( sub_angle(ry,ry0) ) > 0.001f
|| fabs( sub_angle(rz,rz0) ) > 0.001f
){
return false;
}
}
{//ZYX回転
float rx0 = float((double)rand()/double(RAND_MAX))*XM_2PI - XM_PI;//-π~+π
float ry0 = float((double)rand()/double(RAND_MAX))*XM_PI - XM_PIDIV2;//-π/2~+π/2
float rz0 = float((double)rand()/double(RAND_MAX))*XM_2PI - XM_PI;//-π~+π
XMMATRIX mtx = XMMatrixIdentity();
mtx = XMMatrixMultiply(mtx, XMMatrixRotationZ(rz0));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationY(ry0));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationX(rx0));
float rx,ry,rz;
float sin_ry = XMVectorGetX(mtx.r[2]);
ry = asinf(sin_ry);
if( sin_ry > 0.999999f){
//計算不可
rx = rx0;
rz = rz0;
}else if( sin_ry < -0.999999f){
//計算不可
rx = rx0;
rz = rz0;
}else{
rx = -atan2f(XMVectorGetY(mtx.r[2]),XMVectorGetZ(mtx.r[2]));
rz = -atan2f(XMVectorGetX(mtx.r[1]),XMVectorGetX(mtx.r[0]));
}
if( fabs( sub_angle(rx,rx0) ) > 0.001f
|| fabs( sub_angle(ry,ry0) ) > 0.001f
|| fabs( sub_angle(rz,rz0) ) > 0.001f
){
return false;
}
}
{//XYZ回転
float rx0 = float((double)rand()/double(RAND_MAX))*XM_2PI - XM_PI;//-π~+π
float ry0 = float((double)rand()/double(RAND_MAX))*XM_PI - XM_PIDIV2;//-π/2~+π/2
float rz0 = float((double)rand()/double(RAND_MAX))*XM_2PI - XM_PI;//-π~+π
XMMATRIX mtx = XMMatrixIdentity();
mtx = XMMatrixMultiply(mtx, XMMatrixRotationX(rx0));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationY(ry0));
mtx = XMMatrixMultiply(mtx, XMMatrixRotationZ(rz0));
float rx,ry,rz;
float sin_ry = -XMVectorGetZ(mtx.r[0]);
ry = asinf(sin_ry);
if( sin_ry > 0.999999f){
//計算不可
rx = rx0;
rz = rz0;
}else if( sin_ry < -0.999999f){
//計算不可
rx = rx0;
rz = rz0;
}else{
rx = atan2f(XMVectorGetZ(mtx.r[1]),XMVectorGetZ(mtx.r[2]));
rz = atan2f(XMVectorGetY(mtx.r[0]),XMVectorGetX(mtx.r[0]));
}
if( fabs( sub_angle(rx,rx0) ) > 0.001f
|| fabs( sub_angle(ry,ry0) ) > 0.001f
|| fabs( sub_angle(rz,rz0) ) > 0.001f
){
return false;
}
}
}
return true;
}
乱数が怪しいけど。