11 คะแนน โดย GN⁺ 2025-11-24 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • สรุปประสบการณ์การลงมือสร้าง เอนจินเกมขนาดเล็กพร้อมเดโมเกม 2 เกม ด้วยตัวเอง ระหว่างเรียนรู้ Vulkan เป็นเวลา 3 เดือน
  • ค่อย ๆ เอาชนะความซับซ้อนของ Vulkan แบบเป็นขั้นเป็นตอน ต่อยอดจากประสบการณ์เดิมกับ OpenGL พร้อมทำฟีเจอร์หลักอย่างการโหลด glTF, skinning และ shadow mapping
  • เอนจินชื่อ EDBR (Elias Daler’s Bikeshed Engine) มีโค้ดราว 19,000 บรรทัด และใช้เทคนิคกราฟิกสมัยใหม่ เช่น bindless descriptor, PVP, BDA
  • ในบทความยังแชร์รายละเอียดเชิงปฏิบัติของการพัฒนา เช่นไลบรารีจำเป็นอย่าง vk-bootstrap, VMA, volk รวมถึง pipeline pattern, การทำ shader build อัตโนมัติ, การจัดการ synchronization
  • การย้ายมาใช้ Vulkan ทำให้ได้ การตัด global state ออก, การควบคุมแบบ explicit, สภาพแวดล้อมสำหรับดีบักที่ดีขึ้น, ความสม่ำเสมอข้าม GPU และมีแผนเพิ่ม render graph, ฟอนต์ SDF, volumetric effects ต่อไป

ภาพรวมการเรียนรู้ Vulkan และการพัฒนาเอนจิน

  • ผู้เขียนเริ่มเรียนกราฟิกส์โปรแกรมมิงด้วยตัวเอง และเคยเขียนเอนจิน 3D ด้วย OpenGL มาก่อนเมื่อราว 1 ปีครึ่งก่อนหน้านี้
  • เอนจินที่ใช้ Vulkan ตัวนี้เหมาะกับ เกมขนาดเล็กแบบเป็นด่าน โดยเน้นเป้าหมายด้านการเรียนรู้และการทดลอง มากกว่าประสิทธิภาพ
  • ในช่วงแรกใช้แนวทางทำเกม 3D ง่าย ๆ ก่อน แล้วค่อยแยกส่วนที่นำกลับมาใช้ซ้ำได้ออกมาให้กลายเป็นเอนจิน
  • เหตุผลที่ทำเสร็จได้ในเวลา 3 เดือน เป็นเพราะจำกัดขอบเขตให้เป็น เอนจินเฉพาะจุดประสงค์ ไม่ใช่เอนจินสารพัดประโยชน์

เส้นทางการเรียนรู้กราฟิกส์โปรแกรมมิง

  • สำหรับผู้เริ่มต้น แนะนำให้เริ่มจาก OpenGL เพื่อเรียนรู้การแสดงโมเดลพร้อมเท็กซ์เจอร์, แสงแบบ Blinn-Phong, shadow mapping เป็นต้น
  • แหล่งเรียนรู้ที่แนะนำ ได้แก่ learnopengl.com, Anton’s OpenGL 4 Tutorials, Thorsten Thormählen lectures
  • ผู้เขียนยังเน้นความสำคัญของการเข้าใจ พีชคณิตเชิงเส้น (เวกเตอร์, เมทริกซ์, ควอเทอร์เนียน) ควบคู่ไปกับสื่อฝึกปฏิบัติ OpenGL 4.6 สมัยใหม่

คำแนะนำเพื่อหลีกเลี่ยง bike-shedding

  • หลีกเลี่ยง การออกแบบและ abstraction ที่มากเกินจำเป็น และยึดหลัก “ทำเฉพาะสิ่งที่จำเป็นตอนนี้”
  • แนะนำแนวทาง “ทำให้มันใช้งานได้ก่อน แล้วค่อยปรับปรุงทีหลัง”
  • การทำ เกมเล็ก ๆ ให้เสร็จก่อน มีประสิทธิภาพกว่าการมุ่งไปที่ เอนจินอเนกประสงค์
  • อย่าลอกเลียนโค้ดหรือโครงสร้างซับซ้อนของคนอื่นตรง ๆ แต่ควรเริ่มจากโครงสร้างเรียบง่าย

