편집 면책 조항 :이 답변의 편의를 위해 w = = 0 인 벡터는 벡터라고하고 w = = 1 인 포인트라고합니다. FxIII가 지적했듯이 수학적으로 올바른 용어는 아닙니다. 그러나 대답의 요점은 용어가 아니라 두 유형의 벡터를 구별해야 할 필요가 있기 때문에 그 점을 고수하겠습니다. 실용적인 이유로이 규칙은 게임 개발에 널리 사용됩니다.
'w'성분이없는 벡터와 점을 구별하는 것은 불가능합니다. 포인트는 1, 벡터는 0입니다.
벡터에 마지막 행 / 열에 번역이있는 4x4 아핀 변환 행렬이 곱해지면 벡터도 변환됩니다. 잘못된 점만 변환해야합니다. 벡터의 'w'성분의 0은 그 점을 처리합니다.
행렬-벡터 곱셈의이 부분을 강조하면 더 명확 해집니다.
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 번째 성분을 0으로하여 회전 축을 변환하기 위해 점을 변환하는 동일한 행렬을 계속 사용할 수 있으며 결과는 유효합니다. 행렬에 스케일이없는 한 길이는 유지됩니다. 이것이 벡터에 대해 원하는 동작입니다. 4 번째 성분이 없으면 2 개의 행렬 (또는 내재 된 4 번째 매개 변수로 2 개의 다른 곱셈 함수를 작성하고 점과 벡터에 대해 2 개의 다른 함수 호출을 작성해야합니다.
최신 CPU (SSE, Altivec, SPU)의 벡터 레지스터를 사용하려면 어쨌든 4x 32 비트 부동 소수점 (128 비트 레지스터)을 전달해야하며 정렬 (일반적으로 16 바이트)을 처리해야합니다. 따라서 4 번째 구성 요소를위한 공간을 확보 할 기회가 없습니다.
편집 :
질문에 대한 대답은 기본적으로
- w- 컴포넌트를 저장하십시오 : 위치의 경우 1, 벡터의 경우 0
- 또는 다른 행렬 벡터 곱셈 함수를 호출하고 두 함수 중 하나를 선택하여 'w'구성 요소를 암시 적으로 전달합니다.
그중 하나를 선택해야합니다. {x, y, z} 만 저장하고 여전히 하나의 행렬-벡터 곱셈 함수 만 사용할 수는 없습니다. 예컨대 XNA 2가에서 변환 함수함으로써 후자의 방법을 사용 Vector3의 불리는 클래스 Transform
및TransformNormal
다음은 두 가지 방법을 모두 보여주고 가능한 두 가지 방법 중 하나로 두 종류의 벡터를 구분할 필요성을 보여주는 코드 예제입니다. 우리는 게임 엔터티를 행렬로 변형시켜 월드의 위치와 모양을 바꾸어 움직일 것입니다. '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;
자세한 내용은 제 답변을보십시오