Vector sınıfımda 'w' bileşenine ihtiyacım var mı?


21

3B alan için döndürme, çeviri vb. İşleyen matris kodu yazdığınızı varsayın.

Şimdi dönüştürme matrislerinin çeviri bileşenine sığması için 4x4 olması gerekir.

Ancak, aslında yok gerek bir depolamak için wvektör bileşen öyle mi?

Perspektif bölünmesinde bile w, yöntemden dönmeden önce basitçe hesaplayabilir ve vektörün dışında saklayabilirsiniz ve perspektif bölünmesi.

Örneğin:

// 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 ;
}

wVector sınıfında depolamanın bir anlamı var mı ?


2
Bu normal bir matris vektör çarpımı için uygulama değildir, perspektif bölünmesi oraya ait değildir. Ayrıca oldukça yanıltıcıdır, çünkü hesaplamanın yanlış kısımları vurgulanmıştır. Eğer w-bileşeninin ne için olduğunu öğrenmek istiyorsanız, tam uygulamaya bakın, o zaman matrisin son satırının / sütununun (çeviri kısmı) sadece w-bileşeni 1 ise uygulandığını görürsünüz, yani puan için. Bu bölümleri vurgulamalısınız: r.x = ... + a._14*v.w; r.y = ... + a._24*v.w; r.z = ... + a._34*v.w; r.w = ... + a._44*v.w;ayrıntılar için cevabım bak
Maik Semder

Yanıtlar:


27

DÜZENLEME Feragatname : Bu cevapta kolaylık olması için w == 0 olan vektörlere vektör denir ve w == 1 ile vektörlere nokta denir. FxIII'nin işaret ettiği gibi, bu matematiksel olarak doğru bir terminoloji değildir. Bununla birlikte, cevabın noktası terminoloji değil, her iki vektör türünü de ayırt etme ihtiyacı olduğundan, buna bağlı kalacağım. Pratik bir nedenle, bu sözleşme oyun geliştirmede yaygın olarak kullanılmaktadır.


'W' bileşeni olmadan vektörler ve noktalar arasında ayrım yapmak mümkün değildir. Puanlar için 1 ve vektörler için 0'dır.

Vektörler, son satırında / sütununda çevirisi olan bir 4x4 afin dönüşüm-matrisi ile çarpılırsa, vektör de çevrilir, bu yanlıştır, sadece noktalar çevrilmelidir. Bir vektörün 'w' bileşenindeki sıfır, bununla ilgilenir.

Matris-vektör çarpımının bu bölümünü vurgulamak daha açık hale getirir:

    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) 

Yani bir vektörü, örneğin bir dönme eksenini çevirmek yanlış olur, sonuç sadece yanlıştır, 4. bileşen sıfırına sahip olmakla birlikte, dönüş eksenini dönüştürmek için noktaları dönüştüren aynı matrisi kullanabilirsiniz ve sonuç geçerli olacaktır. ve uzunluğu matriste ölçek olmadığı sürece korunur. Vektörler için istediğiniz davranış budur. 4. bileşen olmadan 2 matris (veya örtük bir 4. parametre ile 2 farklı çarpma işlevi) oluşturmanız ve noktalar ve vektörler için 2 farklı işlev çağrısı yapmanız gerekir.

Modern CPU'ların (SSE, Altivec, SPU) vektör kayıtlarını kullanmak için yine de 4x 32 bit şamandıra (128 bitlik bir kayıt) geçmeniz gerekir, ayrıca hizalamaya dikkat etmeniz gerekir, genellikle 16 bayt. Böylece 4. bileşen için alan güvenliğini sağlama şansınız yok.


DÜZENLEME: Sorunun cevabı temelde

  1. W bileşenini depolayın: konumlar için 1 ve vektörler için 0
  2. Veya farklı matris-vektör çarpma işlevlerini çağırın ve iki işlevden birini seçerek 'w' bileşenini dolaylı olarak geçirin

Bunlardan birini seçmek gerekir, sadece {x, y, z} depolamak ve hala sadece bir matris-vektör çarpma fonksiyonu kullanmak mümkün değildir. Örneğin XNA, Vector3 sınıfında TransformveTransformNormal