เหตุผลที่เลือก Vulkan

  • เกม AAA มักใช้ DirectX, บน macOS/iOS ใช้ Metal และบนเว็บใช้ WebGPU/WebGL เป็นหลัก
  • ผู้เขียนเลือก Vulkan เพราะ ชอบโอเพนซอร์สและเทคโนโลยีมาตรฐาน และเหมาะกับเป้าหมายการทำเกม 3D เดสก์ท็อปขนาดเล็ก
  • OpenGL ไม่ได้พัฒนาต่อแล้ว และถูกยกเลิกการรองรับบน macOS
  • WebGPU แม้จะกระชับกว่า แต่ยังมีข้อจำกัด เช่น ยังไม่เสถียรพอ, ฟีเจอร์จำกัด, ไม่รองรับ bindless และ push constants

กระบวนการเรียนรู้ Vulkan

  • ในช่วงแรกมันยากจนให้ความรู้สึกเหมือน “กำลังเขียนกราฟิกไดรเวอร์เอง”
    แต่การมาของ dynamic rendering, vk-bootstrap, vkguide ทำให้เข้าถึงได้ง่ายขึ้น
  • แหล่งเรียนรู้หลัก:
    • vkguide.dev (ปูพื้นฐานและเน้นลงมือทำ)
    • TU Wien Vulkan Lecture Series
    • 3D Graphics Rendering Cookbook, Mastering Graphics Programming with Vulkan
  • ภายในเดือนแรกก็ทำการโหลด glTF, compute skinning, frustum culling และ shadow mapping ได้สำเร็จ

โครงสร้างเอนจิน EDBR และการประมวลผลเฟรม

  • โค้ดเอนจินมีประมาณ 19,000 บรรทัด, เกม 3D มี 4,600 บรรทัด, เกมแพลตฟอร์ม 2D มี 1,200 บรรทัด
  • ลำดับขั้นตอนเรนเดอร์หลัก:
    • Compute skinningCascaded Shadow Mapping (4096×4096)geometry shading แบบ PBR
    • Depth ResolvePost FX (หมอกตามความลึก)UI rendering
  • ระบบกราฟิกทั้งหมดถูก เขียนใหม่สำหรับ Vulkan โดยเฉพาะ โดยไม่ผสมกับโค้ด OpenGL เดิม

เคล็ดลับเชิงปฏิบัติในการพัฒนา Vulkan

ไลบรารีที่แนะนำ

  • vk-bootstrap: ทำให้การตั้งค่าเริ่มต้นและ swapchain ง่ายขึ้น
  • Vulkan Memory Allocator (VMA): ช่วยจัดการหน่วยความจำแบบอัตโนมัติ
  • volk: ทำให้การโหลดฟังก์ชันส่วนขยายง่ายขึ้น

abstraction ของ GfxDevice

  • จัดการ VkDevice, VkQueue, VmaAllocator และองค์ประกอบอื่น ๆ ผ่านออบเจ็กต์เดียว
  • รับผิดชอบการเริ่ม/จบเฟรม, การสร้าง image และ buffer, และการจัดการ bindless descriptor

การจัดการเชดเดอร์

  • ใช้ GLSL และ precompile เป็น SPIR-V ด้วย glslc ตอน build
  • ใช้ CMake DEPFILE เพื่อ rebuild อัตโนมัติเมื่อ shader มีการเปลี่ยนแปลง

pipeline pattern

  • แยกแต่ละขั้นของการเรนเดอร์เป็น pipeline ระดับคลาส (init, cleanup, draw)
  • ใช้ VK_KHR_dynamic_rendering เพื่อ ไม่ต้องมี render pass และ subpass ทำให้โครงสร้างเรียบง่าย

Programmable Vertex Pulling + Buffer Device Address

  • ใช้ โครงสร้าง Vertex แบบเดียว กับทุกเมช
  • ให้ shader เข้าถึงโดยตรงผ่าน buffer_reference จึงไม่ต้องใช้ VAO

Bindless Descriptor

  • ใช้อาร์เรย์เท็กซ์เจอร์แบบ global (textures[], samplers[]) เพื่อ sample จาก texture ID
  • เก็บ texture ID ไว้ในโครงสร้าง material แล้วส่งผ่าน push constants

การอัปโหลดข้อมูลแบบไดนามิก

  • ส่งข้อมูลด้วยการสลับ GPU buffer รายเฟรม หรือใช้ CPU staging buffer
  • ใช้คลาส NBuffer เพื่อจัดการโครงสร้าง frame-in-flight

การเคลียร์รีซอร์สและ synchronization

  • ใช้ ฟังก์ชัน cleanup แบบ explicit และจัดการด้วยมือ แทนการพึ่งการเคลียร์อัตโนมัติใน destructor
  • ใช้ vkCmdPipelineBarrier2 เพื่อทำ memory synchronization ระหว่างแต่ละ pass
  • มีแผนจะทำ Render Graph ในอนาคต

