ความแตกต่างใน glDrawArrays และ glDrawElements


12

ในขณะที่สดชื่นใจของฉันกับ OpenGL ES ฉันมาข้ามและglDrawArraysglDrawElements

ฉันเข้าใจว่ามีการใช้งานอย่างไรและเข้าใจว่าทำไมจึงแตกต่างกัน

สิ่งที่ฉันดูเหมือนจะไม่เข้าใจก็คือฉันไม่สามารถดูวิธีการglDrawElementsบันทึกการโทรแบบดึง ( การบันทึกการโทรแบบดึงเป็นคำอธิบายที่กล่าวถึงในหนังสือส่วนใหญ่ที่ฉันได้อ่าน

ลองนึกภาพสถานการณ์ง่าย ๆ ที่ฉันพยายามวาดรูปสี่เหลี่ยมโดยใช้รูปสามเหลี่ยม 2 รูป

ฉันจะต้องมีชุดของ 6 จุดยอดใช้glDrawArraysในขณะที่ใช้glDrawElementsฉันต้องการเพียง 4 นอกเหนือจากอาร์เรย์ดัชนีที่มี 6 องค์ประกอบ

รับข้างต้นนี่คือสิ่งที่ฉันไม่เข้าใจ:

  1. จะglDrawElementsบันทึกการเรียกสายได้อย่างไรถ้ายังต้องการใช้อาร์เรย์ดัชนี (ดัชนี 6 ตัวในกรณีสี่เหลี่ยม) เพื่อจัดทำดัชนีลงในอาร์เรย์จุดสุดยอดของฉันที่มี 4 องค์ประกอบ (6 ครั้ง) พูดอีกอย่างว่าหมายความว่าglDrawElementsยังคงต้องมีการดึงสายเรียกเข้าทั้งหมด 6 ครั้งglDrawArraysใช่ไหม
  2. จะใช้glDrawElementsพื้นที่ประหยัดได้อย่างไรถ้าใครยังคงต้องมี 2 อาร์เรย์คือหนึ่งสำหรับจุดยอดและอีกหนึ่งสำหรับดัชนี?
  3. ในกรณีของการวาดรูปสี่เหลี่ยมจัตุรัสจากรูปสามเหลี่ยม 2 รูปแบบเพื่อความง่ายจำนวนการเรียกแบบดึงglDrawElements(จำนวน 4 รายการในอาร์เรย์จุดยอดและ 6 รายการในแถวลำดับดัชนี) และglDrawArrays(6 รายการเฉพาะในจุดสุดยอดอาร์เรย์) ต้องแยกกันหรือไม่?

ขอบคุณ

คำตอบ:


16

แต่ละ glDraw * เป็นการเรียกสาย

1 glDrawArrays คือ 1 การเรียกสาย
1 glDrawElements คือ 1 การเรียกสาย

ไม่สำคัญ (เท่าที่มีการนับจำนวนการโทร) จำนวนจุดยอดหรือดัชนีที่คุณใช้คือ 1 glDraw * คือ 1 การโทร

กรณีง่าย ๆ ของการวาดภาพสี่คน (สมมติว่าเป็นรุ่น GL ที่คุณใช้ไม่ได้ลบหน้าสี่หน้า) หรือรูปสามเหลี่ยมไม่ใช่ตัวอย่างที่ดีสำหรับการเปรียบเทียบสิ่งเหล่านี้เนื่องจากรายการของสี่คนหรือรายการรูปสามเหลี่ยมสามารถวาดด้วยการโทรครั้งเดียว และ glDrawArrays จะมีประสิทธิภาพมากกว่าเนื่องจากไม่มีค่าใช้จ่ายเพิ่มเติมในการจัดทำดัชนี

ลองพิจารณากรณีที่ซับซ้อนขึ้นเล็กน้อย แต่กรณีที่เป็นตัวแทนของสถานการณ์โลกแห่งความเป็นจริงที่มากขึ้น ลองนึกภาพว่าคุณมีรูปแบบประกอบด้วยแถบสามเหลี่ยมและแฟน ๆ หลายแบบ:

glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);

ในตัวอย่างนี้การใช้ glDrawArrays ให้การเรียกแบบดึงทั้งหมด 6 ครั้งสำหรับโมเดล อย่างไรก็ตามทั้งแถบและพัดลมสามารถเปลี่ยนเป็นรูปสามเหลี่ยมที่จัดทำดัชนีได้ง่ายดังนั้นเพิ่มดัชนีและกลายเป็น:

glDrawElements (GL_TRIANGLES, .....);

นั่นคือหนึ่งการเรียกร้องสำหรับทั้งรุ่น


