क्या मुझे अपने वेक्टर वर्ग में 'w' घटक की आवश्यकता है?


21

मान लें कि आप मैट्रिक्स कोड लिख रहे हैं जो 3 डी स्थान के लिए रोटेशन, अनुवाद आदि को संभालता है।

अनुवाद घटक को फिट करने के लिए अब परिवर्तन मेट्रिसेस को 4x4 होना चाहिए।

हालांकि, आपको वास्तव में वेक्टर घटक में स्टोर करने की आवश्यकता नहीं है wक्या आप करते हैं?

परिप्रेक्ष्य विभाजन में भी, आप बस wवेक्टर के बाहर गणना कर सकते हैं और स्टोर कर सकते हैं , और पद्धति से लौटने से पहले परिप्रेक्ष्य को विभाजित करते हैं।

उदाहरण के लिए:

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

क्या wवेक्टर क्लास में स्टोर करने की बात है ?


2
यह सामान्य मैट्रिक्स वेक्टर गुणा के लिए कार्यान्वयन नहीं है, परिप्रेक्ष्य विभाजित नहीं है। साथ ही यह काफी भ्रामक है, क्योंकि गणना के गलत हिस्सों को उजागर किया गया है। यदि आप यह जानना चाहते हैं कि डब्ल्यू-कंपोनेंट क्या है, तो पूरा कार्यान्वयन देखें, फिर आप देखें कि मैट्रिक्स की अंतिम पंक्ति / कॉलम (ट्रांसलेशन पार्ट) केवल तभी लागू किया जाता है, यदि डब्ल्यू-कंपोनेंट 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;विवरण के लिए मेरा उत्तर देखें
माईक सेमर

जवाबों:


27

EDIT अस्वीकरण : इस उत्तर में सुविधा के लिए w == 0 वाले वैक्टरों को वैक्टर कहा जाता है और w == 1 के साथ बिंदुओं को कहा जाता है। यद्यपि FxIII ने बताया, यह गणितीय रूप से सही शब्दावली नहीं है। हालांकि, चूंकि उत्तर का बिंदु शब्दावली नहीं है, लेकिन दोनों प्रकार के वैक्टरों को अलग करने की आवश्यकता है, मैं इसे चिपकाऊंगा। व्यावहारिक कारण से इस सम्मेलन का व्यापक रूप से खेल-विकास में उपयोग किया जाता है।


'W' घटक के बिना वैक्टर और बिंदुओं के बीच अंतर करना संभव नहीं है। यह अंक के लिए 1 और वैक्टर के लिए 0 है।

यदि वैक्टर को 4x4 affine परिवर्तन-मैट्रिक्स से गुणा किया जाता है जिसमें इसकी अंतिम पंक्ति / स्तंभ में अनुवाद है, तो वेक्टर का भी अनुवाद किया जाएगा, जो कि गलत है, केवल बिंदुओं का अनुवाद किया जाना चाहिए। एक वेक्टर के '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 वें घटक के बिना आपको एक अंतर्निहित 4 पैरामीटर के साथ 2 मैट्रिसेस (या 2 अलग-अलग गुणा फ़ंक्शंस बनाने होंगे और अंक और वैक्टर के लिए 2 अलग-अलग फ़ंक्शन कॉल करने होंगे।

आधुनिक सीपीयू (SSE, Altivec, SPUs) के वेक्टर रजिस्टरों का उपयोग करने के लिए आपको वैसे भी 4x 32 बिट फ़्लोट पास करना होगा (इसका 128 बिट रजिस्टर), साथ ही आपको संरेखण का ध्यान रखना होगा, आमतौर पर 16 बाइट्स। तो आपके पास वैसे भी चौथे घटक के लिए स्थान सुरक्षित करने का मौका नहीं है।


संपादित करें: प्रश्न का उत्तर मूल रूप से है

  1. या तो डब्ल्यू-घटक को स्टोर करें: पदों के लिए 1 और वैक्टर के लिए 0
  2. या विभिन्न मैट्रिक्स-वेक्टर गुणन कार्यों को कॉल करें और दोनों कार्यों में से किसी एक को चुनकर 'w' घटक को स्पष्ट रूप से पास करें

उनमें से किसी एक को चुनना होगा, यह केवल {x, y, z} को स्टोर करना संभव नहीं है और अभी भी केवल एक मैट्रिक्स-वेक्टर गुणन फ़ंक्शन का उपयोग करें। उदाहरण के लिए XNA अपने वेक्टर 3 वर्ग में 2 ट्रांसफ़ॉर्म फ़ंक्शंस के द्वारा बाद के दृष्टिकोण का उपयोग करता है , जिसे कहा जाता है TransformऔरTransformNormal

यहां एक कोड उदाहरण है जो दोनों दृष्टिकोणों को दिखाता है और 2 संभावित तरीकों में से 1 में दोनों प्रकार के वैक्टर को अलग करने की आवश्यकता को दर्शाता है। हम एक खेल-इकाई को एक मैट्रिक्स के साथ बदलकर दुनिया में एक स्थिति और एक नज़र-दिशा में ले जाएंगे। यदि हम '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 के साथ विभेद करते हैं।


