2 คะแนน โดย GN⁺ 2 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เพื่อทำความเข้าใจหลักการทำงานภายในของ OpenGL·Vulkan·Metal·DirectX จึงสร้าง ซอฟต์แวร์เรนเดอเรอร์ ขึ้นมาเองตั้งแต่ต้นโดยไม่ใช้ไลบรารีกราฟิกภายนอก
  • แปลง โมเดล 3D เป็นภาพ ที่ประกอบด้วยเมชสามเหลี่ยมและเท็กซ์เจอร์ โดยไม่ครอบคลุมการทำ GUI หรือแอปพลิเคชัน GPU
  • โค้ดที่เสร็จสมบูรณ์มีประมาณ 500 บรรทัด และโดยทั่วไปนักเรียนจะเริ่มสร้างเรนเดอเรอร์ที่ใช้งานได้ภายใน 10~20 ชั่วโมง
  • มีให้เพียง คลาสจัดการ TGA ที่รองรับ RGB·RGBA·grayscale และฟังก์ชันตั้งค่าพิกเซลเดี่ยว โดยการวาดเส้นและสามเหลี่ยมต้องลงมือเขียนเอง
  • การเขียนด้วยตัวเองแทนการคัดลอกโค้ดที่เสร็จแล้วจะช่วยให้เข้าใจแนวคิดของการเรนเดอร์และการทำงานภายในของไลบรารี 3D

กระบวนการสร้างเรนเดอริงไปป์ไลน์ด้วยตัวเอง

  • เรียนรู้หลักการทำงานของ เรนเดอริงไปป์ไลน์ โดยอิงโครงสร้างของไลบรารีกราฟิก 3D สมัยใหม่แบบหลวม ๆ
    • แทนที่จะสอนวิธีเขียนแอปพลิเคชัน GPU จะจำลองการทำงานภายในด้วยซอฟต์แวร์เรนเดอเรอร์
    • อินพุตคือโมเดล 3D ที่ประกอบด้วยเมชสามเหลี่ยมและเท็กซ์เจอร์ และเอาต์พุตคือภาพที่เรนเดอร์แล้ว
    • โปรแกรมจะสร้างไฟล์ภาพโดยไม่ต้องมีกราฟิกอินเทอร์เฟซ
  • เพื่อให้มี dependency ภายนอกน้อยที่สุด จึงใช้ TGA ซึ่งเป็นฟอร์แมตภาพอย่างง่าย
    • ฟังก์ชันที่ให้มาตั้งต้นมีเพียงการโหลดภาพ·บันทึกภาพ และ ตั้งค่าสีของพิกเซลหนึ่งจุด
    • ไม่มีฟังก์ชันในตัวสำหรับวาดเส้นหรือสามเหลี่ยม ดังนั้นต้องเขียนเองทั้งหมด
  • ตัวอย่างเริ่มต้นจะสร้างเฟรมบัฟเฟอร์ RGB 64x64 ตั้งค่าพิกเซลตามพิกัดสามจุดให้เป็นสีขาว แล้วบันทึกเป็น framebuffer.tga
    • ค่าของสีระบุในลำดับ BGRA

การบิลด์และรันโค้ด

git clone https://github.com/ssloy/tinyrenderer.git &&
cd tinyrenderer &&
cmake -Bbuild &&
cmake --build build -j &&
build/tinyrenderer obj/diablo3_pose/diablo3_pose.obj obj/floor.obj
  • ผลลัพธ์จากการรันจะถูกบันทึกไว้ใน framebuffer.tga
  • แม้โค้ดที่เสร็จสมบูรณ์จะมีราว 500 บรรทัด แต่กระบวนการลงมือทำเองเป็นสิ่งสำคัญต่อความเข้าใจเชิงแนวคิด จึงไม่แนะนำให้ใช้โค้ดที่มีให้แบบตรง ๆ

