ข้อสงวนสิทธิ์การแก้ไข : เพื่อความสะดวกในคำตอบนี้เวกเตอร์ที่มี 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 ตัว (หรือฟังก์ชันการคูณที่แตกต่างกัน 2 ตัวพร้อมพารามิเตอร์ตัวที่สี่โดยนัยและทำให้ฟังก์ชันที่แตกต่างกัน 2 แบบเรียกหาจุดและเวกเตอร์
ในการใช้ vector register ของ CPU ที่ทันสมัย (SSE, Altivec, SPUs) คุณต้องผ่าน 4x32 บิตลอยต่อไป (การลงทะเบียน 128 บิต) รวมทั้งคุณต้องดูแลการจัดตำแหน่งโดยปกติ 16 ไบต์ ดังนั้นคุณจึงไม่มีโอกาสที่จะปลอดภัยพื้นที่สำหรับองค์ประกอบที่ 4 อยู่ดี
แก้ไข:
คำตอบสำหรับคำถามนั้นเป็นพื้น
- ให้เก็บส่วนประกอบ w: 1 สำหรับตำแหน่งและ 0 สำหรับเวกเตอร์
- หรือเรียกใช้ฟังก์ชันการคูณเมทริกซ์เวกเตอร์ที่แตกต่างกันและส่งผ่านองค์ประกอบ 'w' โดยการเลือกฟังก์ชันใดฟังก์ชันหนึ่ง
หนึ่งต้องเลือกหนึ่งในนั้นมันเป็นไปไม่ได้ที่จะเก็บเพียง {x, y, z} และยังคงใช้ฟังก์ชั่นการคูณเมทริกซ์เวกเตอร์เดียวเท่านั้น ตัวอย่างเช่น XNA ใช้วิธีการหลังโดยมี 2 แปลงฟังก์ชันในVector3ชั้นเรียกว่าTransform
และTransformNormal
นี่คือตัวอย่างของรหัสที่แสดงให้เห็นทั้งสองวิธีและแสดงให้เห็นถึงความจำเป็นที่จะต้องแยกแยะเวกเตอร์ทั้งสองชนิดด้วยวิธีที่เป็นไปได้ 1 ใน 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 และการหมุน 90 องศารอบแกน y จะถูกนำไปใช้กับเอนทิตีนี้ คำตอบที่ถูกต้องหลังจากการเปลี่ยนรูปคือ:
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;
ดูคำตอบของฉันสำหรับรายละเอียด