@ माईक सेमर आप थोड़े गलत हैं ... वैक्टर के बीच एक बिंदु को भेदना संभव नहीं है क्योंकि वे एक ही चीज हैं! (वे आइसोमोर्फिक हैं) 1 वैक्टर के लिए और 0 अनंत दिशाओं वाले वैक्टर के लिए (जैसा कि मेरे उत्तर में कहते हैं)। । प्रतिक्रिया के शेष में गलत धारणाओं के कारण बहुत कम समझ है।
FxIII

1
@FxIII मैं आपकी बात (इस उद्देश्य से कोई दंड नहीं) और इस प्रश्न की प्रासंगिकता को देखने में विफल हूं। आप कह रहे हैं कि वैक्टर और पॉइंट समान हैं, इसलिए यह 'w' को स्टोर करने का कोई मतलब नहीं है, गंभीरता से? अब या तो आप कंप्यूटर ग्राफिक्स में क्रांति करने जा रहे हैं या आपको इस सवाल का जवाब नहीं मिलेगा।
माईक सेमर

1
@FxIII यह बकवास है, आप गेम-डेवलपमेंट में उपयोग किए जाने वाले कुछ 3 डी गणित फ्रेमवर्क का अध्ययन करना चाह सकते हैं, अर्थात सोनी के वीक्टमोथ , आपको बहुत सारे ऐसे कार्यान्वयन मिल जाएंगे, पार्टिकुलेर vmathV4kakeFromV3 और vmathV4MakeFromP3 के कार्यान्वयन में vec_aos.h, अध्ययन अंतर और वे 4 के घटक में क्या डालते हैं, पी 3 के लिए 1.0 और वी 3 के लिए 0.0, 3 डी बिंदु और 3 डी वेक्टर के लिए स्पष्ट रूप से।
माईक सेमर

3
@FxIII भी है कि कारण है कि XNA Vector3 वर्ग एक "रूपांतरण" और एक "TransformNormal" सदस्य-कार्य है, कारण है रेखीय बीजगणित के गणित। आप मूल रूप से उन ट्रांसफ़ॉर्म फ़ंक्शंस में से एक का चयन करके क्या करते हैं, यह '1' या '0' के एक अंतर्निहित 'डब्ल्यू' पैरामीटर से गुजर रहा है, जिसमें मूल रूप से गणना में मैट्रिक्स की 4 वीं पंक्ति शामिल है या नहीं। संक्षेप: यदि आप 'डब्ल्यू' घटक को संग्रहीत नहीं करते हैं, तो आपको उन वैक्टरों के साथ अलग-अलग परिवर्तन कार्यों को कॉल करके इलाज करना होगा।
माईक सेमर

1
जैसा कि कहा गया है, क्षेत्र और बिंदु आइसोमॉर्फिक हैं, इसलिए उनके बीच कोई बीजीय अंतर नहीं है। हालाँकि, जो प्रॉजेक्टिव स्पेस के समरूप मॉडल का प्रतिनिधित्व करने की कोशिश करता है, वह यह है कि वेक्टर SPACES का SET, और पॉइंट्स आइसोमोर्फिक नहीं हैं। वेक्टर रिक्त स्थान का सेट R ^ 3 के लिए बंद होने का एक प्रकार है, जिसमें अनंत क्षेत्र पर बिंदु शामिल हैं। W = 0 वाले बिंदुओं को अक्सर "वैक्टर" के रूप में गलत रूप से संदर्भित किया जाता है - ये वास्तव में दिशा क्षेत्र के लिए आइसोमोर्फिक हैं, और अधिक सटीक रूप से "दिशाएं" कहा जाएगा ... और नहीं, खोने डब्ल्यू अक्सर काम कर सकते हैं, लेकिन ज्यादातर आप परेशान होना।
क्रॉले

4

यदि आप एक वेक्टर वर्ग बना रहे हैं, तो मुझे लगता है कि कक्षा 3 डी वेक्टर के विवरण को संग्रहीत करेगी। 3 डी वैक्टर में एक्स, वाई और जेड मैग्नीट्यूड होते हैं। इसलिए जब तक आपके वेक्टर को एक मनमाना डब्ल्यू परिमाण नहीं चाहिए, नहीं, आप इसे कक्षा में संग्रहीत नहीं करेंगे।

एक वेक्टर और एक परिवर्तन मैट्रिक्स के बीच एक बड़ा अंतर है। यह देखते हुए कि DirectX और OpenGL दोनों आपके लिए मैट्रिसेस से निपटते हैं, मैं आमतौर पर अपने कोड में 4x4 मैट्रिक्स को स्टोर नहीं करता हूं; इसके बजाय, मैं यूलर घुमाव (या यदि आप चाहें - जो संयोग से घटक जगा है) और एक्स, वाई, जेड अनुवाद स्टोर करते हैं। यदि आप चाहें तो अनुवाद एक वेक्टर है, और घुमाव तकनीकी रूप से एक वेक्टर में फिट होगा, जहां प्रत्येक घटक रोटेशन की राशि को अपनी धुरी के आसपास संग्रहीत करेगा।