ตัวอย่างรายละเอียดการทำจริง

การเรนเดอร์สไปรต์

  • ใช้ bindless texture และ instancing เพื่อเรนเดอร์สไปรต์หลายพันตัวในครั้งเดียว
  • อัปโหลดโครงสร้าง SpriteDrawCommand ไปยัง GPU buffer แล้วเรียก vkCmdDraw(6, N)
  • เรนเดอร์สไปรต์ 10,000 ตัวได้ในเวลา 315μs

Compute skinning

  • ใช้ compute shader ทำ vertex deformation ตาม bone matrix และน้ำหนัก
  • สร้าง output buffer แยกสำหรับแต่ละอินสแตนซ์ แล้วนำไปใช้ต่อในขั้นเรนเดอร์แบบเดียวกัน

การแยกเกมกับเรนเดอเรอร์

  • ลอจิกเกมใช้ entt ECS ส่วนเรนเดอเรอร์ประมวลผลเพียง เวกเตอร์ของ DrawCommand
  • สร้างคำสั่งเรนเดอร์ผ่านการเรียก drawMesh, drawSkinnedMesh

การโหลดซีนและ prefab

  • จัดเลย์เอาต์ด่านใน Blender แล้วส่งออกเป็น glTF และใช้กติกาการตั้งชื่อโหนดเพื่อ spawn prefab อัตโนมัติ
  • prefab นิยามด้วย JSON และอ้างอิง glTF ภายนอก

MSAA, UI, ImGui

  • ใช้ MSAA x8 บนพื้นฐาน forward rendering
  • สร้างระบบ auto layout ที่ได้แรงบันดาลใจจาก Roblox UI API
  • เขียน Vulkan backend เองเพื่อแก้ ปัญหา sRGB ของ Dear ImGui

องค์ประกอบอื่น ๆ

  • ใช้ Jolt Physics สำหรับฟิสิกส์, entt สำหรับ ECS, OpenAL-soft สำหรับเสียง และ Tracy สำหรับโปรไฟล์

ข้อดีของการย้ายมาใช้ Vulkan

  • การ ตัด global state ออก ทำให้ได้โครงสร้างโค้ดที่ explicit และเป็นโมดูลมากขึ้น
  • validation layers และการดีบักด้วย RenderDoc ช่วยให้ตามหาปัญหาได้ง่าย
  • ได้ ความสม่ำเสมอระหว่าง GPU และ OS ที่ดีขึ้น และพฤติกรรมคาดเดาได้มากกว่า OpenGL
  • เปิดทางให้ขยายต่อด้วย ภาษา shading ใหม่ ๆ (slang, shady)
  • มี abstraction น้อยลงและควบคุม pipeline ได้ชัดเจน จึงดูแลง่ายขึ้น

