ทำไมบทช่วยสอนใช้วิธีการต่าง ๆ ในการเรนเดอร์ OpenGL


43

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/

บทเรียนสองบทนี้ใช้วิธีการต่างกันโดยสิ้นเชิงเพื่อให้ได้ผลลัพธ์ที่ใกล้เคียงกัน แรก ๆ glBegin(GL_QUADS)ที่ใช้สิ่งที่ต้องการ อันที่สองใช้สิ่งที่ชอบvertexBufferObjectsshaders ตาม GLEW แต่ผลลัพธ์ก็เหมือนกัน: คุณจะได้รูปร่างพื้นฐาน

ทำไมความแตกต่างเหล่านี้จึงมีอยู่?

วิธีแรกดูเหมือนง่ายกว่าที่จะเข้าใจ ข้อดีของวิธีที่สองที่ซับซ้อนคืออะไร


4
ไม่เคยมีวิธีการหนึ่งที่จะทำให้แมวเป็นที่รัก
ฟิลิปป์

4
@ ฟิลิปใช่ แต่มีวิธีที่ถูกต้องและวิธีที่ผิดวิธีเก่าและวิธีใหม่ (และเป็นคำตอบด้านล่างแสดงให้เห็นว่าวิธีการเก่าและใหม่อาจไม่สามารถทำงานร่วมกันได้ในทุกสถานการณ์)
Andrew Hill

3
นอกจากนี้ยังไม่มีวิธีการที่ถูกต้องและวิธีการที่ไม่ถูกต้องเพียงวิธีที่เลวร้ายลงและวิธีที่ดีกว่า (ในมิติที่แตกต่างกัน)
253751

glBeginและglEndเลิกใช้แล้วเนื่องจากไม่มีประสิทธิภาพอย่างยิ่งสำหรับสถาปัตยกรรมกราฟิกปัจจุบัน
อเล็กซ์

คำตอบ:


77

OpenGL มีสี่รุ่นหลักที่แตกต่างกันไม่นับรุ่นสำหรับอุปกรณ์มือถือและระบบฝังตัว (OpenGL | ES) และเว็บผ่าน JavaScript (WebGL) เช่นเดียวกับ Direct3D 11 มีวิธีการทำสิ่งต่าง ๆ มากกว่า Direct3D 8 ดังนั้น OpenGL 3 จึงมีวิธีการทำสิ่งต่าง ๆ มากกว่า OpenGL 1 ความแตกต่างที่สำคัญคือรุ่น OpenGL ส่วนใหญ่เป็นเพียงส่วนเสริมของรุ่นเก่า (แต่ไม่ใช่ ทั้งหมด)

ด้านบนของรุ่นที่แตกต่างกันและรุ่นของ OpenGL, OpenGL หลักยังเพิ่มแนวคิดของโปรไฟล์ โปรไฟล์ความเข้ากันได้ (ซึ่งเปิดใช้งานการสนับสนุน API จากเวอร์ชันเก่ากว่า) และโปรไฟล์หลัก (ซึ่งปิดใช้งาน API เก่าเหล่านั้น) สิ่งต่าง ๆ เช่นglBeginไม่ทำงานเมื่อคุณใช้โปรไฟล์หลัก แต่จะเมื่อคุณใช้โปรไฟล์ความเข้ากันได้ (ซึ่งเป็นค่าเริ่มต้น)

ในฐานะที่เป็นภาวะแทรกซ้อนที่สำคัญอีกประการหนึ่งการใช้งานบางอย่างของ OpenGL (เช่น Apple และอื่น ๆ ) จะเปิดใช้งานฟีเจอร์ OpenGL ที่ใหม่กว่าเมื่อคุณใช้งานโปรไฟล์หลัก ซึ่งหมายความว่าคุณต้องหยุดใช้ API ที่เก่ากว่าเพื่อใช้ API ที่ใหม่กว่า

จากนั้นคุณจะจบลงด้วยสถานการณ์ที่สับสนมากสำหรับบทเรียน:

  1. บทช่วยสอนเก่าและใช้ API ที่เลิกใช้แล้วเท่านั้น
  2. บทแนะนำใหม่และเขียนได้ดีและใช้ API ที่เข้ากันได้กับ Core เท่านั้น
  3. บทแนะนำเป็นเรื่องใหม่ แต่ทำผิดพลาดโดยสมมติว่าคุณกำลังทำงานกับไดรเวอร์ที่เปิดใช้งาน API ทั้งหมดในโหมดความเข้ากันได้และผสมผสานทั้ง API ใหม่และเก่าได้อย่างอิสระ
  4. บทช่วยสอนสำหรับ OpenGL รุ่นอื่นเช่น OpenGL | ES ที่ไม่รองรับ API เก่า ๆ ในทุกรุ่น