Aşağıda, her iki yaklaşımı da gösteren ve her iki vektör türünü de 2 olası yoldan 1'inde ayırt etme ihtiyacını gösteren bir kod örneği verilmiştir. Bir oyun varlığını bir pozisyon ve bakış yönüne sahip bir matrisle dönüştürerek hareket ettireceğiz. 'W' bileşenini kullanmazsak, bu örnekte gösterildiği gibi artık aynı matris-vektör çarpımını kullanamayız. Yine de yaparsak, dönüştürülmüş look_dirvektör için yanlış bir cevap alırız :

#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;
}

İlk Varlık durumu:

position       :   0.000000   0.000000   0.000000   1.000000
look_dir       :   1.000000   0.000000   0.000000   0.000000

Şimdi bu varlığa x + 5 çevrimli bir dönüşüm ve y ekseni etrafında 90 derecelik bir dönüş uygulanacaktır. Dönüşümden sonra doğru cevap:

position       :   5.000000   0.000000   0.000000   1.000000
look_dir       :   0.000000   0.000000   1.000000   0.000000

Doğru yanıtı ancak vektörleri w == 0 ve konumlarını w == 1 ile yukarıda belirtilen yollardan birinde ayırırsak alırız.


@Maik Semder Biraz yanılıyorsunuz ... Vektörler arasında bir noktayı ayırt etmek mümkün değil çünkü bunlar aynı şeydir! . Yanıtın geri kalanının yanlış varsayımlar nedeniyle pek bir anlamı yoktur.
FxIII

1
@FxIII Ne demek istediğini göremiyorum (cinas yok) ve bu soru ile ilgisi. Vektörlerin ve noktaların aynı olduğunu söylüyorsunuz, bu yüzden 'w'yi yine de saklamak mantıklı değil mi? Şimdi ya bilgisayar grafiklerinde devrim yapacaksınız ya da bu sorunun anlamını alamıyorsunuz.
Maik Semder

1
@FxIII Bu saçmalık, oyun geliştirmede kullanılan bazı 3D matematik çerçevelerini incelemek isteyebilirsiniz, yani Sony'nin vectormath'ı , vec_aos.h içinde vmathV4MakeFromV3 ve vmathV4MakeFromP3'ün uygulanmasına özellikle bakın, farkı inceleyin ve 4. bileşene koydukları, P3 için 1.0 ve V3, 3D noktası ve 3D vektör için 0.0.
Maik Semder

3
XNA nedeni de @FxIII vector3 sınıfı "Transform" ve "TransformNormal" üye-işlev vardır, nedeni ise lineer cebir matematik. Temel olarak, bu Transform işlevlerinden birini seçerek yaptığınız, temel olarak matrisin 4. satırını hesaplamaya dahil edip etmemeyi içeren '1' veya '0' örtük 'w' parametresini geçirmektir. Özetle: 'w' bileşenini saklamazsanız, farklı dönüşüm işlevlerini çağırarak bu vektörlere farklı davranmanız gerekir.
Maik Semder

1
Vektörler ve noktalar belirtildiği gibi izomorfiktir, dolayısıyla aralarında cebirsel bir fark yoktur. Bununla birlikte, projektif uzayın homojen modelini temsil etmeye çalıştığı şey, vektör ALANLAR SETİ ve noktaların izomorfik olmamasıdır. Vektör uzayları kümesi aslında R ^ 3 için sonsuz küre üzerindeki noktaları içeren bir tür kapaktır. W = 0 olan noktalara genellikle yanlış olarak "vektörler" denir - bunlar aslında yön küresine izomorfiktir ve daha doğru bir şekilde basitçe "yönler" olarak adlandırılırlar. bela bulmak.
04:28

4

Bir Vector sınıfı yapıyorsanız, sınıfın bir 3B vektörün açıklamasını depolayacağını varsayıyorum. 3B vektörlerin x, y ve z büyüklükleri vardır. Bu nedenle, vektörünüz keyfi bir w büyüklüğüne ihtiyaç duymadıkça, hayır, sınıfta saklamazsınız.

