Apakah saya memerlukan komponen 'w' di kelas Vector saya?

21

Asumsikan Anda sedang menulis kode matriks yang menangani rotasi, terjemahan dll untuk ruang 3d.

Sekarang matriks transformasi harus 4x4 agar sesuai dengan komponen terjemahan.

Namun, Anda sebenarnya tidak perlu menyimpan wkomponen dalam vektor bukan?

Bahkan dalam pembagian perspektif, Anda cukup menghitung dan menyimpan di wluar vektor, dan membagi perspektif sebelum kembali dari metode.

Sebagai contoh:

// post multiply vec2=matrix*vector
Vector operator*( const Matrix & a, const Vector& v )
{
  Vector r ;
  // do matrix mult
  r.x = a._11*v.x + a._12*v.y ...

  real w = a._41*v.x + a._42*v.y ...

  // perspective divide
  r /= w ;

  return r ;
}

Apakah ada gunanya menyimpan wdi kelas Vector?

bobobobo
sumber
2
Itu bukan implementasi untuk perkalian vektor matriks normal, pembagian perspektif tidak termasuk di sana. Itu juga cukup menyesatkan, karena bagian perhitungan yang salah disorot. Jika Anda ingin mencari tahu untuk apa komponen-w, lihat implementasi yang lengkap, maka Anda melihat bahwa baris / kolom terakhir (bagian terjemahan) dari matriks hanya diterapkan, jika komponen-w adalah 1, yaitu untuk poin. Anda harus menyoroti bagian-bagian itu: 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
Maik Semder

Jawaban:

27

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

  1. Baik menyimpan komponen-w: 1 untuk posisi dan 0 untuk vektor
  2. 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 TransformdanTransformNormal

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_dirvektor 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.

Maik Semder
sumber
@Maik Semder Anda sedikit salah ... Tidak mungkin untuk membedakan antara vektor satu poin karena mereka adalah hal yang sama! (Mereka isomorfik) 1 untuk vektor dan 0 untuk vektor arah tak terbatas (seperti yang saya katakan dalam jawaban saya) . Sisa tanggapan memiliki sedikit rasa karena asumsi yang salah.
FxIII
1
@FxIII Saya gagal melihat maksud Anda (tidak ada kata pun yang dimaksudkan) dan relevansi dengan pertanyaan ini. Anda mengatakan vektor dan poinnya sama, jadi tidak masuk akal untuk menyimpannya, serius? Sekarang baik Anda akan merevolusi grafis komputer atau Anda tidak mengerti maksud pertanyaan ini.
Maik Semder
1
@FxIII Itu omong kosong, Anda mungkin ingin mempelajari beberapa kerangka matematika 3D yang digunakan dalam pengembangan game, yaitu Sony vectormath , Anda akan menemukan banyak implementasi seperti itu, terutama melihat implementasi vmathV4MakeFromV3 dan vmathV4MakeFromP3 di vec_aos.h, pelajari perbedaannya dan apa yang mereka masukkan ke dalam komponen ke-4, 1,0 untuk P3 dan 0,0 untuk V3, titik 3D dan vektor 3D jelas.
Maik Semder
3
@FxIII yang juga merupakan alasan mengapa kelas XNA Vector3 memiliki fungsi anggota "Transform" dan "TransformNormal", alasannya adalah matematika aljabar linier. Apa yang pada dasarnya Anda lakukan dengan memilih salah satu dari fungsi-fungsi Transform itu melewati parameter 'w' implisit dari '1' atau '0', yang pada dasarnya memasukkan baris ke-4 dari matriks ke dalam perhitungan atau tidak. Ringkas: Jika Anda tidak menyimpan komponen 'w', maka Anda harus memperlakukan vektor tersebut secara berbeda dengan memanggil fungsi transformasi yang berbeda.
Maik Semder
1
Vektor dan titik isomorfis seperti yang dikatakan, maka tidak ada perbedaan aljabar di antara mereka. Namun, apa yang model homogen dari ruang proyektif mencoba untuk mewakili adalah bahwa SET vektor ruang, dan titik-titik tidak isomorfik. Himpunan ruang vektor pada dasarnya adalah tipe penutupan untuk R ^ 3 yang mencakup titik-titik pada bola tak terhingga. Poin dengan w = 0 sering keliru disebut sebagai "vektor" - ini sebenarnya isomorfis ke arah bola, dan akan lebih akurat disebut "arah" ... Dan tidak, kehilangan w mungkin sering bekerja, tetapi sebagian besar Anda akan akan menemukan masalah.
Crowley9
4

