Vertex Array Objects คืออะไร?


116

วันนี้ฉันเพิ่งเริ่มเรียนรู้ OpenGL จากบทช่วยสอนนี้: http://openglbook.com/the-book/
ฉันมาถึงบทที่ 2 ซึ่งฉันวาดรูปสามเหลี่ยมและฉันเข้าใจทุกอย่างยกเว้น VAO (ตัวย่อนี้ใช้ได้หรือไม่) บทช่วยสอนมีรหัสนี้:

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);

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

Vertex Array Object (หรือ VAO) เป็นอ็อบเจ็กต์ที่อธิบายวิธีการจัดเก็บแอ็ตทริบิวต์จุดยอดใน Vertex Buffer Object (หรือ VBO) ซึ่งหมายความว่า VAO ไม่ใช่ออบเจ็กต์จริงที่จัดเก็บข้อมูลจุดยอด แต่เป็นตัวบอกข้อมูลของจุดยอด แอตทริบิวต์ Vertex สามารถอธิบายได้ด้วยฟังก์ชัน glVertexAttribPointer และฟังก์ชันน้องสาวสองตัว glVertexAttribIPointer และ glVertexAttribLPointer ซึ่งเป็นฟังก์ชันแรกที่เราจะสำรวจด้านล่าง

ฉันไม่เข้าใจว่า VAO อธิบายแอตทริบิวต์จุดยอดอย่างไร ฉันไม่ได้อธิบายไว้ แต่อย่างใด ได้รับข้อมูลจาก glVertexAttribPointer หรือไม่ ฉันเดาว่าต้องเป็นอย่างนี้ VAO เป็นเพียงปลายทางสำหรับข้อมูลจาก glVertexAttribPointer หรือไม่

หมายเหตุด้านข้างบทช่วยสอนที่ฉันปฏิบัติตามเป็นที่ยอมรับหรือไม่ มีอะไรที่ฉันควรระวังหรือบทช่วยสอนที่ดีกว่าให้ทำตาม?

คำตอบ:


102

"Vertex Array Object" มาให้คุณโดยคณะอนุกรรมการ OpenGL ARB สำหรับชื่อโง่

คิดว่ามันเป็นวัตถุรูปทรงเรขาคณิต (ในฐานะโปรแกรมเมอร์ SGI Performer สมัยก่อนฉันเรียกพวกเขาว่า geosets) ตัวแปรอินสแตนซ์ / สมาชิกของวัตถุคือตัวชี้จุดยอดของคุณตัวชี้ปกติตัวชี้สีตัวชี้แอตทริบิวต์ N ...

เมื่อ VAO ถูกผูกมัดครั้งแรกคุณจะกำหนดสมาชิกเหล่านี้โดยการโทร

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

และอื่น ๆ แอตทริบิวต์ใดที่เปิดใช้งานและตัวชี้ที่คุณจัดหาจะถูกเก็บไว้ใน VAO

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

(การตั้งค่าเพียงครั้งเดียวการใช้งานหลายครั้งเป็นวิธีที่ง่ายที่สุดในการใช้ VAO แต่คุณยังสามารถเปลี่ยนแอตทริบิวต์ได้เพียงแค่ผูกมันและทำการเรียกเปิดใช้งาน / ตัวชี้เพิ่มเติม VAO ไม่ใช่ค่าคงที่)

ข้อมูลเพิ่มเติมในการตอบคำถามของ Patrick:

ค่าเริ่มต้นสำหรับ VAO ที่สร้างขึ้นใหม่คือว่างเปล่า (AFAIK) ไม่มีรูปทรงเรขาคณิตเลยแม้แต่จุดยอดดังนั้นหากคุณพยายามวาดมันคุณจะได้รับข้อผิดพลาด OpenGL สิ่งนี้มีเหตุผลพอสมควรเช่นเดียวกับใน "เริ่มต้นทุกอย่างเป็น False / NULL / zero"

คุณจะต้องทำglEnableClientStateเมื่อตั้งค่าเท่านั้น VAO จะจดจำสถานะเปิด / ปิดสำหรับตัวชี้แต่ละตัว

ใช่ VAO จะเก็บและglEnableVertexAttribArray glVertexAttribจุดยอดเก่าปกติสี ... อาร์เรย์จะเหมือนกับอาร์เรย์แอตทริบิวต์จุดยอด == # 0 และอื่น ๆ