ข้อดีของ glDrawElements ผ่าน glDrawArrays เป็นมากกว่าการประหยัดหน่วยความจำซึ่งเป็นวิธีการวัดที่ไร้เดียงสา มีความเป็นไปได้สำหรับการประหยัดหน่วยความจำซึ่งสามารถนำจุดยอดกลับมาใช้ใหม่ได้เนื่องจากโดยทั่วไปดัชนีจะมีขนาดเล็กกว่าจุดสุดยอด (ดังนั้นแม้ว่าคุณจะมีดัชนีจำนวนมากที่มีความสมดุล

  • ลดจำนวนการเรียกสาย การเรียกแต่ละครั้งจะมีค่าใช้จ่ายเกิดขึ้นจากสถานะการตรวจสอบความถูกต้องการตั้งค่า ฯลฯ และค่าใช้จ่ายส่วนใหญ่นี้เกิดขึ้นที่ด้าน CPU ด้วยการลดจำนวนการเรียกเสมอเราหลีกเลี่ยงค่าใช้จ่ายส่วนใหญ่นี้

  • จุดยอดกลับมาใช้ใหม่ นี่เป็นมากกว่าการประหยัดหน่วยความจำ GPU ของคุณน่าจะมีแคชจุดสุดยอดของฮาร์ดแวร์ซึ่งสามารถเก็บจุดยอดที่เพิ่งเปลี่ยนได้ หากจุดสุดยอดเดียวกันเข้ามาอีกครั้งและถ้าอยู่ในแคชก็สามารถนำกลับมาใช้ใหม่จากแคชแทนที่จะต้องแปลงอีกครั้ง ฮาร์ดแวร์ของคุณจะตรวจสอบแคชโดยการเปรียบเทียบดัชนีดังนั้นในเทอม OpenGL วิธีเดียวที่คุณจะใช้แคชคือการใช้ glDrawElements


ดังนั้นเพื่อตอบคำถามเฉพาะของคุณ:

  1. glDrawElements จะบันทึกการโทรได้อย่างไร ... ? ในตัวอย่างที่คุณใช้ไม่เป็นเช่นนั้น แต่ละคนมีหนึ่งสายที่โทรออก ขณะที่ฉันพูดถึงข้างต้นตัวอย่างนี้เป็นสิ่งที่แย่มากสำหรับการเปรียบเทียบทั้งสอง

  2. จะใช้ glDrawElements ช่วยประหยัดพื้นที่ได้อย่างไร ... ? เพราะดัชนีมีขนาดเล็กกว่าจุดยอด ดัชนีแบบ 16 บิตคือสองไบต์ตำแหน่งจุดสุดยอด / สี / texcoord คือ 24 ไบต์ 6 จุดยอดคือ 144 ไบต์, 4 จุดยอดบวก 6 ดัชนีคือ 108 ไบต์

  3. ในกรณีที่วาดรูปสี่เหลี่ยมจัตุรัสจากรูปสามเหลี่ยมสองรูป ... หนึ่งและหนึ่ง การเรียก glDraw * เป็นการเรียกแบบดึงครั้งเดียวจำนวนจุดยอดหรือดัชนีที่ใช้ไม่เกี่ยวข้องกับการวัดนี้ แต่ฉันต้องเน้นอีกครั้งนี่เป็นตัวอย่างที่ดีมากสำหรับการเปรียบเทียบทั้งสอง


ท้ายที่สุดและซับซ้อนเพียงเล็กน้อยในขณะที่ glDrawElements กับรูปสามเหลี่ยมเป็นเส้นทางที่ดีที่สุดในเดสก์ท็อป GPU บนมือถือมันอาจแตกต่างกันมาก GPU มือถือบางรุ่นอาจชอบ glDrawArrays ด้วย GL_TRIANGLE_STRIP (คุณจะเพิ่มรูปสามเหลี่ยมที่เสื่อมลงเพื่อเชื่อมต่อแบบดั้งเดิมในกรณีนี้) และฉันไม่ได้สัมผัสหัวข้อขั้นสูงเช่นการรีสตาร์ทแบบดั้งเดิมหรือการวาดหลายครั้ง


ขอบคุณมากสำหรับความคิดเห็นของคุณ; ฉันแค่สละเวลาอ่านสิ่งที่คุณแบ่งปัน แค่อยากให้คุณรู้ว่าฉันยอมรับคำตอบของคุณ ขอบคุณอีกครั้ง.
Unheilig

ในตัวอย่างของคุณในการแปลง 6 DrawTriangleFans และ DrawTriangleStrips เรียกไปยัง 1 DrawTriangles สายเดียว สิ่งนี้ช่วยปรับปรุงประสิทธิภาพได้จริงหรือ เพื่อความเข้าใจของฉันการวาดแถบสามเหลี่ยมประกอบด้วย 100 รูปสามเหลี่ยมมีประสิทธิภาพมากกว่าการวาดรูปสามเหลี่ยม 100 รูปแยกกันและผลประโยชน์จะชดเชยการลงโทษที่เกิดขึ้นจากการใช้ฟังก์ชัน 6 สายได้อย่างง่ายดายมากกว่า 1
Samuel Li

@SamuelLi 6 ต่อ 1 จะไม่ได้รับการปรับปรุงอย่างมากมันมีวัตถุประสงค์เพียงเพื่อแสดงให้เห็นถึงหลักการ นอกจากนี้และตามที่ฉันระบุแถบโดยทั่วไปจะไม่ได้รับประสิทธิภาพมากกว่ารายการที่จัดทำดัชนีไว้ในฮาร์ดแวร์เดสก์ท็อปหลังปี 1998 ตรวจสอบวันที่ในเอกสารใด ๆ ที่แนะนำให้คุณใช้แถบ
Maximus Minimus

เข้าใจแล้วขอบคุณ @MaximusMinimus
Samuel Li
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.