Penyangkalan EDIT : Untuk kenyamanan dalam jawaban ini, vektor dengan w == 0 disebut vektor dan dengan w == 1 disebut poin. Meskipun seperti yang ditunjukkan oleh FxIII, itu bukan terminologi yang benar secara matematis. Namun, karena titik jawabannya bukanlah terminologi, tetapi kebutuhan untuk membedakan kedua jenis vektor, saya akan menaatinya. Untuk alasan praktis, konvensi ini banyak digunakan dalam pengembangan game.
Tidak mungkin membedakan antara vektor dan titik tanpa komponen 'w'. Ini adalah 1 untuk poin dan 0 untuk vektor.
Jika vektor dikalikan dengan matriks transformasi affine 4x4 yang memiliki terjemahan pada baris / kolom terakhir, vektor juga akan diterjemahkan, yang salah, hanya titik yang harus diterjemahkan. Nol dalam komponen 'w' dari vektor menangani hal itu.
Menyoroti bagian ini dari perkalian matriks-vektor membuatnya lebih jelas:
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)
Yaitu akan salah untuk menerjemahkan vektor, misalnya sumbu rotasi, hasilnya hanya salah, Dengan memiliki komponen ke-4 Anda masih dapat menggunakan matriks yang sama yang mengubah titik untuk mengubah sumbu rotasi dan hasilnya akan valid dan panjangnya dipertahankan selama tidak ada skala dalam matriks. Itu adalah perilaku yang Anda inginkan untuk vektor. Tanpa komponen ke-4 Anda harus membuat 2 matriks (atau 2 fungsi perkalian yang berbeda dengan parameter ke-4 yang tersirat. Dan membuat 2 pemanggilan fungsi yang berbeda untuk titik dan vektor.
Untuk menggunakan register vektor CPU modern (SSE, Altivec, SPU), Anda harus tetap mengapung 4x 32 bit (register 128 bit), ditambah Anda harus menjaga perataan, biasanya 16 byte. Jadi, Anda tidak memiliki kesempatan untuk mengamankan ruang untuk komponen ke-4.
EDIT:
Jawaban atas pertanyaan pada dasarnya
- Baik menyimpan komponen-w: 1 untuk posisi dan 0 untuk vektor
- Atau panggil fungsi penggandaan matriks-vektor yang berbeda dan secara implisit meneruskan komponen 'w' dengan memilih salah satu dari kedua fungsi tersebut
Seseorang harus memilih salah satunya, tidak mungkin menyimpan hanya {x, y, z} dan masih menggunakan hanya satu fungsi penggandaan matriks-vektor. XNA misalnya menggunakan pendekatan yang terakhir dengan memiliki 2 fungsi Transform di kelas Vector3 , yang disebut Transform
danTransformNormal
Berikut adalah contoh kode yang menunjukkan kedua pendekatan dan menunjukkan kebutuhan untuk membedakan kedua jenis vektor dalam 1 dari 2 cara yang mungkin. Kami akan memindahkan entitas permainan dengan posisi dan arah pandang di dunia dengan mengubahnya dengan sebuah matriks. Jika kita tidak menggunakan komponen 'w', kita tidak bisa menggunakan perkalian matriks-vektor yang sama lagi, seperti ditunjukkan contoh ini. Jika kita tetap melakukannya, kita akan mendapatkan jawaban yang salah untuk look_dir
vektor yang diubah :
#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;
}
Status Entitas Awal:
position : 0.000000 0.000000 0.000000 1.000000
look_dir : 1.000000 0.000000 0.000000 0.000000
Sekarang transformasi dengan terjemahan x + 5 dan rotasi 90 derajat di sekitar sumbu y akan diterapkan pada entitas ini. Jawaban yang benar setelah tranformasi adalah:
position : 5.000000 0.000000 0.000000 1.000000
look_dir : 0.000000 0.000000 1.000000 0.000000
Kami hanya akan mendapatkan jawaban yang benar jika kami membedakan vektor dengan w == 0 dan posisi dengan w == 1 dalam salah satu cara yang disajikan di atas.
r.x = ... + a._14*v.w;
r.y = ... + a._24*v.w;
r.z = ... + a._34*v.w;
r.w = ... + a._44*v.w;
lihat jawaban saya untuk perincian