สิ่งที่ต้องการglBeginเป็นส่วนหนึ่งของสิ่งที่บางครั้งเรียกว่า API โหมดทันที นี่เป็นความสับสนอย่างมากเพราะไม่มีสิ่งใดในโหมด OpenGL และ "โหมดเร่งด่วน" ที่มีความหมายแตกต่างกันในกราฟิก เป็นการดีกว่าที่จะอ้างถึง OpenGL 1.x APIs เนื่องจากล้าสมัยตั้งแต่ OpenGL 2.1

1.x API ของ OpenGL จะส่งจุดยอดทันทีไปยังกราฟิกไลน์ในสมัยก่อน สิ่งนี้ทำงานได้ดีเมื่อความเร็วของฮาร์ดแวร์ที่แสดงจุดยอดโดยประมาณเท่ากับความเร็วของ CPU ที่สร้างข้อมูลจุดสุดยอด OpenGL ย้อนกลับไปแล้วเพียงแค่ลดการแรสเตอร์ของสามเหลี่ยมและไม่มาก

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

ไดรเวอร์ GL ทั้งหมดต้องเลียนแบบglBeginโดยการจัดสรรบัฟเฟอร์จุดสุดยอดภายในวางจุดยอดที่ส่งglVertexเข้ามาในบัฟเฟอร์นี้แล้วส่งบัฟเฟอร์ทั้งหมดนั้นในการเรียกการดึงครั้งเดียวเมื่อglEndถูกเรียก โอเวอร์เฮดของฟังก์ชั่นเหล่านี้ยิ่งใหญ่กว่าถ้าคุณเพิ่งอัพเดทบัฟเฟอร์จุดสุดยอดด้วยตัวเองซึ่งเป็นสาเหตุที่เอกสารบางอย่าง (ผิดพลาดมาก!) อ้างถึงเวอร์เท็กซ์บัฟเฟอร์ว่า "การเพิ่มประสิทธิภาพ" (ไม่ใช่การเพิ่มประสิทธิภาพ พูดคุยกับ GPU)

มี API อื่น ๆ อีกมากมายที่เลิกใช้แล้วหรือล้าสมัยใน OpenGL ในช่วงหลายปีที่ผ่านมา ไปป์ไลน์ฟังก์ชั่นคงที่ที่เรียกว่าเป็นอีกชิ้นส่วนดังกล่าว เอกสารบางอย่างอาจยังคงใช้ไปป์ไลน์นี้หรือผสมกับไปป์ไลน์ที่ตั้งโปรแกรมได้ ไปป์ไลน์ฟังก์ชันคงที่มาจากสมัยก่อนเมื่อกราฟิกการ์ดฮาร์ดโค้ดคณิตศาสตร์ทั้งหมดที่ใช้ในการเรนเดอร์ฉาก 3 มิติและ OpenGL API ถูก จำกัด ให้ตั้งค่าการกำหนดค่าบางอย่างสำหรับคณิตศาสตร์นั้น ทุกวันนี้ฮาร์ดแวร์มีคณิตศาสตร์ที่เขียนโค้ดยากมากและ (เช่นเดียวกับ CPU ของคุณ) รันโปรแกรมที่ผู้ใช้จัดหา (มักเรียกว่า shaders) แทน

อีกครั้งไดรเวอร์จะต้องเลียนแบบ API เก่าเนื่องจากคุณสมบัติการทำงานคงที่เพียงแค่ไม่ได้อยู่ในฮาร์ดแวร์อีกต่อไป ซึ่งหมายความว่าคนขับมีพวงของความเข้ากันได้ที่ฝังอยู่ในนั้นซึ่งรันคณิตศาสตร์เก่าจากวันที่มีฟังก์ชั่นคงที่ซึ่งจะใช้เมื่อคุณไม่ได้จัดหาตัวของคุณเอง ฟังก์ชั่น OpenGL แบบเก่าที่ปรับเปลี่ยนสถานะฟังก์ชั่นคงที่เก่า (เช่น OpenGL lighting API แบบเดิม) กำลังใช้คุณสมบัติ OpenGL ที่ทันสมัยเช่นบัฟเฟอร์แบบเครื่องแบบเพื่อป้อนค่าเหล่านี้ไปยังส่วนรองรับความเข้ากันได้ของไดรเวอร์

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

