Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> การเขียนโปรแกรม Bash

การรวม OpenGL กับ Bash: Binding ที่อัปเดตและเอกสารประกอบ

ในบทความก่อนหน้าที่อธิบายการออกแบบของ Perl 5 และความเหมาะสมเป็น "glue language" ผมเคยกล่าวว่าผมเคยเขียน OpenGL bindings สำหรับ Bash นี่อาจเป็นข้อความที่น่าอัศจรรย์เกินไปที่จะพูดโดยไม่มีหลักฐาน ดังนั้นผมจึงกลับไปที่มุมฝุ่นบนฮาร์ดดิสก์ของผม ขุดมันออกมา ปรับปรุงให้ทันสมัยเล็กน้อย ปรับปรุงการรองรับฟอนต์ เขียนเอกสาร และเผยแพร่บนไซต์ของผมและ GitHub (คุณจะต้องมีระบบที่มีทั้ง Bash และ OpenGL support เพื่อลองจริง แต่นี่คือวิดีโอ)

तो अब मेरा इकबाल: Perl graphics ใน DeLorean dashboard ที่ผมอธิบายใน My DeLorean runs Perl มีประวัติร่วมกับโปรเจกต์ OpenGL for Bash ของผม ในความเป็นมาที่สนุกและขัดเจน ผมเริ่มโปรเจกต์นี้ 13 ปีก่อน หลังจากสังเกต Frozen Bubble และรู้สึกถูกปגועทางเทคนิคที่ใครสร้าง real-time video game ใน Perl ขึ้นมา ตอนนั้นภาษาหลักของผมคือ C++ และผมกำลังศึกษา OpenGL เพื่อวิดีโอเกม ผมประกาศให้เพื่อนๆ รู้ว่าสิ่งที่แย่กว่านั้นคือถ้ามันเป็น 3D และเขียนใน Bash หลังจากพูดความคิดนั้นออกมาเสียง มันคอยแทงใจผม และผมตัดสินใจลองทำดู เพื่อ "เอาชนะ" ความ "แย่" ของการใช้ Perl สำหรับกราฟิกส์ real-time

การขยาย Bash

วิธีตรงไปตรงมาที่สุดในการเพิ่ม OpenGL support ให้ Bash จะเป็นการแก้ไข source code และเพิ่มแต่ละฟังก์ชัน OpenGL เป็น shell "builtin" แต่คนที่จะได้ลองใช้จะมีแค่คนที่ยินดีติดตั้ง Bash เวอร์ชันกำหนดเอง และน่าจะไม่มีใครอยากทำ นอกจากนี้มันก็ไม่ดูเหมือนจะเป็น "the Bash way"

ผมจึงคิดไอเดีย client-server โดยแต่ละฟังก์ชัน OpenGL จะถูกติดตั้งใน path และเรียก client ที่จะเชื่อมต่อกับ OpenGL server เพื่อส่งข้อความ execute ฟังก์ชันนั้น นั่นจะเป็นโซลูชันที่ "แย่" อย่างสนุกสนาน แต่ไม่ว่าผมจะ optimize อย่างไร มันยังช้เกินไปสำหรับผมที่จะพูดว่าผมประสบความสำเร็จในเป้าหมายโดยรวม

สุดท้าย ผมเลือก design ที่มีกระบวนการ "OpenGL Interpreter" ที่จะอ่านคำสั่ง OpenGL บน standard-in และเขียน user input events ใดๆ ไปยัง standard-out จากนั้นสคริปต์ Bash สามารถเริ่ม interpreter นี้เชื่อมต่อกับ pipes และแต่ละคำสั่ง OpenGL จะเป็นฟังก์ชัน Bash ที่เขียนลง pipe ในขณะนั้น ผมตัดสินใจโยนถุงทรงพลังของฉลาดทั้งหมดเข้าไป และเขียน interpreter ด้วย C แบบธรรมดา พร้อม hash tables และ red-black trees ที่คอมไพล์แบบ static

OpenGL immediate mode

รายละเอียด OpenGL คือจุดที่โปรเจกต์นี้เริ่มทับซ้อนกับโปรเจกต์ DeLorean dashboard ก่อนอื่น ผมจะย้อนกลับมาสรุป API ของ OpenGL