62
'"Vertex Array Object" มาถึงคุณโดยคณะอนุกรรมการ OpenGL ARB สำหรับชื่อโง่ ๆ ' ใช่ชื่อโง่ ๆ สำหรับออบเจ็กต์ที่เก็บการโยงอาร์เรย์จุดยอด
Nicol Bolas

2
นอกจากนี้ VAO ที่เกี่ยวข้องกับglVertexAttribPointer
Patrick

2
โปรดเพิ่มข้อมูลเกี่ยวกับการใช้แอตทริบิวต์จุดยอดทั่วไปสำหรับผู้ที่ใช้โปรไฟล์หลัก
Oskar

8
@NicolBolas ชื่อที่ดีกว่านี้น่าจะเป็นVertexArrayMacroหรืออะไรที่คล้ายกัน
bobobobo

7
@NicolBolas "Vertex Array Object" เป็นชื่อที่แย่มาก มันเป็นเรื่องเกี่ยวกับการผูกข้อมูลคุณลักษณะ ไม่เกี่ยวกับอาร์เรย์ของจุดยอดตามที่ชื่อมีความหมาย ไม่มีการอ้างอิงถึงการผูกหรือแอตทริบิวต์ในชื่อและเนื่องจาก "จุดยอดอาร์เรย์" เป็นแนวคิดที่แยกออกจากกันจึงทำให้เข้าใจยากขึ้น IMHO, "(Vertex) Attributes Binding Object" นั้นเข้าใจง่ายกว่า แม้แต่ Geometry Object ก็ดีกว่า: ฉันไม่ชอบ แต่อย่างน้อยมันก็ไม่มากเกินไป
AkiRoss

9

Vertex Array Objects เป็นเหมือนมาโครในโปรแกรมประมวลผลคำและอื่น ๆ คำอธิบายที่ดีจะพบว่าที่นี่

แมโครเพียงจำการกระทำที่คุณทำเช่นเปิดใช้งานคุณลักษณะนี้ผูกบัฟเฟอร์ที่ ฯลฯ เมื่อคุณเรียกglBindVertexArray( yourVAOId )มันก็ไกลเหล่านั้นแอตทริบิวต์การผูกตัวชี้และ buffer ผูก

ดังนั้นการเรียกวาดครั้งต่อไปของคุณจะใช้อะไรก็ได้ที่ถูกผูกไว้โดย VAO

VAO ของไม่ได้เก็บข้อมูลจุดสุดยอด ไม่ข้อมูลจุดยอดจะถูกเก็บไว้ในบัฟเฟอร์จุดยอดหรือในอาร์เรย์ของหน่วยความจำไคลเอนต์


19
-1: ไม่เหมือนมาโคร หากเป็นเช่นนั้นการผูก VAO ใหม่จะไม่ปิดใช้งานอาร์เรย์จุดยอดที่เปิดใช้งานโดย VAO ก่อนหน้าเว้นแต่ VAO ใหม่จะ "บันทึก" ไว้ซึ่งคุณได้ปิดใช้งานอาร์เรย์เหล่านั้นอย่างชัดเจน VAOs เช่นเดียวกับวัตถุ OpenGL ทั้งหมดถือสถานะไม่ใช่คำสั่ง คำสั่งเพียงแค่เปลี่ยนสถานะ แต่วัตถุมาพร้อมกับชุดสถานะเริ่มต้น นั่นเป็นเหตุผลที่มีผลผูกพันที่สร้างขึ้นใหม่ VAO จะเสมอปิดใช้งานคุณลักษณะทั้งหมด
Nicol Bolas

7

ฉันมักจะคิดว่า VAO เป็นอาร์เรย์ของบัฟเฟอร์ข้อมูลที่ OpenGL ใช้ การใช้ OpenGL ที่ทันสมัยคุณจะสร้าง VAO และ Vertex Buffer Objects

ใส่คำอธิบายภาพที่นี่

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

ขั้นตอนต่อไปคือการผูกข้อมูลกับบัฟเฟอร์:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

ณ จุดนี้ OpenGL เห็น:

ใส่คำอธิบายภาพที่นี่

ตอนนี้เราสามารถใช้ glVertexAttribPointer เพื่อบอก OpenGL ว่าข้อมูลในบัฟเฟอร์แสดงถึงอะไร:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

ใส่คำอธิบายภาพที่นี่

ขณะนี้ OpenGL มีข้อมูลอยู่ในบัฟเฟอร์และรู้ว่าข้อมูลถูกจัดระเบียบอย่างไรให้เป็นจุดยอด สามารถใช้กระบวนการเดียวกันกับพิกัดพื้นผิว ฯลฯ แต่สำหรับพิกัดพื้นผิวจะมีสองค่า

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

