glVertexAttribPointer
में दो त्रुटियां हैं, उनमें से एक अर्ध-व्यक्तिपरक, दूसरा उद्देश्य है।
पहली दोष इसकी निर्भरता GL_ARRAY_BUFFER
पर निर्भर है। इसका मतलब है कि glVertexAttribPointer
का व्यवहार GL_ARRAY_BUFFER
पर जो भी कहा गया था उस पर आकस्मिक है। लेकिन एक बार इसे बुलाया जाता है, GL_ARRAY_BUFFER
के लिए बाध्य क्या है अब कोई मायने नहीं रखता; बफर ऑब्जेक्ट का संदर्भ वीएओ में कॉपी किया गया है। यह सब बहुत ही कमजोर और भ्रमित है, यहां तक कि कुछ अर्द्ध अनुभवी उपयोगकर्ताओं तक भी।
यह भी आपको एक पूर्णांक बाइट ऑफ़सेट के बजाय बफर ऑब्जेक्ट में "पॉइंटर" के रूप में ऑफसेट प्रदान करने की आवश्यकता है। इसका मतलब यह है कि आप एक पूर्णांक से एक पॉइंटर तक एक अजीब कास्ट करते हैं (जिसे ड्राइवर में समान रूप से अजीब कलाकार द्वारा मिलान किया जाना चाहिए)।
दूसरा दोष यह है कि यह दो परिचालनों को स्वीकार करता है जो तर्कसंगत रूप से काफी अलग हैं। ओपनजीएल पढ़ सकते हैं कि एक वर्टेक्स सरणी को परिभाषित करने के लिए, आपको दो चीजें प्रदान करनी होंगी:
- स्मृति से डेटा कैसे प्राप्त करें।
- वह डेटा कैसा दिखता है।
glVertexAttribPointer
इन दोनों को एक साथ प्रदान करता है। GL_ARRAY_BUFFER
बफर ऑब्जेक्ट, साथ ही ऑफ़सेट "पॉइंटर" और स्ट्रैड परिभाषित करता है कि डेटा कहां संग्रहीत किया जाता है और इसे कैसे लाया जाता है। अन्य पैरामीटर बताते हैं कि डेटा की एक इकाई कैसा दिखती है। आइए इसे सरणी के vertex format पर कॉल करें।
एक व्यावहारिक मामले के रूप में, उपयोगकर्ता वर्टेक्स डेटा वर्टेक्स प्रारूपों से कहां से बदलते हैं। आखिरकार, दृश्य में कई वस्तुएं अपने चरम को उसी तरह से स्टोर करती हैं। जो कुछ भी हो सकता है: 3 पदों के लिए तैरता है, रंगों के लिए 4 हस्ताक्षरित बाइट्स, टेक्सास-कॉर्ड के लिए 2 हस्ताक्षरित शॉर्ट्स इत्यादि। आम तौर पर, आपके पास केवल कुछ वर्टेक्स प्रारूप हैं।
जबकि आपके पास कहीं अधिक स्थान हैं जहां से आप डेटा खींचते हैं। यहां तक कि यदि ऑब्जेक्ट्स एक ही बफर से आते हैं, तो आप ऑफसेट को के भीतर ऑफसेट से ऑब्जेक्ट पर स्विच करने के लिए बफर करना चाहते हैं।
glVertexAttribPointer
के साथ, आप केवल ऑफ़सेट अपडेट नहीं कर सकते हैं। आपको एक ही समय में संपूर्ण प्रारूप + बफर जानकारी निर्दिष्ट करना होगा। हर बार।
वीएओ प्रति ऑब्जेक्ट उन सभी कॉल करने के लिए कमजोर पड़ते हैं, लेकिन यह पता चला है कि वे वास्तव में समस्या का समाधान नहीं करते हैं। हां, आपको वास्तव में glVertexAttribPointer
पर कॉल करने की आवश्यकता नहीं है। लेकिन यह इस तथ्य को नहीं बदलेगा कि वर्टेक्स प्रारूप बदलना महंगा है।
As discussed here, वर्टेक्स प्रारूपों को बदलना बहुत महंगा है। जब आप एक नया वीएओ (या, जब आप एक नया वीएओ बाध्य करने के बाद प्रस्तुत करते हैं) को बाध्य करते हैं, तो कार्यान्वयन या तो वर्टेक्स प्रारूप को बदलता है या दो वीएओ की तुलना करना पड़ता है यह देखने के लिए कि वे परिभाषित वर्टेक्स प्रारूप अलग हैं या नहीं। किसी भी तरह से, यह काम कर रहा है कि इसे करने की आवश्यकता नहीं है।
glVertexAttribFormat
और glBindVertexBuffer
इन दोनों समस्याओं को ठीक करें। glBindVertexBuffer
सीधे बफर ऑब्जेक्ट निर्दिष्ट करता है और बाइट ऑफसेट को वास्तविक (64-बिट) पूर्णांक के रूप में लेता है। तो GL_ARRAY_BUFFER
बाध्यकारी का कोई अजीब उपयोग नहीं है; यह बाध्यकारी पूरी तरह से बफर ऑब्जेक्ट में हेरफेर करने के लिए प्रयोग किया जाता है।
और क्योंकि दो अलग-अलग अवधारणाएं अब अलग-अलग कार्य हैं, इसलिए आपके पास एक वीएओ हो सकता है जो प्रारूप को संग्रहीत करता है, इसे बांधता है, फिर प्रत्येक ऑब्जेक्ट या ऑब्जेक्ट्स के समूह के लिए वर्टेक्स बफर को बांधता है। वर्टेक्स बफर बाध्यकारी स्थिति बदलना वर्टेक्स प्रारूप स्थिति से सस्ता है।
अलग विशेषता बाध्यकारी कार्य इस तरह काम करते हैं। glVertexAttrib*Format
फ़ंक्शंस एक विशेषता के लिए सभी वर्टेक्स स्वरूपण पैरामीटर प्रदान करता है। इसके प्रत्येक पैरामीटर का सटीक कॉल के मानकों के समान सटीक अर्थ है glVertexAttrib*Pointer
पर।
जहां चीजें थोड़ा भ्रमित हो जाती हैं glBindVertexBuffer
के साथ होती है।
इसका पहला पैरामीटर एक सूचकांक है। लेकिन यह एक विशेषता स्थान है; यह केवल एक बफर बाध्यकारी बिंदु है। यह अलग-अलग सरणी विशेषता स्थानों से अपनी अधिकतम सीमा के साथ है। तो तथ्य यह है कि आप सूचकांक 0 के लिए बफर बांधते हैं, इसका मतलब है कुछ भी जहां विशेषता स्थान 0 से इसका डेटा प्राप्त होता है।
बफर बाइंडिंग और विशेषता स्थानों के बीच कनेक्शन glVertexAttribBinding
द्वारा परिभाषित किया गया है। पहला पैरामीटर विशेषता स्थान है, और दूसरा उस विशेषता के स्थान को लाने के लिए बफर बाध्यकारी अनुक्रमणिका है। चूंकि फ़ंक्शन का नाम "VertexAttrib" से शुरू होता है, इसलिए आपको इसे वर्टेक्स प्रारूप स्थिति का हिस्सा बनना चाहिए और इस प्रकार यह बदलना महंगा है।
ऑफ़सेट की प्रकृति पहले भी थोड़ा उलझन में हो सकती है। glVertexAttribFormat
में ऑफ़सेट पैरामीटर है। लेकिन glBindVertexBuffer
भी है। लेकिन इन ऑफसेट्स का मतलब अलग-अलग चीजें हैं। अंतर को समझने के लिए सबसे आसान तरीका एक interleaved डेटा संरचना का एक उदाहरण का उपयोग करना है:
struct Vertex
{
GLfloat pos[3];
GLubyte color[4];
GLushort texCoord[2];
};
शिखर बफर ऑफसेट बंधन बाइट पहले सिरे सूचकांक करने के लिए बफर वस्तु की शुरुआत से ऑफसेट निर्दिष्ट करता है। यही है, जब आप इंडेक्स 0 प्रस्तुत करते हैं, तो GPU बफर ऑब्जेक्ट के पते + बाध्यकारी ऑफ़सेट से स्मृति प्राप्त करेगा।
शिखर प्रारूप ऑफसेट प्रत्येक शिखर कि विशेष रूप से विशेषता के डेटा के शुरू से ही ऑफसेट निर्दिष्ट करता है। बफर में डेटा Vertex
द्वारा परिभाषित किया गया है, तो प्रत्येक विशेषता के लिए ऑफसेट होगा:
glVertexAttribFormat(0, ..., offsetof(Vertex, pos)); //AKA: 0
glVertexAttribFormat(1, ..., offsetof(Vertex, color)); //Probably 12
glVertexAttribFormat(2, ..., offsetof(Vertex, texCoord)); //Probably 16
तो ऑफसेट बंधन में परिभाषित किया गया है, जहां शिखर 0, स्मृति में है, जबकि प्रारूप ऑफसेट परिभाषित जहां प्रत्येक विशेषता के डेटा आता है से एक कशेरुक के भीतर।
समझने की आखिरी बात यह है कि बफर बाध्यकारी है जहां की तरफ परिभाषित किया गया है। यह अजीब लग सकता है, लेकिन हार्डवेयर परिप्रेक्ष्य से इसके बारे में सोचें।
बफर बाध्यकारी में वर्टेक्स इंडेक्स या इंस्टेंस इंडेक्स को स्मृति स्थान में बदलने के लिए हार्डवेयर द्वारा आवश्यक सभी जानकारी होनी चाहिए। एक बार ऐसा करने के बाद, वर्टेक्स प्रारूप बताता है कि उस स्मृति स्थान में बाइट्स की व्याख्या कैसे करें।
यही कारण है कि उदाहरण divisor glVertexBindingDivisor
के माध्यम से बफर बाध्यकारी स्थिति का हिस्सा है। एक इंस्टेंस इंडेक्स को स्मृति पते में बदलने के लिए हार्डवेयर को विभाजक को जानना आवश्यक है।
बेशक, इसका मतलब यह भी है कि अब आप ओपनजीएल पर भरोसा नहीं कर सकते हैं ताकि आप के लिए बातचीत की जा सके। उपरोक्त कलाकारों में, आप बस sizeof(Vertex)
का उपयोग करते हैं।
अलग विशेषता प्रारूप पूरी तरह से पुराने glVertexAttribPointer
मॉडल इतनी अच्छी तरह से शामिल किया गया है कि पुराने समारोह अब की नई शर्तों में पूरी तरह से परिभाषित किया गया है: इस बराबर समारोह विशेषता स्थान के लिए एक ही सूचकांक मूल्य का उपयोग करता है
void glVertexAttrib*Pointer(GLuint index, GLint size, GLenum type, {GLboolean normalized,} GLsizei stride, const GLvoid * pointer)
{
glVertexAttrib*Format(index, size, type, {normalized,} 0);
glVertexAttribBinding(index, index);
GLuint buffer;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, buffer);
if(buffer == 0)
glErrorOut(GL_INVALID_OPERATION); //Give an error.
if(stride == 0)
stride = CalcStride(size, type);
GLintptr offset = reinterpret_cast<GLintptr>(pointer);
glBindVertexBuffer(index, buffer, offset, stride);
}
नोट और बफर बाध्यकारी सूचकांक। यदि आप अंतःस्थापित गुण कर रहे हैं, तो आपको जहां संभव हो वहां से बचना चाहिए; इसके बजाय, एक ही बफर से जुड़े सभी विशेषताओं के लिए एक बफर बाइंडिंग का उपयोग करें।
संबंधित प्रश्न, भले ही वे 'glvindVertexBuffer() 'के बारे में अधिक हैं,' glvertexAttribFormat()': http://stackoverflow.com/questions/26767939/do-opengl-vertex-array-objects-store-vertex- बफर-नाम-और-सूचकांक-या-केवल-इन, http://stackoverflow.com/questions/29220416/can-opengl-vertex-buffer-binding-points-be-reused-across- dififferent-vaos। –