glVertexAttribPointer ชี้แจง


94

แค่อยากให้แน่ใจว่าฉันเข้าใจสิ่งนี้ถูกต้อง (ฉันจะถามใน SO Chat แต่มันตายอยู่ที่นั่น!):

เรามี Vertex Array ซึ่งเราสร้าง "ปัจจุบัน" โดยการผูกมัน
จากนั้นเราจะมี Buffer ซึ่งเราผูกกับ Target
จากนั้นเราก็เติม Target นั้นด้วยการglBufferData เติมสิ่งที่เกี่ยวข้องกับเป้าหมายนั้นเป็นหลักเช่น Buffer ของเรา
จากนั้นเราเรียกglVertexAttribPointerสิ่งที่อธิบายถึงวิธีการจัดวางข้อมูล - ข้อมูลที่ถูกผูกไว้กับอะไรก็ตามGL_ARRAY_BUFFER และตัวบอกเล่านี้จะถูกบันทึกลงใน Vertex Array ดั้งเดิมของเรา

(1) ความเข้าใจของฉันถูกต้องหรือไม่? เอกสารเป็นเบาบางเล็ก ๆ น้อย ๆ เกี่ยวกับวิธีการความสัมพันธ์ทุกอย่าง

(2) มี Vertex Array เริ่มต้นบางประเภทหรือไม่? เนื่องจากฉันลืม / ละเว้นglGenVertexArraysและglBindVertexArrayโปรแกรมของฉันทำงานได้ดีหากไม่มีมัน


แก้ไข:ฉันพลาดขั้นตอน glEnableVertexAttribArray...

(3) Vertex Attrib เชื่อมโยงกับ Vertex Array ในเวลาที่glVertexAttribPointerเรียกหรือไม่จากนั้นเราสามารถเปิด / ปิดการใช้งานแอตทริบิวต์glEnableVertexAttribArrayนั้นได้ตลอดเวลาไม่ว่า Vertex Array จะถูกผูกไว้ในปัจจุบันหรือไม่

หรือ (3b) Vertex Attrib เชื่อมโยงกับ Vertex Array ในเวลานั้นglEnableVertexAttribArrayถูกเรียกหรือไม่ดังนั้นเราจึงสามารถเพิ่ม Vertex Attrib เดียวกันให้กับ Vertex Arrays หลายตัวได้โดยการเรียกglEnableVertexAttribArrayในเวลาที่ต่างกันเมื่อมีการผูก Vertex Arrays ที่แตกต่างกัน

คำตอบ:


212

คำศัพท์บางคำอาจไม่ถูกต้อง:

  • A Vertex Arrayเป็นเพียงอาร์เรย์ (โดยทั่วไปคือ a float[]) ที่มีข้อมูลจุดยอด ไม่จำเป็นต้องผูกพันกับสิ่งใด ไม่ต้องสับสนกับ a Vertex Array Objectหรือ VAO ซึ่งฉันจะพูดถึงในภายหลัง
  • A Buffer Objectโดยทั่วไปเรียกว่าVertex Buffer Objectเมื่อเก็บจุดยอดหรือ VBO สั้น ๆ คือสิ่งที่คุณเรียกว่าเป็นเพียงไฟล์Buffer.
  • ไม่มีสิ่งใดถูกบันทึกกลับไปยังอาร์เรย์จุดสุดยอดglVertexAttribPointerทำงานเหมือนglVertexPointerหรือglTexCoordPointerทำงานได้ทุกประการแทนที่จะเป็นแอตทริบิวต์ที่ตั้งชื่อคุณจะต้องระบุตัวเลขที่ระบุคุณลักษณะของคุณเอง indexคุณผ่านค่านี้เป็น ทั้งหมดของคุณglVertexAttribPointerโทรได้คิวขึ้นสำหรับครั้งต่อไปที่คุณโทรหรือglDrawArrays glDrawElementsหากคุณมี VAO ผูกไว้ VAO จะจัดเก็บการตั้งค่าสำหรับแอตทริบิวต์ทั้งหมดของคุณ