แผนในอนาคต

  • มีแผนเพิ่ม ฟอนต์ SDF, การโหลดภาพแบบขนานและการสร้าง mipmap, Bloom, volumetric fog, animation blending, render graph, AO
  • แม้การเรียน Vulkan จะยาก แต่ก็ช่วยอย่างมากในการ เข้าใจ modern graphics API และพัฒนาทักษะการออกแบบเอนจิน

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

 
GN⁺ 2025-11-24
ความคิดเห็นจาก Hacker News
  • แม้จะผ่านไปแล้วตั้งแต่ โพสต์ของฉัน เมื่อ 1 ปีก่อน ความคิดของฉันเกี่ยวกับ Vulkan ก็แทบไม่เปลี่ยน
    มันอาจน่าสนใจสำหรับคนที่ต้องการควบคุมกราฟิกระดับล่าง แต่สำหรับฉันมันเป็น API ที่ใช้งานทรมานมาก
    ฉันยังอยากลองสร้างเกมเอนจินด้วยตัวเองอยู่เหมือนเดิม แต่แม้แต่การตั้งค่าเริ่มต้นของ Vulkan ก็ยังน่ากลัวสำหรับฉัน
    สิ่งที่ฉันต้องการคืออะไรสักอย่างที่เหมือน SDL เวอร์ชัน 3D ในแบบที่ SDL จัดการกราฟิก 2D
    ถ้าจะทำ 3D บน SDL ก็สุดท้ายต้องลงไปใช้ OpenGL อยู่ดี ซึ่งไม่ใช่ระดับที่ฉันต้องการ
    บางที WebGPU อาจเป็นทางเลือกที่ฉันสนุกกับมันได้

    • SDL 3.0 เพิ่ม GPU API เข้ามาเมื่อราว ๆ ปีก่อน เป็น ชั้นนามธรรม ที่ครอบหลายแบ็กเอนด์อย่าง Vulkan เอาไว้ จึงน่าลองดู
      ฉันเองก็เคยสร้างเอนจินตัวหนึ่งด้วยมัน แต่สุดท้ายก็กลับไปใช้เอนจินที่อิง Vulkan เพราะต้องการการควบคุมและประสิทธิภาพมากกว่า
      ถึงอย่างนั้น ฉันก็ได้เรียนรู้ แพตเทิร์นการซิงโครไนซ์ จากโค้ด SDL GPU และมันช่วยกับเอนจิน Vulkan ของฉันมาก
    • wgpu ของ Rust เป็น จุดกึ่งกลาง ที่ให้ระดับนามธรรมแบบ WebGPU
      มันทรงพลังกว่า OpenGL แต่ไม่ต้องลงมือจัดการรายละเอียดอย่าง resource barrier หรือ layout transition เอง
      มันช่วยทำ bookkeeping บางส่วนให้ตอนรันไทม์ และมีข้อจำกัดอย่างรองรับคิวเดียวเท่านั้น
      Vulkan นั้นยาก แต่ถ้าใช้ส่วนขยายที่ผู้ผลิตหลักรองรับก็จะดีขึ้นพอสมควร
      อย่างไรก็ดี มันก็ยังมี ความซับซ้อนที่ไม่จำเป็น อยู่ เช่น บังคับให้ต้องตั้งค่ารายละเอียดที่ไดรเวอร์มักเมินอยู่ดี
    • ในฐานะโปรแกรมเมอร์ OpenGL รุ่นเก่า ฉันเห็นด้วยอย่างยิ่ง
      ตอนนี้ API ระดับกลาง ระหว่างเกมเอนจินระดับสูงกับ Vulkan/Metal ระดับล่างแทบหายไปแล้ว
      ถ้ามือใหม่จะเริ่มเรียนกราฟิก 3D ก็ควรมี API ระดับง่ายแบบ “วาดสามเหลี่ยม” ที่ไม่ต้องรู้เรื่อง shader หรือ buffer ก่อน
      การควบคุมจุกจิกแบบ Vulkan มีความจำเป็นกับนักพัฒนาเอนจินเพียงส่วนน้อยมาก และสำหรับคนส่วนใหญ่ระดับ OpenGL ก็เพียงพอแล้ว
    • ความพยายามจะสร้าง “3D แบบ SDL” มักจะบานปลายกลายเป็น เอนจินฟูลสแตก อย่างรวดเร็ว
      3D มีองค์ประกอบที่นำมาประกอบกันได้มากกว่า 2D มาก จนกราฟิก API แบบเรียบง่ายรับมือได้ยาก
      เดิมที OpenGL ก็มีเป้าหมายประมาณนั้น แต่สุดท้ายก็ซับซ้อนขึ้นอยู่ดี
  • ‘bike shedding’ หมายถึงการหมกมุ่นกับ ปัญหาเล็กน้อยจนพลาดเรื่องสำคัญ
    สิ่งที่อธิบายในต้นฉบับจริง ๆ แล้วใกล้กับ feature creep หรือ over-engineering มากกว่า

    • ถ้าเป็นผู้เขียน อาจใช้คำว่า “hobby horsing” ก็ได้
      หมายถึงการจดจ่อกับความเพลิดเพลินส่วนตัวจนขัดขวางความก้าวหน้าของโปรเจ็กต์
      bike shedding มักอธิบายว่าเป็นการ “มัวแต่เลือกสีโรงเก็บจักรยานก่อนที่บ้านจะสร้างเสร็จ”
    • กำลัง bike shedding กันอยู่กับคำว่า “bike shedding” เองนั่นแหละ
  • มีคนบอกว่า “การเริ่มพัฒนาเอนจินด้วย Minecraft multiplayer clone ไม่ใช่ความคิดที่ดี” แต่
    จริง ๆ แล้วหลายคนก็สร้าง เกมสไตล์ Minecraft เป็นโปรเจ็กต์เอนจินชิ้นแรก
    สำหรับเอนจิน voxel นั่นแทบจะเป็น “Hello, world” เลย

  • โพสต์ในตอนนั้น (2024) ได้ 625 คะแนนและ 260 คอมเมนต์ — ลิงก์ต้นฉบับ

  • Vulkan เป็น เทคโนโลยีที่ยากที่สุด ที่ฉันเคยเรียน
    มันทั้งไม่เป็นธรรมชาติและมีงานซ้ำซากมากจนดูดความสนุกของการเขียนโปรแกรมออกไป

    • ไม่ใช่ว่านายหัวไม่ดี Vulkan เป็น API นามธรรมของชิประดับล่าง ที่สนุกพอ ๆ กับ USB API
      ถ้าอยากเริ่มแบบง่ายกว่านี้ แนะนำ OpenGL โดยเฉพาะเวอร์ชันก่อนมี shader
      แต่ตอนนี้อุตสาหกรรมก็กำลังค่อย ๆ ผลัก OpenGL ออกไป
    • ตอนฉันเริ่มเรียน Vulkan ครั้งแรกก็รู้สึกแบบเดียวกันเป๊ะ
      ฉันทำได้แค่ตาม tutorial แล้วคัดลอกโค้ด โดยไม่ได้เข้าใจแนวคิดจริง ๆ
      เลยเปลี่ยนไปใช้ WebGPU (Google Dawn) ซึ่งง่ายกว่า Vulkan มาก
      หลังจากเรียนรู้แนวคิดผ่านข้อจำกัดของ WebGPU แล้ว ค่อยกลับมาเรียน Vulkan อีกครั้งก็ง่ายขึ้นมาก
      WebGPU ไม่มี push constant และมีปัญหา pipeline explosion แต่ Vulkan ยากกว่าในด้าน synchronization และ memory management
      SDL_GPU ก็เป็น API ระดับใกล้เคียงกัน จึงเหมาะสำหรับเริ่มต้น
    • เหตุผลที่ฉันยังไม่ย้ายจาก OpenGL ไป Vulkan ก็เหมือนกัน
      Vulkan เป็น API ที่ออกแบบเกินจำเป็น
      การจองหน่วยความจำ GPU ที่ใน CUDA ทำได้ในบรรทัดเดียว กลับต้องใช้ boilerplate จำนวนมากใน Vulkan
      แม้ Vulkan รุ่นใหม่จะดีขึ้นมากแล้ว แต่ก็ยังไปไม่สุด
    • แค่ดูตัวอย่างโค้ด Vulkan ทั่วไปก็รู้ทันทีว่านี่ ไม่ใช่สำหรับนักพัฒนาเกม
      ฉันหวังว่า SDL3 หรือ wgpu จะกลายเป็นชั้นนามธรรมที่ช่วยลดความซับซ้อนนี้ลง
      Valve สนับสนุน SDL3 อยู่ จึงคิดว่าทางนั้นมีแววมากกว่า
    • การเขียนโปรแกรม Vulkan/DX12 นั้นทรมานจริง ๆ
      คำถามแรกที่ต้องถามคือ “จำเป็นต้องประมวลผลกราฟิกแบบมัลติเธรดไหม?”
      ถ้าไม่ ก็ไม่มีเหตุผลจะใช้ Vulkan/DX12
      จนกว่าจะเจอปัญหาด้านประสิทธิภาพ การใช้ OpenGL, DX11 หรือเกมเอนจินจะดีกว่ามาก
  • ฉันหลงใหลในงานเขียนโปรแกรม 3D/เกม และมักดูยูทูบเบอร์บางคนทำเกมอยู่บ่อย ๆ
    แต่เมื่อเทียบกับเว็บแอปหรือ DevOps แล้ว มันเป็น โลกที่ซับซ้อนกว่ามาก
    มีทั้ง pixel shader, compute shader, geometry, linear algebra ไปจนถึง PDE
    ช่อง YouTube ของ TokyoSpliff

  • ฉันชอบที่ทุกวันนี้การ สร้างเกมเอนจินเป็นงานอดิเรกถูกมองว่าเท่มาก
    ฉันเองก็พัฒนาเอนจินส่วนตัวมา 10 ปีแล้ว และมันเป็นประสบการณ์ที่คุ้มค่ามาก

  • ถ้าจะเริ่มทำกราฟิกโปรแกรมมิงครั้งแรก ควร เริ่มจาก OpenGL
    ฉันอ่านบทเรียน OpenGL ของ NeHe เมื่อ 23 ปีก่อน และจนถึงตอนนี้ก็ยังคิดว่ามันเป็นหนึ่งในสื่อการเรียนรู้ที่วางโครงสร้างไว้ดีที่สุด

  • อ้อ ฉันไม่ใช่ผู้เขียนบทความต้นฉบับ แค่คงชื่อเรื่องไว้เท่านั้น