編集免責事項:この回答では便宜上、w == 0のベクトルはベクトルと呼ばれ、w == 1のベクトルはポイントと呼ばれます。FxIIIが指摘したように、それは数学的に正しい用語ではありません。ただし、答えのポイントは用語ではなく、両方のタイプのベクトルを区別する必要があるため、これに固執します。実際的な理由から、この規約はゲーム開発で広く使用されています。
'w'コンポーネントがないと、ベクトルとポイントを区別することはできません。ポイントの場合は1、ベクトルの場合は0です。
最後の行/列に平行移動がある4x4アフィン変換行列をベクトルに乗算すると、ベクトルも平行移動しますが、これは間違っています。点だけを平行移動する必要があります。ベクトルの「w」コンポーネントのゼロがそれを処理します。
マトリックスとベクトルの乗算のこの部分を強調すると、より明確になります。
r.x = ... + a._14 * v.w;
r.y = ... + a._24 * v.w;
r.z = ... + a._34 * v.w;
r.w = ... + a._44 * v.w;
a._14, a._24 and a._34 is the translational part of the affine matrix.
Without a 'w' component one has to set it implicitly to 0 (vector) or to 1 (point)
すなわち、回転軸などのベクトルを変換するのは間違っていますが、結果は単純に間違っています。4番目のコンポーネントをゼロにすると、ポイントを変換する同じマトリックスを使用して回転軸を変換でき、結果が有効になりますマトリックスにスケールがない限り、その長さは保持されます。これがベクトルに必要な動作です。4番目のコンポーネントがなければ、2つの行列(または暗黙的な4番目のパラメーターを持つ2つの異なる乗算関数)を作成し、ポイントとベクトルに対して2つの異なる関数呼び出しを行う必要があります。
最新のCPU(SSE、Altivec、SPU)のベクターレジスタを使用するには、とにかく4x 32ビットの浮動小数点数(128ビットのレジスタ)を渡す必要があり、さらに、通常16バイトのアライメントに注意する必要があります。とにかく、4番目のコンポーネントのスペースを安全に確保する機会はありません。
編集:
質問に対する答えは基本的に
- wコンポーネントを格納します:位置に1、ベクトルに0
- または、異なる行列ベクトル乗算関数を呼び出し、いずれかの関数のいずれかを選択して暗黙的に「w」コンポーネントを渡します
そのうちの1つを選択する必要があります。{x、y、z}のみを保存し、1つの行列ベクトル乗算関数のみを使用することはできません。たとえば、XNAはVector3クラスに2つのTransform関数を持ちTransform
、TransformNormal
両方のアプローチを示し、2つの可能な方法のいずれかで両方の種類のベクトルを区別する必要があることを示すコード例を次に示します。マトリックスで変換することで、世界の位置と視線方向でゲームエンティティを移動します。「w」コンポーネントを使用しない場合、この例で示すように、同じマトリックスとベクトルの乗算を使用できなくなります。とにかくそれを行うと、変換されたものに対して間違った答えが得られますlook_dir
ベクトル。
#include <cstdio>
#include <cmath>
struct vector3
{
vector3() {}
vector3(float _x, float _y, float _z) { x = _x; y = _y; z = _z; }
float x, y, z;
};
struct vector4
{
vector4() {}
vector4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; }
float x, y, z, w;
};
struct matrix
{
// convenience column accessors
vector4& operator[](int col) { return cols[col]; }
const vector4& operator[](int col) const { return cols[col]; }
vector4 cols[4];
};
// since we transform a vector that stores the 'w' component,
// we just need this one matrix-vector multiplication
vector4 operator*( const matrix &m, const vector4 &v )
{
vector4 ret;
ret.x = v.x * m[0].x + v.y * m[1].x + v.z * m[2].x + v.w * m[3].x;
ret.y = v.x * m[0].y + v.y * m[1].y + v.z * m[2].y + v.w * m[3].y;
ret.z = v.x * m[0].z + v.y * m[1].z + v.z * m[2].z + v.w * m[3].z;
ret.w = v.x * m[0].w + v.y * m[1].w + v.z * m[2].w + v.w * m[3].w;
return ret;
}
// if we don't store 'w' in the vector we need 2 different transform functions
// this to transform points (w==1), i.e. positions
vector3 TransformV3( const matrix &m, const vector3 &v )
{
vector3 ret;
ret.x = v.x * m[0].x + v.y * m[1].x + v.z * m[2].x + 1.0f * m[3].x;
ret.y = v.x * m[0].y + v.y * m[1].y + v.z * m[2].y + 1.0f * m[3].y;
ret.z = v.x * m[0].z + v.y * m[1].z + v.z * m[2].z + 1.0f * m[3].z;
return ret;
}
// and this one is to transform vectors (w==0), like a direction-vector
vector3 TransformNormalV3( const matrix &m, const vector3 &v )
{
vector3 ret;
ret.x = v.x * m[0].x + v.y * m[1].x + v.z * m[2].x + 0.0f * m[3].x;
ret.y = v.x * m[0].y + v.y * m[1].y + v.z * m[2].y + 0.0f * m[3].y;
ret.z = v.x * m[0].z + v.y * m[1].z + v.z * m[2].z + 0.0f * m[3].z;
return ret;
}
// some helpers to output the results
void PrintV4(const char *msg, const vector4 &p ) { printf("%-15s: %10.6f %10.6f %10.6f %10.6f\n", msg, p.x, p.y, p.z, p.w ); }
void PrintV3(const char *msg, const vector3 &p ) { printf("%-15s: %10.6f %10.6f %10.6f\n", msg, p.x, p.y, p.z); }
#define STORE_W 1
int main()
{
// suppose we have a "position" of an entity and its
// look direction "look_dir" which is a unit vector
// we will move this entity in the world
// the entity will be moved in the world by a translation
// in x+5 and a rotation of 90 degrees around the y-axis
// let's create that matrix first
// the rotation angle, 90 degrees in radians
float a = 1.570796326794896619f;
matrix moveEntity;
moveEntity[0] = vector4( cos(a), 0.0f, sin(a), 0.0f);
moveEntity[1] = vector4( 0.0f, 1.0f, 0.0f, 0.0f);
moveEntity[2] = vector4(-sin(a), 0.0f, cos(a), 0.0f);
moveEntity[3] = vector4( 5.0f, 0.0f, 0.0f, 1.0f);
#if STORE_W
vector4 position(0.0f, 0.0f, 0.0f, 1.0f);
// entity is looking towards the positive x-axis
vector4 look_dir(1.0f, 0.0f, 0.0f, 0.0f);
// move the entity using the matrix
// we can use the same function for the matrix-vector multiplication to transform
// the position and the unit vector since we store 'w' in the vector
position = moveEntity * position;
look_dir = moveEntity * look_dir;
PrintV4("position", position);
PrintV4("look_dir", look_dir);
#else
vector3 position(0.0f, 0.0f, 0.0f);
// entity is looking towards the positive x-axis
vector3 look_dir(1.0f, 0.0f, 0.0f);
// move the entity using the matrix
// we have to call 2 different transform functions one to transform the position
// and the other one to transform the unit-vector since we don't
// store 'w' in the vector
position = TransformV3(moveEntity, position);
look_dir = TransformNormalV3(moveEntity, look_dir);
PrintV3("position", position);
PrintV3("look_dir", look_dir);
#endif
return 0;
}
初期エンティティ状態:
position : 0.000000 0.000000 0.000000 1.000000
look_dir : 1.000000 0.000000 0.000000 0.000000
これで、x + 5の平行移動とy軸を中心とした90度の回転を伴う変換がこのエンティティに適用されます。変換後の正しい答えは次のとおりです。
position : 5.000000 0.000000 0.000000 1.000000
look_dir : 0.000000 0.000000 1.000000 0.000000
上記のいずれかの方法でw == 0のベクトルとw == 1の位置を区別する場合にのみ、正しい答えが得られます。
r.x = ... + a._14*v.w;
r.y = ... + a._24*v.w;
r.z = ... + a._34*v.w;
r.w = ... + a._44*v.w;
詳細については、私の答えを見て