เอกสารจำนวนมากอาจแนะนำให้คุณเริ่มต้นด้วย API เก่า ๆ เพียงเพราะมันง่ายต่อการเริ่มต้นใช้งาน Direct3D แก้ปัญหานี้สำหรับผู้เริ่มต้นด้วยการเสนอไลบรารี่สหาย ( DirectX Tool Kit ) ที่ให้ API การวาดที่ง่ายขึ้นและการเขียนล่วงหน้าซึ่งสามารถผสมกับการใช้งาน Direct3D 11 แบบดิบๆได้อย่างอิสระตามความเชี่ยวชาญของคุณ ชุมชน OpenGL ที่กว้างขึ้นส่วนใหญ่ติดอยู่กับโปรไฟล์ความเข้ากันได้สำหรับผู้เริ่มต้นโชคไม่ดีซึ่งเป็นปัญหาเนื่องจากมีระบบที่ไม่อนุญาตให้คุณรวม OpenGL APIs เก่ากับใหม่ มีไลบรารีและเครื่องมือที่ไม่เป็นทางการสำหรับการเรนเดอร์ที่ง่ายขึ้นบน OpenGL ใหม่พร้อมด้วยคุณสมบัติและระดับการใช้งานและกรณีการใช้งานเป้าหมายที่แตกต่างกัน ( MonoGame สำหรับผู้ใช้. NET เป็นต้น) แต่ไม่มีสิ่งใดรับรองอย่างเป็นทางการ

เอกสารที่คุณค้นหาอาจไม่ได้สำหรับ OpenGL แต่อาจเป็นหนึ่งใน API ที่คล้ายคลึงกันอื่น ๆ OpenGL | ES 1.x มีการเรนเดอร์ฟังก์ชันคงที่ แต่ไม่มี OpenGL 1.x APIs สำหรับการส่งจุดสุดยอด OpenGL | ES 2.x + และ WebGL 1+ ไม่มีฟังก์ชั่นคงที่เลยและไม่มีโหมดความเข้ากันได้ย้อนหลังสำหรับ API เหล่านั้น

API เหล่านี้ดูคล้ายกับ OpenGL หลักมาก พวกเขาไม่สามารถใช้งานร่วมกันได้ แต่มีส่วนขยายอย่างเป็นทางการสำหรับ OpenGL ที่ไดรเวอร์ (ไม่ใช่ทั้งหมด) สนับสนุนเพื่อให้สามารถใช้งานร่วมกับ OpenGL | ES (ซึ่งอิงตาม WebGL) เพราะสิ่งต่าง ๆ ไม่เคยสับสนมาก่อน


4
+1 คำตอบสุดวิเศษ! หากคุณสามารถพูดถึงคู่ของห้องสมุดอย่างไม่เป็นทางการเหล่านั้นและเครื่องมือสำหรับการแสดงผลที่เรียบง่ายใน OpenGL ใหม่ที่จะดี :)
Mehrdad

2
คำตอบที่ยอดเยี่ยม ฉันมีปัญหาเดียวกันกับ DirectX ย้อนกลับไปในวันนี้ - ง่ายกว่า OpenGL มาก แต่การกระโดดจากโหมดคงไว้ / เร่งด่วนไปสู่เฉดสีนั้นมีขนาดใหญ่มาก โชคดีที่เอกสารช่วยได้มาก (ต่างจาก OpenGL อย่างน้อยสำหรับฉัน) แต่จุดเริ่มต้นของ "ฉันจะเบาได้ยังไง" ก็บ้า: D
Luaan

ฉันเป็นผู้เขียน opengl-tutorial.org และฉันเห็นด้วยกับฌอน API ได้รับการพัฒนาด้วยวิธีนี้เพื่อเหตุผลด้านประสิทธิภาพเป็นหลัก
Calvin1602

ข้อมูลที่ดีมากเกี่ยวกับหัวข้อ ..
reynmar

1
@ Mehrdad: ฉันไม่จำใด ๆ ออกจากด้านบนของหัวของฉัน; มีห้องสมุดอย่าง SDL2 หรือ SFML ที่เพิ่มการเรนเดอร์ 2D แบบง่าย ๆ , ไลบรารีกราฟฉากต่างๆ, MonoGame สำหรับ C # ฯลฯ แต่ฉันไม่ได้ตระหนักถึงสิ่งใดที่เทียบเท่าโดยตรงกับ Direct TK ตอนนี้ที่ฉันคิดเกี่ยวกับมัน จะแก้ไขโพสต์ตั้งแต่พูดว่า "หลายคน" อาจเป็นเรื่องโกหกที่อ้วนมาก :)
Sean Middleditch

9

ความแตกต่างหลักคือวิธีการที่ทันสมัยของกลยุทธ์ โหมดทันทีที่ใช้ในบทช่วยสอนแรก:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