Bir vektör ile bir dönüşüm matrisi arasında büyük bir fark vardır. DirectX ve OpenGL'nin sizin için matrislerle uğraştığı göz önüne alındığında, kodumda genellikle 4x4 matrisi saklamıyorum; daha ziyade, Euler rotasyonlarını (veya isterseniz Quaternions'ı saklıyorum - tesadüfen aw bileşenine sahip olan) ve x, y, z çeviri. Çeviri, isterseniz bir vektördür ve dönüş, teknik olarak bir vektöre de sığar, burada her bileşen dönüş miktarını kendi ekseni çevresinde depolar.

Bir vektörün matematiğine biraz daha derine dalmak istiyorsanız, Öklid vektörü sadece bir yön ve büyüklüktür. Yani tipik olarak bu, her bir sayının bir eksen boyunca büyüklüğü olduğu bir sayı üçlüsü ile temsil edilir; yönü bu üç büyüklüğün kombinasyonu ile ifade edilir ve büyüklük Öklid uzaklık formülü ile bulunabilir. Veya bazen gerçekten bir yön (uzunluk = 1 olan bir vektör) ve bir büyüklük (bir şamandıra) olarak saklanır, eğer uygun olan buysa (örneğin, büyüklük yönden daha sık değişirse, sadece daha uygun olabilir bu büyüklük sayısını bir vektör almak, normalleştirmek ve bileşenleri yeni büyüklükle çarpmaktan ziyade değiştirin ).


6
Modern OpenGL sizin için matrislerle uğraşmaz.
SurvivalMachine

4

3B vektördeki dördüncü boyut, sadece matrisleri kullanarak hesaplanması imkansız olacak afin dönüşümleri hesaplamak için kullanılır. Alan üç boyutlu olarak kalır, bu da dördüncüsünün 3d alanda bir şekilde eşlendiği anlamına gelir.

Bir boyutları eşlemek, farklı 4D vektörlerinin aynı 3B noktasını gösterdiğini gösterir. Harita, eğer A = [x ', y', z'.w '] ve B = [x ", y", z ", w"] ise, x' / x "= y 'ise aynı noktayı temsil ederler. / y "= z '/ z" = w' / w "= α yani bileşen, aynı katsayı a için orantılıdır.

Bir noktayı (örneğin, 1,3,7) - (1,3,7,1) veya (2,6,14,2) veya (131,393,917,131) gibi sonsuz şekilde veya genel olarak (α · 1, α · 3, α · 7, α).

Bu, 4D vektörünü aynı 3D noktasını temsil eden bir başkasına ölçekleyebileceğiniz anlamına gelir, böylece w = 1: form (x, y, z, 1) kanonik formdur.

Bu vektöre bir matris uyguladığınızda, w = 1 olmayan bir vektör elde edebilirsiniz, ancak sonuçları her zaman kanonik formda saklamak için ölçekleyebilirsiniz. Yani cevap "matematik yaparken 4D vektörleri kullanmalısınız, ancak dördüncü bileşeni saklamayın" gibi görünüyor .

Bu oldukça doğrudur ama bazı noktalar vardır edemez kanonik formda koyun: (4,2,5,0) gibi noktalar. Bu noktalar özel olanlardır, yönlendirilmiş sonsuz noktayı temsil ederler ve sürekli olarak birim vektöre normalleştirilebilirler: güvenle sonsuzluğa gidebilir ve Chuck Norris olmadan geri dönebilirsiniz (iki kez bile). Bu vektörleri kanonik formda zorlamaya çalışırsanız sıfıra bölünebilir bir şekilde bölünürsünüz.

Şimdi biliyorsunuz, seçim sizin!


1

Evet yaparsın. Dönüşümünüz bazı vektör türleri için yanlıştır. Bunu D3DX matematiksel kütüphanesinde görebilirsiniz - bir tanesi w = 0 ve diğeri w = 1 için olmak üzere iki farklı matris-vektör çarpma işlevi vardır.


0

İstediğiniz ve ihtiyacınız olan şeye bağlıdır. :)

Ben saklıyorum, b / c dönüşümler için gerekli ve böyle (3 vektörü 4x4 matris ile çarpamazsınız), ancak her zaman sadece 1'e sahipseniz, sanırım sadece sahte olabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.