Jika Anda membuat kelas vektor, maka saya kira kelas akan menyimpan deskripsi vektor 3D. Vektor 3D memiliki besaran x, y, dan z. Jadi kecuali jika vektor Anda membutuhkan besaran arbitrer, tidak, Anda tidak akan menyimpannya di kelas.

Ada perbedaan besar antara vektor dan matriks transformasi. Karena DirectX dan OpenGL menangani matriks untuk Anda, saya biasanya tidak menyimpan matriks 4x4 dalam kode saya; alih-alih, saya menyimpan rotasi Euler (atau Quaternions jika Anda mau - yang kebetulan memiliki komponen aw) dan terjemahan x, y, z. Terjemahan adalah vektor jika Anda mau, dan rotasi secara teknis akan cocok dengan vektor juga, di mana setiap komponen akan menyimpan jumlah rotasi di sekitar porosnya.

Jika Anda ingin menyelami sedikit lebih dalam matematika dari suatu vektor, vektor Euclidean hanyalah arah dan besarnya. Jadi biasanya ini diwakili oleh triplet angka, di mana setiap angka adalah besarnya sepanjang sumbu; arahnya tersirat oleh kombinasi dari tiga magnitudo ini, dan magnitudo dapat ditemukan dengan rumus jarak Euclidean . Atau, kadang-kadang itu benar-benar disimpan sebagai arah (vektor dengan panjang = 1) dan besarnya (float), jika itu yang nyaman (misalnya jika besarnya berubah lebih sering daripada arah, mungkin lebih nyaman untuk hanya ubah angka magnitudo dari pada mengambil vektor, normalkan , dan gandakan komponen dengan magnitudo baru).

Ricket
sumber
6
OpenGL modern tidak berurusan dengan matriks untuk Anda.
SurvivalMachine
4

Dimensi keempat dalam vektor 3D digunakan untuk menghitung transformasi affine yang tidak mungkin untuk dihitung menggunakan matriks saja. Ruang tetap tiga dimensi sehingga ini berarti bahwa keempat dipetakan dalam ruang 3d dalam beberapa cara.

Memetakan dimensi berarti bahwa vektor 4D yang berbeda menunjukkan titik 3D yang sama. Peta itu adalah jika A = [x ', y', z'.w '] dan B = [x ", y", z ", w"] mereka mewakili titik yang sama jika x' / x "= y ' / y "= z '/ z" = w' / w "= α yaitu komponen proporsional untuk koefisien α yang sama.

Mengatakan bahwa Anda dapat mengekspresikan suatu titik - katakanlah (1,3,7) - dalam perilaku tak terbatas seperti (1,3,7,1) atau (2,6,14,2) atau (131,393,917,131) atau secara umum (α · 1, α · 3, α · 7, α).

Ini berarti bahwa Anda dapat skala vektor 4D ke yang lain yang mewakili titik 3D yang sama sehingga w = 1: formulir (x, y, z, 1) adalah bentuk kanonik.

Saat Anda menerapkan matriks ke vektor ini, Anda dapat memperoleh vektor yang tidak memiliki w = 1, tetapi Anda selalu dapat menskalakan hasil untuk menyimpannya dalam bentuk kanonik. Jadi jawabannya tampaknya "Anda harus menggunakan vektor 4D saat melakukan matematika tetapi jangan menyimpan komponen keempat" .

Ini cukup benar tetapi ada beberapa poin yang tidak dapat Anda masukkan ke dalam bentuk kanonik: poin seperti (4,2,5,0). Poin-poin itu adalah yang spesial, mereka mewakili titik tak terhingga yang diarahkan dan dapat dinormalisasi ke satuan vektor secara konsisten: Anda dapat dengan aman pergi ke yang tak terbatas dan kembali (bahkan dua kali) tanpa menjadi Chuck Norris. Anda akan mendapatkan pembagian yang menyedihkan dengan nol jika Anda mencoba untuk memaksa vektor-vektor itu dalam bentuk kanonik.

Sekarang Anda tahu, jadi pilihan ada di tangan Anda!

FxIII
sumber
1

Ya, benar. Transformasi Anda salah untuk beberapa jenis vektor. Anda dapat melihat ini di perpustakaan matematika D3DX - mereka memiliki dua fungsi multiplikasi matriks-vektor yang berbeda, satu untuk w = 0 dan satu untuk w = 1.

DeadMG
sumber
0

Tergantung pada apa yang Anda inginkan dan butuhkan. :)

Saya akan menyimpannya, b / c itu diperlukan untuk transformasi dan semacamnya (Anda tidak dapat melipatgandakan vektor 3 dengan matriks 4x4), meskipun jika Anda selalu memiliki aw of 1, saya kira Anda bisa memalsukannya.

alat pemecah es
sumber