OpenGL คือการเขียนโปรแกรมที่วางแผนกราฟิกส์ในรูปของพิกัด 3D และ textures แล้วส่งข้อมูลนั้นไปยังการ์ดจอเพื่อ render ให้เป็นหน้าจอ 2D อย่างง่ายๆ มีชุดคณิตศาสตร์ที่ใช้บอกรายละเอียดพื้นที่ของหน้าจอที่กำลังถูกทาสี และชุดคณิตศาสตร์สำหรับบอกสีของแต่ละพิกเซลในพื้นที่นั้น

การดำเนินการทั่วไปคือ บอกรายละเอียดสามมุมของสามเหลี่ยมในพื้นที่ 3D virtual ให้ OpenGL คำนวณว่ามันตกบนหน้าจอ 2D ที่ไหน แล้วบอกมันให้ยืดรูปภาพ 2D ไปครอบพื้นที่นั้น อาจรวมกับรูปภาพอีกภาพ หรือปรับแต่งด้วยการคำนวณความสว่าง ลูปหลักของโปรแกรมสร้างหนึ่งเฟรมของวิดีโอโดยการล้าง buffer วาดทุกโปลิгонที่มองเห็นได้ และส่งไปยังหน้าจอ หากคุณดึงทั้งหมดนี้ใน 16 มิลลิวินาทีหรือน้อยกว่า คุณสามารถรักษา 60 frames per second และได้กราฟิกส์ที่ลื่นไหล

ผมไม่สามารถพูดด้วยอำนาจเกี่ยวกับที่มาของ OpenGL API แต่ดูชัดเจนว่ามันสร้างรอบความคิดของ streaming อาจเพื่อความเข้ากันได้กับ X11 display protocol แต่ก็เพราะมันเป็นไอเดียที่ดี ดังนั้น ฟังก์ชัน OpenGL ส่วนใหญ่ไม่มีค่าส่งคืน (return value) ตรงข้ามกับ API อื่นๆ ที่แต่ละ function call ส่งคืนสถานะบอกผลลัพธ์ของการดำเนินการ หากคุณจินตนาการฟังก์ชัน OpenGL เช่น glVertex3f(1,2,3) เป็นคำสั่ง print ที่เขียนตัวเลขสามตัวผ่าน pipe คุณก็ได้ไอเดียค่อนข้างดีว่ามันทำงานด้านหลังอย่างไร จริงๆ แล้วสำหรับ Bash bindings ผมเขียน glVertex 1 2 3 ผ่าน pipe ตอนที่ glVertex 1 2 3 ถูก execute สคริปต์ Bash รันแบบ blind โดยไม่มีความรู้ว่าคำสั่งกราฟิกส์ของมันทำงานตามที่ตั้งใจหรือไม่

มีการปรับเปลี่ยน API OpenGL ใหญ่หลายครั้งตลอดปี และคนเขียนบททั้งหมดในหนังสือเกี่ยวกับ "retained" vs. "immediate" modes แต่ทั้งหมดลดทอนลงมาสองแนวคิด:

  • ช้าเกินไปที่จะส่งข้อมูลทั้งหมดผ่าน pipe ทุกเฟรม มา cache บางส่วนไว้ที่อีกปลาย
  • เราไม่มีทางตอบสนองความต้องการทุกคนด้วยคณิตศาสตร์ built-in มาให้ภาษาเพื่ออธิบาย custom math ของตัวเอง

ด้วยที่กล่าวมา แม้ caching และ custom math ที่เสนอโดย OpenGL APIs ใหม่จะมีประสิทธิภาพมากกว่าและจำเป็นสำหรับเกมระดับท็อป แต่ก็ต้องใช้ความพยายามมากกว่าในการ setup และทำงาน (และเรียนรู้) และอาจทำ害มากกว่าประโยชน์ต่อ hobbyists พวกมันก็ไม่จำเป็นหากคุณไม่ทำ advanced graphics effects หรือ high-detail models

ดังนั้น แม้ Bash OpenGL bindings จะจัดการแค่ API ที่ "deprecated" แล้ว ผมยังคงสนับสนุนให้คนดูเพื่อการศึกษาและการเล่นทดลอง

Display lists

โชคดีที่ API OpenGL ต้นฉบับมีกลไก caching บางอย่าง และใช้ง่าย พวกมันทำงานประมาณลำดับ:

"Hey OpenGL, I want you to create object 37 on the remote end"

"Here's some data describing object 37"

"Use object 37 in the next rendering steps"

เพื่อทำให้ง่ายขึ้น Bash bindings ให้คุณใช้ชื่อแทนตัวเลข