ถัดไปคุณสามารถผูกพื้นผิวและวาดอาร์เรย์คุณจะต้องสร้าง Vert และ Frag shader รวบรวมและแนบเข้ากับโปรแกรม (ไม่รวมอยู่ที่นี่)

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square

1
+1 สำหรับแผนภาพ มีความหมายมากกว่าสิ่งอื่นใดที่ฉันเคยเห็น จากสิ่งที่ฉันสามารถรวบรวมได้ VAO เป็นเหมือนโครงสร้างที่สมาชิกแต่ละคนเป็นอาร์เรย์ที่มีความยาวเท่ากัน สมาชิกที่สำคัญที่สุดประกอบด้วยจุดในปริภูมิ 3 มิติ ("จุดยอด") สมาชิกคนอื่น ๆ มีข้อมูลเสริม ("แอตทริบิวต์") เกี่ยวกับจุดเหล่านั้นเช่นสีและปกติ น่ารำคาญจริงๆที่บทช่วยสอนเพียงแค่บอกให้คุณคัดลอกและวางโค้ดโดยไม่ต้องอธิบายว่าโค้ดกำลังทำอะไรอยู่ มีคนเรียนรู้น้อยมากจากการคัดลอกและวาง ...
allyourcode

6

VAO เป็นออบเจ็กต์ที่แสดงถึงขั้นตอนการดึงข้อมูลจุดยอดของไปป์ไลน์ OpenGL และใช้ในการจัดหาอินพุตให้กับจุดยอด Shader

คุณสามารถสร้างวัตถุอาร์เรย์จุดยอดได้เช่นนี้

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

ก่อนอื่นให้ทำตัวอย่างง่ายๆ พิจารณาพารามิเตอร์อินพุตดังกล่าวในโค้ด shader

layout (location = 0) in vec4 offset; // input vertex attribute

ในการกรอกแอตทริบิวต์นี้เราสามารถใช้ได้

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

แม้ว่าออบเจ็กต์อาร์เรย์จุดยอดจะเก็บค่าแอตทริบิวต์แบบคงที่เหล่านี้ไว้ให้คุณ แต่ก็สามารถทำอะไรได้มากกว่านี้

หลังจากสร้างวัตถุอาร์เรย์จุดยอดเราสามารถเริ่มกรอกข้อมูลในสถานะได้ เราจะขอให้ OpenGL กรอกข้อมูลโดยอัตโนมัติโดยใช้ข้อมูลที่เก็บไว้ในวัตถุบัฟเฟอร์ที่เราจัดหา แอตทริบิวต์จุดยอดแต่ละรายการจะดึงข้อมูลจากบัฟเฟอร์ที่ผูกไว้กับการเชื่อมบัฟเฟอร์จุดยอดหนึ่งในหลาย ๆ glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex)สำหรับการใช้งานเรานี้สิ้นสุด นอกจากนี้เรายังใช้glVertexArrayVertexBuffer()ฟังก์ชันเพื่อผูกบัฟเฟอร์กับการผูกบัฟเฟอร์จุดยอด เราใช้ฟังก์ชั่นในการอธิบายถึงรูปแบบและรูปแบบของข้อมูลและในที่สุดก็ช่วยให้เรากรอกข้อมูลอัตโนมัติของแอตทริบิวต์โดยการเรียกglVertexArrayAttribFormat()glEnableVertexAttribArray()

เมื่อแอตทริบิวต์จุดสุดยอดมีการเปิดใช้ OpenGL จะป้อนข้อมูลไปยังจุดสุดยอด Shader บนพื้นฐานของรูปแบบและสถานที่ข้อมูลที่คุณให้กับ และglVertexArrayVertexBuffer() glVertexArrayAttribFormat()เมื่อแอตทริบิวต์ถูกปิดใช้งาน, Shader glVertexAttrib*()จุดสุดยอดจะมีให้กับข้อมูลคงที่ที่คุณให้กับเรียกร้องให้

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

และโค้ดใน shader

layout (location = 0) in vec4 position;

glDeleteVertexArrays(1, &vao)หลังจากที่ทุกคนที่คุณจำเป็นต้องเรียกร้องให้


คุณสามารถอ่านOpenGL SuperBibleเพื่อทำความเข้าใจได้ดีขึ้น


3
เป็นเรื่องดีที่เห็นผู้คนส่งเสริมการใช้ OpenGL สไตล์ DSA
Nicol Bolas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.