यदि आप एक वेक्टर के गणित में थोड़ा गहरा गोता लगाना चाहते हैं, तो यूक्लिडियन वेक्टर केवल एक दिशा और परिमाण है। इसलिए आमतौर पर यह संख्याओं के एक समूह द्वारा दर्शाया जाता है, जहां प्रत्येक संख्या एक अक्ष के साथ परिमाण होती है; इसकी दिशा इन तीन परिमाणों के संयोजन से निहित है, और यूक्लिडियन दूरी सूत्र के साथ परिमाण पाया जा सकता है। या, कभी-कभी इसे वास्तव में एक दिशा के रूप में संग्रहीत किया जाता है (लंबाई के साथ एक वेक्टर = 1) और एक परिमाण (एक फ्लोट), यदि यह सुविधाजनक है (जैसे कि अगर परिमाण दिशा से अधिक बार बदलता है, तो यह सिर्फ और अधिक सुविधाजनक हो सकता है एक वेक्टर लेने की तुलना में उस परिमाण संख्या को बदल दें , इसे सामान्य करें , और नए परिमाण द्वारा घटकों को गुणा करें)।


6
आधुनिक OpenGL आपके लिए मेट्रिसेस के साथ सौदा नहीं करता है।
सर्वाइवलमैचिन

4

3 डी वेक्टर में चौथे आयाम का उपयोग एफाइन परिवर्तनों की गणना करने के लिए किया जाता है जो अकेले मैट्रिक्स का उपयोग करके गणना करना असंभव होगा। अंतरिक्ष तीन आयामी रहता है इसलिए इसका मतलब यह है कि चौथे को किसी तरह से 3 डी अंतरिक्ष में मैप किया जाता है।

एक आयाम का मतलब है कि अलग 4D वैक्टर एक ही 3 डी बिंदु को इंगित करता है। नक्शा यह है कि अगर A = [x ', y', z'w '] और B = [x ", y", z ", w"] वे उसी बिंदु का प्रतिनिधित्व करते हैं यदि x' / x "= y ' / y "= z '/ z" = w' / w "= α अर्थात घटक समान गुणांक α के लिए आनुपातिक हैं।

कहा कि आप एक बिंदु व्यक्त कर सकते हैं - कहते हैं (1,3,7) - अनंत शिष्टाचारों में (जैसे 1,3,7,1) या (2,6,14,2) या (131,393,917,131) या सामान्य रूप से (α) 1, α · 3, α · 7, α)।

इसका मतलब है कि आप उसी 3 डी बिंदु का प्रतिनिधित्व करने वाले 4 डी वेक्टर को स्केल कर सकते हैं ताकि डब्ल्यू = 1: फॉर्म (एक्स, वाई, जेड, 1) विहित रूप हो।

जब आप इस वेक्टर पर एक मैट्रिक्स लागू करते हैं तो आप एक वेक्टर प्राप्त कर सकते हैं जिसमें w = 1 नहीं होता है, लेकिन आप हमेशा परिणाम को कैनोनिकल रूप में संग्रहीत करने के लिए स्केल कर सकते हैं। तो उत्तर प्रतीत होता है "आपको गणित करते समय 4D वैक्टर का उपयोग करना चाहिए लेकिन चौथे घटक को संग्रहीत नहीं करना चाहिए"

यह काफी हद तक सही है लेकिन कुछ ऐसे बिंदु हैं जिन्हें आप कैनोनिकल फॉर्म में नहीं रख सकते हैं : (जैसे 4,2,5,0) अंक। वे बिंदु विशेष हैं, वे निर्देशित अनंत बिंदु का प्रतिनिधित्व करते हैं और लगातार यूनिट वेक्टर को सामान्यीकृत किया जा सकता है: आप चक नॉरिस होने के बिना सुरक्षित रूप से अनंत और वापसी (यहां तक ​​कि दो बार) जा सकते हैं। यदि आप उन वैक्टरों को विहित रूप में बाध्य करने का प्रयास करते हैं तो आपको शून्य से दुखी विभाजन मिलेगा।

अब तुम्हें पता है, तो चुनाव तुम्हारा है!


1

हाँ आप कीजिए। आपका परिवर्तन कुछ प्रकार के वेक्टर के लिए गलत है। आप इसे D3DX गणितीय पुस्तकालय में देख सकते हैं- उनके पास दो अलग-अलग मैट्रिक्स-वेक्टर गुणन कार्य हैं, जिनमें से एक w = 0 और एक w = 1 के लिए है।


0

निर्भर करता है कि आपको क्या चाहिए और क्या चाहिए। :)

मैं इसे स्टोर करूंगा, b / c यह ट्रांसफॉर्म के लिए आवश्यक है और ऐसे (आप 4x4 मैट्रिक्स के साथ 3 वेक्टर को गुणा नहीं कर सकते हैं), हालांकि यदि आप हमेशा सिर्फ 1 से जागते हैं, तो मुझे लगता है कि आप इसे नकली बना सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.