ปัญหาหลักคือคุณกำลังสับสนแอตทริบิวต์จุดยอดกับ VAO แอตทริบิวต์จุดยอดเป็นเพียงวิธีใหม่ในการกำหนดจุดยอดเท็กซ์คูร์บรรทัดฐาน ฯลฯ สำหรับการวาด สถานะร้านค้า VAO ก่อนอื่นฉันจะอธิบายว่าการวาดภาพทำงานกับแอตทริบิวต์จุดยอดอย่างไรจากนั้นอธิบายว่าคุณสามารถลดจำนวนวิธีการเรียกด้วย VAO ได้อย่างไร:

  1. คุณต้องเปิดใช้งานแอตทริบิวต์ก่อนจึงจะใช้ใน shader ได้ ตัวอย่างเช่นถ้าคุณต้องการที่จะส่งไปยังจุด Shader คุณกำลังส่วนใหญ่มีแนวโน้มจะส่งเป็นแอตทริบิวต์แรก 0. glEnableVertexAttribArray(0);ดังนั้นก่อนที่จะทำให้คุณต้องเปิดการใช้งานด้วย
  2. เมื่อเปิดใช้งานแอตทริบิวต์แล้วคุณต้องกำหนดข้อมูลที่จะใช้ เพื่อที่จะทำดังนั้นคุณจำเป็นต้องผูก VBO คุณ glBindBuffer(GL_ARRAY_BUFFER, myBuffer);-
  3. และตอนนี้เราสามารถกำหนดแอตทริบิวต์ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);- ตามลำดับของพารามิเตอร์: 0 คือแอตทริบิวต์ที่คุณกำลังกำหนด 3 คือขนาดของจุดยอดแต่ละจุดGL_FLOATคือประเภทGL_FALSEหมายถึงการไม่ทำให้จุดยอดแต่ละจุดเป็นปกติเลขศูนย์ 2 ตัวสุดท้ายหมายความว่าไม่มีการก้าวข้ามหรือหักล้างจุดยอด
  4. วาดบางอย่างด้วย - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. สิ่งต่อไปที่คุณวาดอาจไม่ใช้แอตทริบิวต์ 0 (ตามความเป็นจริงจะเป็น แต่นี่เป็นตัวอย่าง) ดังนั้นเราจึงสามารถปิดใช้งานได้ - glDisableVertexAttribArray(0);

รวมสิ่งนั้นไว้ในการglUseProgram()โทรและคุณมีระบบการแสดงผลที่ทำงานร่วมกับเฉดสีได้อย่างเหมาะสม แต่สมมติว่าคุณมี 5 แอตทริบิวต์จุดยอดเท็กซ์คอยด์บรรทัดฐานสีและพิกัดไลท์แมปที่แตกต่างกัน ก่อนอื่นคุณจะglVertexAttribPointerโทรเพียงครั้งเดียวสำหรับแต่ละแอตทริบิวต์เหล่านี้และคุณต้องเปิดใช้งานแอตทริบิวต์ทั้งหมดก่อน สมมติว่าคุณกำหนดแอตทริบิวต์ 0-4 ตามที่ฉันระบุไว้ คุณจะเปิดใช้งานทั้งหมดดังนี้:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

และจากนั้นคุณจะต้องผูก VBOs แตกต่างกันสำหรับแต่ละแอตทริบิวต์ (เว้นแต่คุณจะเก็บไว้ทั้งหมดในหนึ่ง VBO และการใช้ชดเชย / กางเกง) แล้วคุณจะต้องทำให้แตกต่างกัน 5 glVertexAttribPointerสายจากglVertexAttribPointer(0,...);ไปglVertexAttribPointer(4,...);สำหรับจุดพิกัด Lightmap ตามลำดับ

หวังว่าระบบนั้นจะสมเหตุสมผล ตอนนี้ฉันจะไปยัง VAO เพื่ออธิบายวิธีใช้เพื่อลดจำนวนการเรียกใช้เมธอดเมื่อทำการเรนเดอร์ประเภทนี้ โปรดทราบว่าไม่จำเป็นต้องใช้ VAO

A Vertex Array Objectหรือ VAO ใช้เพื่อจัดเก็บสถานะของการglVertexAttribPointerโทรทั้งหมดและ VBO ที่กำหนดเป้าหมายเมื่อมีการglVertexAttribPointerโทรแต่ละครั้ง