ล้าสมัยและไม่รองรับกับเวอร์ชันที่ใหม่กว่า

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


2

เพียงเพิ่มบริบทเพิ่มเติมให้กับคำตอบที่ยอดเยี่ยมอื่น ๆ

โหมดทันทีตามที่อธิบายไว้ในลิงค์แรกคืออย่างที่คนอื่น ๆ พูดกันว่ารหัสดั้งเดิมจาก OpenGL เวอร์ชันเก่าที่สุด (1.1) มันถูกนำกลับมาใช้เมื่อ GPU มีขนาดเล็กกว่าสามเหลี่ยมแรสเตอร์ไรเซอร์เล็กน้อยและไม่มีแนวคิดเรื่องท่อส่งที่ตั้งโปรแกรมได้ หากคุณดูซอร์สโค้ดสำหรับเกมที่มีการเร่งฮาร์ดแวร์ในช่วงต้นเช่น GLQuake และ Quake 2 คุณจะเห็นโหมดการใช้งานทันที กล่าวอย่างง่ายๆคือ CPU จะส่งคำแนะนำสำหรับจุดสูงสุดทีละจุดไปยัง GPU เพื่อเริ่มวาดรูปสามเหลี่ยมบนหน้าจอ สำหรับเร็กคอร์ดนั้น GL_QUADS มีผลลัพธ์เช่นเดียวกับ GL_TRIANGLES ยกเว้น GPU จะต้องแปลงค่าล่ามเหล่านั้นให้เป็นรูปสามเหลี่ยมในทันที

ทันสมัย ​​(3.2+) OpenGL ใช้แนวทางที่แตกต่าง มันบัฟเฟอร์ข้อมูลจุดสุดยอดลงในหน่วยความจำ GPU เพื่อการเข้าถึงที่รวดเร็วและจากนั้นคุณสามารถส่งคำแนะนำการวาดภาพโดยใช้ glDrawArrays หรือ glDrawElements นอกจากนี้คุณยังมีไปป์ที่ตั้งโปรแกรมได้ (glUseProgram) ซึ่งช่วยให้คุณสามารถปรับแต่งตำแหน่งของ GPU และสีของจุดยอด

มีสาเหตุบางประการที่ทำให้โหมดเลิกใช้ทันทีเหตุผลหลักคือประสิทธิภาพ ดังที่ฌอนกล่าวไว้ในคำตอบของเขาทุกวันนี้ GPU สามารถบีบอัดข้อมูลได้เร็วกว่าที่ซีพียูสามารถอัปโหลดได้ดังนั้นคุณจะต้องทำงานคอขวดในเรื่องประสิทธิภาพของ GPU มีค่าใช้จ่ายเล็ก ๆ น้อย ๆ ที่เกี่ยวข้องกับการโทรไปยัง OpenGL ทุกครั้งที่คุณโทรออกมันเป็น miniscule แต่เมื่อคุณโทรนับหมื่นทุกเฟรมมันจะเริ่มซ้อนกัน เพียงแค่ใส่รูปแบบพื้นผิวโดยใช้โหมดทันทีคุณต้องมีการโทรอย่างน้อย 2 ครั้งต่อยอด (glTexCoord2f และ glVertex3f) ต่อเฟรม ด้วย OpenGL สมัยใหม่คุณใช้การเรียกสองสามครั้งเพื่อเริ่มต้นการบัฟเฟอร์ข้อมูลจากนั้นคุณสามารถวาดรูปแบบทั้งหมดโดยไม่คำนึงถึงจำนวนจุดยอดที่มีโดยใช้เพียงไม่กี่สายในการผูกวัตถุอาร์เรย์จุดสุดยอดเปิดใช้งานตัวชี้คุณลักษณะบางอย่างและ จากนั้นเรียกใช้ครั้งเดียวไปยัง glDrawElements หรือ glDrawArrays

เทคนิคใดที่ถูกต้อง? ขึ้นอยู่กับว่าคุณกำลังพยายามทำอะไร เกม 2D ที่เรียบง่ายซึ่งไม่ต้องใช้เทคนิคหรือโพรเซสซิงโพรเซสซิงใด ๆ จะทำงานได้ดีโดยใช้โหมดทันทีและมันอาจจะง่ายกว่าในการเขียนโค้ด อย่างไรก็ตามเกม 3 มิติที่ทันสมัยกว่าจะต่อสู้ได้จริงและหากคุณวางแผนที่จะเรียนรู้ GLSL (ภาษา shader) คุณก็จะได้เรียนรู้เทคนิคที่ทันสมัย

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