DirectXMathメモ 行列からオイラー角など

投稿日:

行列のかける順序やオイラー角への変換など、よく使う行列計算のメモ書き。

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;
}

乱数が怪しいけど。

コメントを残す

メールアドレスが公開されることはありません。

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.