glGenVertexArraysคุณสร้างหนึ่งที่มีการเรียกร้องให้ ในการจัดเก็บทุกสิ่งที่คุณต้องการใน VAO ผูกมันด้วยแล้วทำโทรวาดเต็มรูปแบบglBindVertexArray การโทรผูกการดึงทั้งหมดจะถูกดักจับและจัดเก็บโดย VAO คุณสามารถยกเลิกการผูก VAO ด้วยไฟล์glBindVertexArray(0);

ตอนนี้เมื่อคุณต้องการวาดวัตถุคุณไม่จำเป็นต้องเรียกการผูก VBO ทั้งหมดหรือการglVertexAttribPointerโทรอีกครั้งคุณเพียงแค่ต้องผูก VAO ด้วยglBindVertexArrayแล้วโทรglDrawArraysหรือจากนั้นglDrawElementsคุณจะวาดสิ่งเดียวกันทุกประการราวกับว่าคุณ กำลังทำการเรียกเมธอดเหล่านั้นทั้งหมด คุณอาจต้องการยกเลิกการผูก VAO ในภายหลังเช่นกัน

เมื่อคุณเลิกผูก VAO แล้วสถานะทั้งหมดจะกลับสู่สภาพเดิมก่อนที่คุณจะผูก VAO ฉันไม่แน่ใจว่าการเปลี่ยนแปลงใด ๆ ที่คุณทำในขณะที่มีการผูก VAO ไว้หรือไม่ แต่สามารถคำนวณได้อย่างง่ายดายด้วยโปรแกรมทดสอบ ฉันเดาว่าคุณคงคิดว่าglBindVertexArray(0);มีผลผูกพันกับ VAO "default" ...


อัปเดต: มีคนแจ้งให้ฉันทราบถึงความจำเป็นในการเรียกเงินจริง ปรากฎว่าคุณไม่จำเป็นต้องทำการโทรแบบเต็มรูปแบบเมื่อตั้งค่า VAO เพียงแค่สิ่งที่มีผลผูกพันทั้งหมด ไม่รู้ว่าทำไมฉันถึงคิดว่ามันจำเป็นก่อนหน้านี้ แต่ตอนนี้แก้ไขแล้ว


10
"วัตถุบัฟเฟอร์ Vertex หรือ VBO (บางครั้งเรียกว่าเป็นเพียงวัตถุบัฟเฟอร์)" บางครั้ง "เรียกสิ่งนั้นเพราะนั่นคือสิ่งที่เรียกว่าจริงๆ มันเป็นเพียงวัตถุบัฟเฟอร์ไม่แตกต่างจากวัตถุบัฟเฟอร์อื่น ๆ ที่คุณอาจใช้สำหรับบล็อกแบบสม่ำเสมอการถ่ายโอนพิกเซลการตอบสนองการเปลี่ยนแปลงหรือการใช้งานอื่น ๆ ข้อมูลจำเพาะของ OpenGL ไม่เคยอ้างถึงสิ่งใด ๆ ในฐานะ "วัตถุบัฟเฟอร์จุดยอด"; แม้แต่ข้อมูลจำเพาะของส่วนขยายดั้งเดิมก็ไม่เคยเรียกมันว่า
Nicol Bolas

3
คำตอบที่ยอดเยี่ยม ขอขอบคุณที่สละเวลาเขียนสิ่งนี้! คู่ติดตามคำถามว่า: (1) คุณบอกว่า "ก่อนที่คุณจะแสดงผลและก่อนที่คุณกำหนดแอตทริบิวต์ที่คุณจะต้องเปิดใช้งานได้กับ glEnableVertexAttribArray (0)" - คุณแน่ใจหรือว่าจะต้องมีการเปิดใช้งานก่อนที่จะเรียกร้องให้glVertexAttribPointer? ในการทดสอบของฉันลำดับดูเหมือนจะไม่สำคัญ (2) ถ้าฉันเข้าใจคุณอย่างถูกต้องคุณสมบัติของ Vertex จะเป็นแบบโกลบอลและมีเพียงสถานะเปิด / ปิดใช้งานเท่านั้นที่บันทึกไว้ใน VAO ที่ผูกไว้ในปัจจุบัน?
mpen