ประเภท object หลักแรกคือ texture โดยคุณโหลดรูปภาพ 2D เข้าไปในการ์ดจอ และจากนั้น "ทาสี" โปลิ곤ด้วยมัน ประเภทที่สองคือ "display list" โดยคุณบันทึกลำดับคำสั่ง OpenGL แล้วเล่นกลับเหมือนเป็นคำสั่ง OpenGL เดียว

Display lists ทำงานได้ดีสำหรับ prototyping แบบ ad-hoc คุณสามารถวาดจุดบางส่วนอธิบายโมเดล 3D (หรือ 2D) ของคุณโดยใช้คำสั่ง vertex เรียบง่าย แล้วบันทึกลำดับจุดนั้นเป็น display list และตอนนี้คุณสามารถ render โมเดลนั้นด้วยคำสั่งเดียว

สำหรับตัวอย่างของพลังนี้ ดู Robot.sh ในไดเรกทอรี Examples มันสร้าง display list หนึ่งสำหรับแต่ละส่วนของร่างกายหุ่นยนต์ตอน startup และขณะรัน มันเพียง emit 58 บรรทัดของข้อความต่อเฟรมเพื่อ render หุ่นยนต์ Bash สามารถ generate 58 บรรทัดข้อความใน 16 มิลลิวินาทีได้ง่าย (แม้ 12 ปีก่อนก็ทำได้) ดังนั้น demo จึงสามารถรันที่ความเร็วเต็มบนฮาร์ดแวร์ทั่วไป

Display lists เป็นทริกหลักที่ผมนำไปใช้กับ Perl graphics ที่ผมใช้ในซอฟต์แวร์ DeLorean dashboard ฟังก์ชัน Perl calls ค่อนข้าง expensive เทียบกับ C โดยเฉพาะสำหรับ API ที่หนาแน่นด้วยฟังก์ชันอย่าง OpenGL 1.4 แต่โดยการรวมสิ่งต่างๆ เข้า display lists Perl ไม่มีงานมากต่อ video frame หากผมไม่ได้เรียนรู้ทริกนี้สำหรับโปรเจกต์ Bash โปรเจกต์ Perl จะไม่ประสบความสำเร็จเท่านี้

ทำไมผมไม่เผยแพร่เรื่องนี้ 12 ปีก่อน?

แม้ผมจะสร้าง animation demos ให้ดู fancy ใน Bash ได้ แต่เป้าหมายจริงของผมคือสร้างเกมครบถ้วน ผมมุ่งเป้า clone เกม flight-sim เก่า Terminal Velocity และผมไปได้ถึง spaceship ที่บินผ่าน field ของลูกบาศก์ (ดู Flight.sh ใน Examples) แต่ปัญหาถัดไปคือ collision detection primitives ที่มีใน Bash คือ "associative array" ทั่วไปของชื่อตัวแปรและตัวแปรอาร์เรย์ (แม้ version 4 ตอนนี้มี associate-array variables) และคณิตศาสตร์ทั้งหมดเป็น integers แม้ผมจะทำ 3D matrix rotations ใน fixed-point integer math ได้ แต่การ implement collision detection ดูเหมือนอุปสรรคใหญ่เกินไป ผมยังไม่พอใจกับ font API ของผมด้วย ขณะคิดหาวิธีแก้ปัญหาเหล่านี้ โปรเจกต์ค่อยๆ ตกออกจากรายการ

นี่คือจุดที่ผมสรุป (ตามที่เขียนไว้) "Bash is a horrible glue language" ในขณะที่โปรเจกต์นี้เริ่มและจบเป็นเรื่องตลก (หรืออาจเป็น educational tool) สไตล์โปรแกรมเดียวกันใน Perl กลับกลายเป็นประโยชน์มากสำหรับแอปพลิเคชันจริง หากผมให้ Frozen Bubble ความพิจารณาจริงจัง 13 ปีก่อน ผมอาจได้เปรียบก่อนใน Perl ภาษาปัจจุบันที่ผมชอบ

ผมไม่มีแผนปรับปรุงโปรเจกต์นี้นอกจาก bug fixes แต่ผมคิดว่ามันควรมีประโยชน์ต่อคนที่อยากเรียนรู้ concept ของ OpenGL หรือคนที่มีเวลาว่างเยอะและมีความปรารถนาอยากเขียน Doom clone ใน M4

Enjoy! และสนุกกับการ forward ลิงก์นี้ ไม่งั้นไม่มีใครจะเชื่อคุณ