1 ความคิดเห็น

 
GN⁺ 2 시간 전
ความเห็นจาก Hacker News
  • เมื่อไม่กี่เดือนก่อนเคยลงมือทำ ซอฟต์แวร์เรนเดอเรอร์ ด้วย Rust เองโดยไม่ใช้ LLM และยังเพิ่มเกมง่าย ๆ พร้อมพิกเซลเชดเดอร์กับเอฟเฟ็กต์ความคลาดสีที่ขอบลำแสงไฟฉายด้วย
    https://github.com/kshitijl/tinyrenderer-rs
    ในรีโพมีสกรีนช็อตจำนวนมากที่บันทึกกระบวนการพัฒนาและบั๊กภาพตลก ๆ เอาไว้ นอกจากหลักการเรนเดอร์แล้ว ยังได้เรียนรู้อย่างมากด้วยว่า CPU สมัยใหม่เร็วมาก และแม้เป็น CPU renderer แบบเธรดเดียว ก็ยังรันเกม 3D แบบโต้ตอบที่มีเอฟเฟ็กต์พิเศษอลังการได้
    • สงสัยว่าทำไมเป็นซอฟต์แวร์เรนเดอเรอร์แต่ยังพึ่งพา wgpu
    • สงสัยว่าตอนเขียนเกมลอจิกด้วย Rust จำเป็นถึงขั้นต้องใส่ ECS เพิ่มด้วยหรือไม่
  • เอกสารนี้กับ Mathematics for Computer Graphics ของ John Vince เป็นของสำคัญมากในการทำ ซอฟต์แวร์เรนเดอเรอร์ ของฉัน
    เป็นช่วงก่อนมี LLM เลยใช้เวลาอย่างน้อยสองเดือน และเวลาส่วนใหญ่หมดไปกับการทำความเข้าใจคณิตศาสตร์คอมพิวเตอร์กราฟิกส์และตามแก้ segmentation fault ของ C
    • สงสัยว่าช่วงหลายเดือนนั้นทำวันละกี่ชั่วโมง
  • สงสัยว่าหนังสือของ Foley กับ Van Dam ยังเป็นหนังสืออ้างอิงหลักของสายนี้อยู่ไหม แม้จะมีฉบับปรับปรุงในปี 2013 แต่ฉันคุ้นกับ ฉบับปี 1982 ที่เน้น 2D มากกว่า และตอนนั้นมันแทบเป็นหนังสือมาตรฐานของคอมพิวเตอร์กราฟิกส์เลย
    • ไม่ได้หยิบกลับมาอ่านอีกนานแล้ว และสำหรับฉันมันใกล้เคียง สารานุกรม แปลก ๆ ที่มีคุณค่าทางประวัติศาสตร์มากกว่า
      โน้ตการสอนใน GitHub นี้เหมาะกับการทบทวนแนวคิดมากกว่า แม้จะไม่ชอบสไตล์โค้ดในรีโพ และแรสเตอร์ไรเซอร์แบบเก่าก็เรียบง่ายกับไม่มีประสิทธิภาพเกินไป แต่ก็ยังอ่านง่ายกว่าหนังสือของ Foley
    • ฉันเองก็เรียนจากฉบับพิมพ์ครั้งที่ 2 และมีฉบับล่าสุดปี 2013 ด้วย ซึ่งก็ถือว่าใช้ได้
      แต่ละฉบับพัฒนาภาษาที่ใช้จาก Pascal ไปเป็น C แล้วก็ C กับ C++ และฉบับล่าสุดยังมี C# เล็กน้อย แม้แนวคิดใหม่หลายอย่างจะหายไป แต่ก็ยังมี เนื้อหาที่มีคุณค่า อยู่อีกมาก
  • อยากให้มีสักบทเรียนของซอฟต์แวร์เรนเดอเรอร์ที่อธิบาย การ clipping สามเหลี่ยม อย่างจริงจัง ถ้าเป็นเรนเดอเรอร์ที่ใช้งานได้จริงก็ต้องจัดการกรณีที่ geometry ตัดกับ view frustum แม้ในฉากพื้นฐานอยู่แล้ว แต่สำหรับฉันนี่เป็นส่วนที่ยากที่สุด
    • หัวข้อนี้ถูกอธิบายเต็มหนึ่งบท: https://gabrielgambetta.com/computer-graphics-from-scratch/11-clipping.html
    • การ clipping สามเหลี่ยมจำเป็นเฉพาะตอนที่การแทรกค่าคุณสมบัติของสามเหลี่ยมขนาดใหญ่มากมีความสำคัญ โดยมีสองวิธีคือทิ้งทันทีกับ ประกอบ primitive
      การ clipping กับ frustum จัดการได้ด้วยการเลือกจุดของ local tile ส่วนการประกอบ primitive จะง่ายกว่าถ้าจัดการสี่เหลี่ยม clipping ที่แปลงกลับแล้วในระบบพิกัด barycentric สามารถควบคุมความคลาดเคลื่อนจากการปัดเศษได้ด้วยเลขทศนิยมความแม่นยำคู่หรือ fixed-point และจุดยากหลักคือการสร้างค่า Z และ 1/Z ของจุดยอดใหม่ขึ้นมาอีกครั้ง ถ้าเป็นแรสเตอร์ไรเซอร์แบบ lazy attribute composition ที่เหลือก็จะไหลผ่าน pipeline ไปเองตามธรรมชาติ และดูตัวอย่างได้จาก implementation โอเพนซอร์สของ OpenSWR.org
    • ฉันเองก็ติดอยู่ที่ขั้น “ต้องทำ clipping แล้ว” อยู่เสมอ แต่สุดท้ายก็เขียนโค้ดที่ใช้งานได้โดยไม่ลำบากมาก และมารู้ทีหลังว่านั่นคือการค้นพบ อัลกอริทึม Sutherland–Hodgman ซ้ำขึ้นมาเอง
      กำแพงทางใจที่ใหญ่ที่สุดคือความไม่คุ้นกับปริภูมิฉายภาพและพิกัด homogeneous ระนาบทั้งหกของ clip space นั้นเรียบง่ายแค่ x = ±w, y = ±w, z = ±w เพียงวนไปตามแต่ละขอบของรูปหลายเหลี่ยม ตัดสินว่าจุดปลายทั้งสองอยู่ด้านในหรือด้านนอก แล้วแทรกเชิงเส้นตำแหน่งจุดตัดขอบเขตและคุณสมบัติของจุดยอด กระบวนการนี้เมื่อนำไปใช้กับทุกระนาบตามลำดับ สามเหลี่ยมจะกลายเป็นรูปหลายเหลี่ยมนูนที่มีจุดยอดได้สูงสุด 9 จุด และสามารถแบ่งเป็นสามเหลี่ยมใหม่ได้ง่าย ถ้าคำนวณ outcode ไว้ล่วงหน้า ก็ข้ามการ clipping ของสามเหลี่ยมที่อยู่ข้างในหรือข้างนอกทั้งหมดได้
      บทความ: https://dl.acm.org/doi/10.1145/360767.360802
  • ในสภาพแวดล้อมสมัยใหม่ จริง ๆ แล้วแทบไม่มี C++ ล้วน อยู่เลย คุณไม่สามารถเขียนตรงไปที่รีจิสเตอร์กับ VRAM เพื่อแสดงผลเหมือนคอมพิวเตอร์ยุค 1980 ได้ และสุดท้ายก็ต้องพึ่งโค้ดจำนวนมหาศาลบนชั้นของ API, ไดรเวอร์ และเฟิร์มแวร์ที่หนามาก
  • กำลังลองกลับไปทำซอฟต์แวร์เรนเดอริงอีกครั้งด้วยความคิดถึงยุค 1990 โดยผสม CLUT bank แบบ 2D เข้ากับเทคนิคสามเหลี่ยมแบบแบ่งช่วงและพิกัด barycentric สมัยใหม่
    ถ้ายังคงรูปแบบ fixed pipeline ไว้ ก็ใช้ฟังก์ชันวาดที่ค่อนข้างเรียบง่ายแต่จัดการสามเหลี่ยมได้มากอย่างน่าประหลาด
  • พบ บั๊กในการจัดการ OpenMP บน macOS และได้ส่ง PR แรกให้รีโพนี้เป็นครั้งแรกในรอบนานมาก