読者です 読者をやめる 読者になる 読者になる

syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

HLSLの行列乗算がmul()関数な理由

GLSLでは行列の乗算に*演算子を使います。

// GLSL
mat4 mTransformA;
mat4 mTransformB;
vec4 vIn;
vec4 vOut = mTransformB * mTransformA * vIn;

一方HLSLではmul()関数を使います。

// HLSL
float4x4 mTransformA;
float4x4 mTransformB;
float4 vIn;
float4 vOut = mul(mul(vIn, mTransformA), mTransformB);

ときどき「なんでHLSLは乗算が*演算子じゃなくてmul()関数なんだ? オブジェクト指向(というかジェネリックプログラミング)的に言えば*演算子を使うべきだろう」という議論を見かけるのですが、HLSLにも次元のまったく同じ行列・ベクトルに対する*演算子はあります。しかしこれは実は「数学的乗算」ではなく、単に各成分を単純に掛け合わせるだけの演算になります。

// HLSL
float4 vPosA;
float4 vPosB;
float4 vStarAB = vPosA * vPosB;
float4x4 mTransformA;
float4x4 mTransformB;
float4x4 mStarAB = mTransformA * mTransformB;

つまり、HLSLで*演算子を使った結果は

vStarAB =
{
  vA.x * vB.x, vA.y * vB.y, vA.z * vB.z, vA.w * vB.w
};

mStarAB =
{
  mA._11 * mB._11, mA._12 * mB._12, mA._13 * mB._13, mA._14 * mB._14,
  mA._21 * mB._21, mA._22 * mB._22, mA._23 * mB._23, mA._24 * mB._24,
  mA._31 * mB._31, mA._32 * mB._32, mA._33 * mB._33, mA._34 * mB._34,
  mA._41 * mB._41, mA._42 * mB._42, mA._43 * mB._43, mA._44 * mB._44,
};

となります。これは一般的な代数学で使われる行列の乗算ではありません。また、この演算は結果が被演算数の順序に依存しません。
おそらくHLSLの設計としては、順序依存性のある一般的な行列積は演算子で提供したくなかったのだと思われます。一方、D3DXやXNAには、C++/C#向けの乗算演算子オーバーロードがありますが、使い方を誤ると混乱のもとになります。

成分ごとの演算 (DirectX HLSL)

クォータニオンの使い方 - ひにけにGD - Site Home - MSDN Blogs

ちなみに個人的な趣向としてはHLSLのほうが好みです。