1
(1) ผมไม่คิดว่าเรื่องการสั่งซื้อตราบใดที่คุณได้เปิดใช้งานมาก่อนหรือglDrawArrays glDrawElementsฉันจะอัปเดตโพสต์เพื่อแสดงว่า (2) ใช่ แต่ไม่ใช่แค่สถานะเปิด / ปิดที่จัดเก็บ แต่ทุกอย่างที่เกี่ยวข้องกับการโทรเหล่านั้น - สิ่งที่ผูกพันกับ GL_ARRAY_BUFFER ในเวลานั้นประเภทก้าวย่างและออฟเซ็ต โดยพื้นฐานแล้วจะจัดเก็บไว้เพียงพอที่จะเปลี่ยนแอตทริบิวต์ Vertex ทั้งหมดกลับไปเป็นวิธีที่คุณตั้งค่าด้วย VAO
Robert Rouhani

2
ใช่ VAO ได้รับการออกแบบมาเพื่อให้คุณแทนที่วิธีการวาดส่วนใหญ่ด้วยการผูก VAO ทุกเอนทิตีสามารถมี VAO แยกกันได้และจะยังใช้งานได้ดี คุณยังคงต้องอัปเดตเครื่องแบบและผูกพื้นผิวของคุณเอง และคุณต้องใช้ดัชนีแอตทริบิวต์เดียวกันกับที่คุณต้องผูกแอตทริบิวต์ของ shader กับดัชนีแอตทริบิวต์ไม่ว่าจะผ่านlayout(location = x)ใน shader หรือglBindAttributeLocationเมื่อรวบรวม shader ตัวอย่าง
Robert Rouhani

8
โปรดทราบว่าใน OpenGL สมัยใหม่ไม่มีออบเจ็กต์อาร์เรย์จุดยอดเริ่มต้นอีกต่อไปคุณต้องสร้างขึ้นมาเองมิฉะนั้นแอปพลิเคชันของคุณจะไม่ทำงานในบริบทที่เข้ากันได้กับการส่งต่อ
Overv

3

คำศัพท์และลำดับของ API ที่จะเรียกนั้นค่อนข้างสับสนแน่นอน สิ่งที่น่าสับสนยิ่งไปกว่านั้นคือการเชื่อมโยงด้านต่างๆ - บัฟเฟอร์แอตทริบิวต์จุดยอดทั่วไปและตัวแปรคุณลักษณะ shader เชื่อมโยงกันอย่างไร ดูคำศัพท์ OpenGLสำหรับคำอธิบายที่ดีงาม

นอกจากนี้ลิงก์OpenGL-VBO, shader, VAO ยังแสดงตัวอย่างง่ายๆด้วยการเรียก API ที่จำเป็น เหมาะอย่างยิ่งสำหรับผู้ที่เปลี่ยนจากโหมดทันทีเป็นไปป์ไลน์ที่ตั้งโปรแกรมได้

หวังว่าจะช่วยได้

แก้ไข: ดังที่คุณเห็นจากความคิดเห็นด้านล่างผู้คนสามารถตั้งสมมติฐานและข้ามไปที่ข้อสรุปได้ ความจริงก็คือมันค่อนข้างสับสนสำหรับผู้เริ่มต้น


" ดูคำศัพท์ OpenGL สำหรับคำอธิบายที่ค่อนข้างดี " ในเวลาไม่ถึง 1 นาทีฉันพบข้อมูลที่ผิดอยู่หนึ่งส่วน: "สิ่งเหล่านี้จะถูกแทนที่ด้วยแอตทริบิวต์จุดยอดทั่วไปด้วยตัวระบุ (เรียกว่าดัชนี) ที่เชื่อมโยงกับตัวแปร shader (สำหรับ cooordinates สี ฯลฯ ) ที่ประมวลผลแอตทริบิวต์ " ไม่เรียกว่า "ดัชนี" พวกเขาคือ "สถานที่" นั่นเป็นความแตกต่างที่สำคัญมากเนื่องจากแอตทริบิวต์จุดยอดยังมี "ดัชนี"ซึ่งแตกต่างจากสถานที่ตั้งมาก นั่นคือเว็บไซต์ที่แย่มาก
Nicol Bolas

2
นั่นเป็นความคิดเห็นที่ยุติธรรม แต่ไม่ถูกต้องทั้งหมด ถ้าคุณมองไปที่ OpenGL API เพื่อกำหนดคุณลักษณะทั่วไปglVertexAttribPointer , void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer), indexระบุจะเรียกว่าเป็น ตัวระบุเดียวกันในบริบทของโปรแกรมที่เรียกว่าlocationใน API ได้glGetAttribLocation
ap-osd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.