ฉันตัดสินใจที่จะเขียนเล็กน้อยเกี่ยวกับแง่มุมการเขียนโปรแกรมและองค์ประกอบที่พูดคุยกัน บางทีมันจะส่องแสงในบางพื้นที่
การนำเสนอ
สิ่งที่ต้องใช้แม้จะมีภาพเดียวที่คุณโพสต์ในคำถามของคุณวาดบนหน้าจอ?
มีหลายวิธีในการวาดรูปสามเหลี่ยมบนหน้าจอ เพื่อความง่ายสมมติว่าไม่มีการใช้บัฟเฟอร์จุดสุดยอด ( บัฟเฟอร์จุดสุดยอดเป็นพื้นที่ของหน่วยความจำที่คุณเก็บพิกัด) สมมติว่าโปรแกรมบอกขั้นตอนการประมวลผลกราฟิกเกี่ยวกับจุดสุดยอดทุกจุดเดียว (จุดสุดยอดเป็นเพียงพิกัดในพื้นที่) ในแถว
แต่ก่อนที่เราจะวาดอะไรได้เราต้องเริ่มนั่งร้านก่อน เราจะเห็นว่าทำไมในภายหลัง:
// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Drawing Using Triangles
glBegin(GL_TRIANGLES);
// Red
glColor3f(1.0f,0.0f,0.0f);
// Top Of Triangle (Front)
glVertex3f( 0.0f, 1.0f, 0.0f);
// Green
glColor3f(0.0f,1.0f,0.0f);
// Left Of Triangle (Front)
glVertex3f(-1.0f,-1.0f, 1.0f);
// Blue
glColor3f(0.0f,0.0f,1.0f);
// Right Of Triangle (Front)
glVertex3f( 1.0f,-1.0f, 1.0f);
// Done Drawing
glEnd();
แล้วมันทำอะไร?
เมื่อคุณเขียนโปรแกรมที่ต้องการใช้กราฟิกการ์ดโดยปกติคุณจะเลือกอินเทอร์เฟซบางอย่างกับไดรเวอร์ บางอินเตอร์เฟสที่รู้จักกันดีกับไดรเวอร์คือ:
สำหรับตัวอย่างนี้เราจะใช้ OpenGL ตอนนี้อินเทอร์เฟซของคุณกับไดรเวอร์คือสิ่งที่ให้เครื่องมือทั้งหมดที่คุณต้องการเพื่อให้โปรแกรมของคุณพูดคุยกับการ์ดแสดงผล (หรือไดรเวอร์ซึ่งจะพูดกับการ์ด)
อินเตอร์เฟซนี้ถูกผูกไว้เพื่อให้คุณบางเครื่องมือ เครื่องมือเหล่านี้มีรูปแบบของAPIที่คุณสามารถโทรได้จากโปรแกรมของคุณ
API นั้นคือสิ่งที่เราเห็นว่ามีการใช้งานในตัวอย่างด้านบน ลองมาดูอย่างใกล้ชิด
นั่งร้าน
ก่อนที่คุณจะสามารถทำได้จริงๆวาดจริงใด ๆ ที่คุณจะต้องดำเนินการติดตั้ง คุณต้องกำหนดวิวพอร์ตของคุณ (พื้นที่ที่จะแสดงจริง) มุมมองของคุณ ( กล้องเข้าสู่โลกของคุณ) การต่อต้านนามแฝงที่คุณจะใช้ (เพื่อทำให้ขอบสามเหลี่ยมของคุณราบรื่น) ...
แต่เราจะไม่มองสิ่งนั้น เราก็จะใช้เวลามองที่สิ่งที่คุณจะต้องทำทุกเฟรม ชอบ:
การล้างหน้าจอ
ไปป์ไลน์กราฟิกจะไม่ล้างหน้าจอสำหรับคุณทุกเฟรม คุณจะต้องบอกมัน ทำไม? นี่คือเหตุผล:
หากคุณไม่ได้ล้างหน้าจอคุณก็แค่วาดมันทุกเฟรม นั่นเป็นเหตุผลที่เราโทรหาglClear
ด้วยGL_COLOR_BUFFER_BIT
ชุด อีกบิต ( GL_DEPTH_BUFFER_BIT
) บอก OpenGL เพื่อล้างบัฟเฟอร์ความลึก บัฟเฟอร์นี้ใช้เพื่อกำหนดว่าพิกเซลใดที่อยู่ข้างหน้า (หรือหลัง) พิกเซลอื่น ๆ
การแปลง
แหล่งที่มาของภาพ
การแปลงเป็นส่วนที่เรารับพิกัดอินพุตทั้งหมด (จุดยอดของสามเหลี่ยม) และใช้เมทริกซ์ ModelView ของเรา นี่คือเมทริกซ์ที่อธิบายถึงวิธีการที่แบบจำลองของเรา(จุดยอด) หมุน, ปรับขนาดและแปล (ย้าย)
ต่อไปเราใช้เมทริกซ์การฉายของเรา นี่เป็นการย้ายพิกัดทั้งหมดเพื่อให้พวกเขาหันหน้าเข้าหากล้องของเราอย่างถูกต้อง
ตอนนี้เราแปลงอีกครั้งด้วยเมทริกซ์วิวพอร์ตของเรา เราทำเช่นนี้เพื่อปรับขนาดโมเดลของเราตามขนาดของจอภาพ ตอนนี้เรามีจุดยอดที่พร้อมจะแสดง!
เราจะกลับมาเปลี่ยนแปลงอีกในภายหลัง
การวาดภาพ
การวาดรูปสามเหลี่ยมเราก็สามารถบอก OpenGL ที่จะเริ่มต้นใหม่รายชื่อของรูปสามเหลี่ยมโดยการโทรglBegin
ที่มีGL_TRIANGLES
อย่างต่อเนื่อง
นอกจากนี้ยังมีรูปแบบอื่น ๆ ที่คุณสามารถวาด เช่นเดียวกับแถบสามเหลี่ยมหรือแฟนสามเหลี่ยม สิ่งเหล่านี้เป็นการปรับให้เหมาะสมที่สุดเนื่องจากต้องการการสื่อสารที่น้อยกว่าระหว่าง CPU และ GPU เพื่อดึงสามเหลี่ยมจำนวนเท่ากัน
หลังจากนั้นเราสามารถให้รายการชุดของ 3 จุดยอดที่ควรประกอบเป็นสามเหลี่ยมแต่ละอัน สามเหลี่ยมทุกอันใช้ 3 พิกัด (เนื่องจากเราอยู่ในพื้นที่ 3D) นอกจากนี้ผมยังให้สีสำหรับแต่ละจุดสุดยอดโดยการเรียกglColor3f
ก่อนที่จะglVertex3f
เรียก
สีระหว่าง 3 จุด (3 มุมของรูปสามเหลี่ยม) คำนวณโดย OpenGL โดยอัตโนมัติ มันจะทำการแทรกสีให้ทั่วใบหน้าของรูปหลายเหลี่ยม
ปฏิสัมพันธ์
ตอนนี้เมื่อคุณคลิกที่หน้าต่าง แอปพลิเคชันมีเพียงการจับข้อความหน้าต่างที่ส่งสัญญาณการคลิก จากนั้นคุณสามารถเรียกใช้การกระทำใด ๆ ในโปรแกรมที่คุณต้องการ
นี้ได้รับจำนวนมากยากมากขึ้นเมื่อคุณต้องการที่จะเริ่มมีปฏิสัมพันธ์กับฉาก 3 มิติของคุณ
ก่อนอื่นคุณต้องรู้อย่างชัดเจนว่าพิกเซลใดที่ผู้ใช้คลิกหน้าต่าง จากนั้นเมื่อคำนึงถึงมุมมองของคุณแล้วคุณสามารถคำนวณทิศทางของรังสีได้จากจุดคลิกเมาส์ไปยังฉากของคุณ จากนั้นคุณสามารถคำนวณได้ว่าวัตถุใด ๆ ในฉากของคุณตัดกับรังสีนั้น ตอนนี้คุณรู้แล้วว่าผู้ใช้คลิกวัตถุหรือไม่
ดังนั้นคุณจะทำให้มันหมุนได้อย่างไร
การแปลง
ฉันตระหนักถึงการแปลงสองประเภทที่ใช้โดยทั่วไป:
- การแปลงตามเมทริกซ์
- การเปลี่ยนแปลงของกระดูก
แตกต่างก็คือกระดูกส่งผลกระทบต่อเดียวจุด เมทริกซ์จะมีผลต่อจุดยอดที่วาดในลักษณะเดียวกัน ลองดูตัวอย่าง
ตัวอย่าง
ก่อนหน้านี้เราโหลดเมทริกซ์เอกลักษณ์ของเราก่อนที่จะวาดรูปสามเหลี่ยมของเรา เมทริกซ์เอกลักษณ์เป็นสิ่งที่ไม่มีการเปลี่ยนแปลงเลย ดังนั้นสิ่งที่ฉันวาดได้รับผลกระทบจากมุมมองของฉันเท่านั้น ดังนั้นสามเหลี่ยมจะไม่ถูกหมุนเลย
ถ้าฉันต้องการหมุนตอนนี้ฉันสามารถทำคณิตศาสตร์ด้วยตัวเอง (บน CPU) และเพียงแค่เรียกglVertex3f
กับพิกัดอื่น (ที่หมุน) หรือฉันสามารถปล่อยให้ GPU ทำทุกอย่างได้โดยการโทรglRotatef
ก่อนวาดภาพ:
// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);
amount
แน่นอนว่าเป็นเพียงค่าคงที่ หากคุณต้องการเคลื่อนไหวคุณจะต้องติดตามamount
และเพิ่มทุกเฟรม
ดังนั้นรอเกิดอะไรขึ้นกับเมทริกซ์พูดก่อนหน้านี้?
ในตัวอย่างง่ายๆนี้เราไม่ต้องสนใจเมทริกซ์ เราเพียงแค่โทรหาglRotatef
และดูแลทุกอย่างให้เรา
glRotate
ก่อให้เกิดการหมุนของangle
องศารอบเวกเตอร์ xyz เมทริกซ์ปัจจุบัน (ดูglMatrixMode ) ถูกคูณด้วยเมทริกซ์การหมุนด้วยผลิตภัณฑ์ที่แทนที่เมทริกซ์ปัจจุบันราวกับว่าglMultMatrixถูกเรียกด้วยเมทริกซ์ต่อไปนี้เป็นอาร์กิวเมนต์:
x 2 1 - c + cx y 1 - c - z sx z 1 - c + y s 0 y x 1 - c + z sy 2 1 - c + cy z 1 - c - x s 0 x z 1 - c - y sy z 1 - c + x sz 2 1 - c + c 0 0 0 0 1 1
ขอบคุณมากสำหรับสิ่งนั้น!
ข้อสรุป
สิ่งที่ชัดเจนคือมีการพูดคุยกับ OpenGL มากมาย แต่มันไม่ได้บอกอะไรเรา การสื่อสารอยู่ที่ไหน
สิ่งเดียวที่ OpenGL บอกกับเราในตัวอย่างนี้คือเมื่อทำเสร็จแล้ว ทุกการดำเนินการจะใช้เวลาพอสมควร การดำเนินการบางอย่างใช้เวลานานอย่างไม่น่าเชื่อการดำเนินการบางอย่างรวดเร็วอย่างไม่น่าเชื่อ
การส่งจุดสุดยอดไปยัง GPU จะเร็วมากฉันไม่รู้ด้วยซ้ำว่าจะแสดงออกมาอย่างไร การส่งจุดยอดนับพันจากซีพียูไปยัง GPU ในทุก ๆ เฟรมเป็นไปได้มากที่สุดที่จะไม่มีปัญหาเลย
การล้างหน้าจออาจใช้เวลาเป็นมิลลิวินาทีหรือแย่กว่านั้น (โปรดทราบว่าโดยปกติคุณจะมีเวลาประมาณ 16 มิลลิวินาทีในการวาดแต่ละเฟรม) ขึ้นอยู่กับวิวพอร์ตของคุณ เพื่อล้างมัน OpenGL ต้องวาดทุกพิกเซลเดียวในสีที่คุณต้องการล้างซึ่งอาจเป็นล้านพิกเซล
นอกจากนั้นเราสามารถถาม OpenGL เกี่ยวกับความสามารถของกราฟิกการ์ดของเราได้ (ความละเอียดสูงสุดการลบรอยหยักสูงสุดความลึกสีสูงสุด ... )
แต่เราสามารถเติมพื้นผิวด้วยพิกเซลที่แต่ละอันมีสีเฉพาะ แต่ละพิกเซลจึงมีค่าและพื้นผิวเป็น "ไฟล์" ขนาดใหญ่ที่เต็มไปด้วยข้อมูล เราสามารถโหลดสิ่งนั้นลงในการ์ดกราฟิก (โดยการสร้างบัฟเฟอร์พื้นผิว) จากนั้นโหลดshaderบอกให้ shader นั้นใช้พื้นผิวของเราเป็นอินพุตและเรียกใช้การคำนวณที่หนักมากใน "ไฟล์" ของเรา
จากนั้นเราสามารถ "แสดง" ผลลัพธ์ของการคำนวณของเรา (ในรูปแบบของสีใหม่) ลงในพื้นผิวใหม่
นั่นเป็นวิธีที่คุณสามารถทำให้ GPU ใช้งานได้ในวิธีอื่น ฉันถือว่า CUDA ดำเนินการคล้ายกับด้านนั้น แต่ฉันไม่เคยมีโอกาสได้ทำงานกับมัน
พวกเราสัมผัสวัตถุทั้งหมดเพียงเล็กน้อยเท่านั้น การเขียนโปรแกรมกราฟิก 3D เป็นนรกของสัตว์ร้าย
แหล่งที่มาของภาพ