2 คะแนน โดย GN⁺ 2025-01-05 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • วิธีหลัก ๆ ใน Unity สำหรับวาด เส้นขอบ รอบวัตถุ ได้แก่ rim effect, vertex expansion, blur buffer, Jump Flood algorithm และ edge detection ซึ่งแต่ละวิธีมีคุณภาพ ประสิทธิภาพ และต้นทุนการตั้งค่าที่แตกต่างกัน
  • Rim effect ใช้ dot product ระหว่าง normal กับทิศทางการมองเพื่อประมาณ Fresnel และเน้นบริเวณด้านในของขอบ แต่ผลลัพธ์จะแตกต่างกันมากระหว่างโมเดลทรงกลมกับโมเดลที่มีมุมคม
  • Vertex expansion สร้างเส้นรอบนอกโดยขยาย mesh ที่คัดลอกมาไว้ด้านหลัง และถ้าทำใน clip space จะช่วยให้ความกว้างบนหน้าจอสม่ำเสมอกว่า
  • Blur buffer และ Jump Flood algorithm เป็นแนวทางที่วาด silhouette ลงใน buffer แล้วขยายออก โดยมีจุดเด่นตามลำดับคือเส้นที่นุ่มนวล หรือเส้นขอบที่กว้างมาก
  • Edge detection ค้นหาความไม่ต่อเนื่องของ depth, normal และ color buffer จากทั้งหน้าจอเพื่อสร้างเส้น แต่ต้องปรับ threshold และ modulation อย่างละเอียดเพื่อลดเส้นที่ไม่ต้องการ

เหตุผลที่ใช้การเรนเดอร์เส้นขอบ

  • เส้นขอบถูกใช้ในเกมเพื่อสร้าง สไตล์ภาพ หรือเป็น ตัวช่วยด้าน gameplay เช่น การเน้นวัตถุหรือการเลือกวัตถุ
    • Sable ใช้เส้นขอบเพื่อให้ได้สไตล์เหมือนหนังสือการ์ตูน
    • The Last of Us ใช้เส้นขอบเพื่อเน้นศัตรูเมื่อผู้เล่นเข้าสู่โหมดลอบเร้น
  • เทคนิคทั้งห้าวิธีมี trade-off ที่แตกต่างกันระหว่างประสิทธิภาพ คุณภาพของภาพ และปริมาณการตั้งค่าด้วยมือ

Rim effect: เน้นขอบด้านในด้วยการประมาณ Fresnel

  • Rim effect ประมาณ Fresnel effect เพื่อเรนเดอร์เส้นที่ rim/ขอบ ของวัตถุ
  • การคำนวณอิงกับ dot product ของ normal vector N ที่ normalize แล้ว และทิศทางการมอง V ที่ normalize แล้ว
    • นำค่า 1.0 - saturate(dot(N, V)) ไปยกกำลังด้วย P เพื่อสร้างความเข้มของเอฟเฟกต์
    • ใกล้เคียงกับการประมาณสำหรับเส้นขอบมากกว่า Fresnel ที่ถูกต้องตามฟิสิกส์
  • ในการใช้งานบน Unity custom shader จะควบคุม ความกว้าง, power, ความนุ่มนวล และสี ของเส้นขอบ
    • ตัวอย่างการใช้งานใช้ smoothstep, lerp, _OutlineWidth, _OutlineSoftness, _OutlinePower, _OutlineColor
  • วิธีนี้จะแสดงเป็น เส้นด้านใน เสมอ และไม่เห็นบริเวณขอบนอกของวัตถุ
  • เหมาะกับวัตถุที่กลมและเรียบ แต่สำหรับ cube หรือโมเดลที่ซับซ้อน ความกว้างของเส้นอาจไม่สม่ำเสมอหรือดูไม่เหมือนเส้นขอบ

Vertex expansion: ขยาย mesh ที่คัดลอกเพื่อสร้างเส้นด้านนอก

  • Vertex expansion เรนเดอร์ สำเนา ของวัตถุหรือ mesh ต้นฉบับอีกครั้ง แล้วขยาย vertex จากด้านหลังของต้นฉบับเพื่อสร้างรูปทรงที่ใหญ่ขึ้น จากนั้นเรนเดอร์เป็นสีเดียว
  • การเลือกทิศทางการขยาย

    • คุณภาพของเส้นขอบจะแตกต่างกันมากตามทิศทางที่ย้าย vertex
    • หากย้ายตาม ทิศทางตำแหน่งของ vertex mesh จะมีลักษณะพองออก
    • ใน local space ตำแหน่งของ vertex สามารถใช้เหมือนเวกเตอร์จากศูนย์กลางวัตถุไปยัง vertex ได้
    • ถ้าไม่ normalize vertex ที่อยู่ไกลจากศูนย์กลางกว่าจะถูกย้ายมากกว่า
    • การใช้ normalize(positionOS) * width ช่วยให้การย้ายใน object space สม่ำเสมอกว่า
    • หากย้ายตาม ทิศทาง normal vector จะได้ผลดีในวัตถุที่มีขอบเรียบ เช่น sphere หรือ capsule
    • ในวัตถุที่มีมุมคมอย่าง cube อาจเกิดช่องว่างในเส้นขอบได้
    • สามารถใช้ vertex color เป็นทิศทางการขยายได้เช่นกัน
    • วิธีนี้คือการสร้าง custom normal แล้วเก็บไว้ในช่อง vertex color ของ mesh
    • หาก bake normal แบบทรงกลมที่นุ่มนวลลงใน vertex color ของ cube จะได้เส้นขอบที่ดีกว่า
    • ข้อเสียคือเป็นการตั้งค่าด้วยมือที่ต้องสร้าง custom normal สำหรับแต่ละ mesh แต่สามารถทำให้เป็นอัตโนมัติด้วยสคริปต์ได้
  • การเลือก space สำหรับการขยาย

    • ในขั้น vertex ของ shader พิกัด vertex เริ่มจาก object space แล้วถูกแปลงผ่านเมทริกซ์ MVP ไปเป็น clip space
    • ลำดับคือ object/model/local space → world space → camera/view space → clip space → screen space → viewport/window space
    • หากขยายใน object space อาจทำให้เส้นขอบบิดเบี้ยว เพราะยังไม่ได้ใช้การแปลง MVP
    • เมื่อใช้ model matrix อาจเกิดปัญหาเรื่อง scaling
    • เมื่อไปจาก clip space เป็น screen space จะเกิด perspective shrinking จาก perspective division
    • การเคลื่อนที่บางส่วนใน 3D space จะเคลื่อนไปตามทิศทางกล้อง จึงไม่ช่วยเพิ่มความกว้างของเส้นขอบบนหน้าจอ
    • หากขยายใน clip space จะสร้างเส้นขอบที่สะอาดและมีความกว้างบนหน้าจอใกล้เคียงกันได้
    • แปลงตำแหน่ง vertex และ normal ไปเป็น clip space แล้วแก้เฉพาะพิกัด x, y
    • หารด้วยความกว้างและความสูงของหน้าจอเพื่อชดเชยอัตราส่วนภาพ
    • คูณด้วย w ของ clip space เพื่อไม่ให้ perspective division หลังจากนั้นส่งผลสุทธิต่อความกว้างของเส้นขอบ
    • คูณด้วย width * 2 เพื่อให้หน่วยความกว้าง 1 ตรงกับ 1 พิกเซลบนหน้าจอ
    • วิธี clip space ก็ยังขึ้นกับ normal ของ mesh
    • หากไม่ใช้ custom normal อาจเกิดช่องว่างที่มุมคมได้
    • ถ้า normal ตั้งค่าไม่ถูกและชี้ไปทิศทางตรงข้าม vertex ของเส้นขอบก็จะย้ายไปทางตรงข้าม ทำให้เกิดช่องว่าง
    • ดูคำอธิบายเพิ่มเติมได้ที่ creating an outline in clip space
  • การทำ masking

    • ใน mesh ที่คัดลอกมา ควรแสดงเฉพาะ ส่วนเส้นขอบที่ยื่นออกมา เท่านั้น
    • วิธีทั่วไปคือ cull front-facing geometry ของ mesh ที่คัดลอก แล้วใช้ backface สร้างเส้นขอบ
    • depth test ใช้ less than or equal to เพื่อให้ backface เห็นเฉพาะตำแหน่งเส้นขอบ
    • อีกวิธีคือใช้ stencil mask เพื่อไม่ให้ mesh ที่คัดลอกมาแสดงอยู่ด้านหน้าต้นฉบับ
    • ในกรณีนี้ไม่จำเป็นต้อง cull
    • จะไม่เกิดเส้นด้านในของวัตถุเลย
    • หากวัตถุสองชิ้นซ้อนกัน จะเห็นเส้นขอบเฉพาะรอบวัตถุทั้งสองเท่านั้น

Blur buffer: ขยาย silhouette ด้วยการเบลอ

  • วิธี blur buffer เรนเดอร์ silhouette ของวัตถุลงใน buffer แล้วขยายด้วย blur จากนั้นใช้ผลลัพธ์นั้นในการเรนเดอร์เส้นขอบ
  • Silhouette buffer

    • ขั้นแรกคือการสร้าง silhouette buffer
    • เรนเดอร์แต่ละวัตถุลงใน texture ด้วย shader ที่ส่งออกเป็นสีเดียว
    • หากเรนเดอร์ silhouette ทั้งหมดเป็นสีขาว สามารถคูณด้วยสีเส้นขอบที่ต้องการในตอนท้ายเพื่อให้เป็นสีเดียวได้
    • หากต้องการเส้นขอบคนละสีสำหรับแต่ละวัตถุ ก็สามารถเรนเดอร์ silhouette แต่ละอันด้วยสีเฉพาะได้
  • Blur pass

    • Blur pass ใช้สำหรับขยาย silhouette buffer
    • โดยปกติใช้ box blur หรือ Gaussian blur
    • เพื่อปรับปรุงประสิทธิภาพ สามารถลดขนาด silhouette buffer ก่อน blur ได้
    • Blur pass มีค่าใช้จ่ายสูง เพราะต้องคำนวณค่าเฉลี่ยหรือค่าเฉลี่ยถ่วงน้ำหนักของหลายพิกเซลรอบ ๆ แต่ละพิกเซล
    • ควรทำ blur แบบ 2 pass
    • สำหรับ box blur และ Gaussian blur ซึ่งเป็น separable filter สามารถลดความซับซ้อนของ algorithm จาก O(N²) เป็น O(2N) ได้
    • เริ่มจาก blur ในแนวตั้งก่อน แล้วนำผลลัพธ์ไป blur อีกครั้งในแนวนอนเพื่อให้ได้ผลลัพธ์สุดท้าย
    • ความกว้างของเส้นขอบควบคุมด้วยพารามิเตอร์ _KernelSize ของ blur shader
  • Outline pass และ masking

    • หลังจาก blur pass ให้นำ silhouette ที่ถูกเบลอไป composite กับฉากต้นฉบับเพื่อสร้างเส้นขอบ
    • Blur buffer เหมาะกับ เส้นขอบที่นุ่มนวลหรือเรืองแสง
    • หากใช้ step processing กับผล blur ก็สามารถเรนเดอร์เส้นขอบแบบแข็งได้เช่นกัน
    • สามารถใช้ stencil mask เหมือนวิธี vertex expansion เพื่อให้เส้นขอบถูกเรนเดอร์เฉพาะด้านหลัง geometry
    • อาจมีผลกระทบต่อประสิทธิภาพมากกว่าวิธีอื่น

Jump Flood algorithm: จัดการเส้นขอบที่กว้างมาก

  • วิธีที่สี่คือการเรนเดอร์เส้นขอบด้วย Jump Flood algorithm
  • ข้อดีคือสามารถเรนเดอร์เส้นขอบที่กว้างมากได้ด้วยต้นทุนด้านประสิทธิภาพที่สมเหตุสมผล
  • รายละเอียดต่อไปอยู่ในบทความของ Ben Golus เรื่อง The Quest for Very Wide Outlines

Edge detection: ค้นหาความไม่ต่อเนื่องจากทั้งหน้าจอ

  • วิธี edge detection วาดเส้นโดยค้นหา ความไม่ต่อเนื่อง ของฉากใน full-screen pass
  • ความไม่ต่อเนื่องสามารถตรวจจับได้จากค่า depth buffer, normal vector, albedo color หรือข้อมูลอื่น ๆ ที่ใช้ได้ระหว่างกระบวนการเรนเดอร์
  • Roberts cross

    • Roberts cross operator เป็น differential operator ที่คำนวณผลรวมกำลังสองของความต่างระหว่างพิกเซลแนวทแยง
    • ในการใช้งานจริง จะตรวจจับ edge โดย convolution kernel กับภาพต้นฉบับ
    • ใช้ kernel 2 ชุดสำหรับทิศทาง x และทิศทาง y
    • ขนาด kernel คือ 2 x 2
    • ต้องใช้ sample เพียง 4 จุดรอบหนึ่งพิกเซล
    • เป็น operator ที่เรียบง่ายแต่ให้ผลลัพธ์ที่ดีได้
  • Sobel operator

    • Sobel operator ก็ใช้ kernel 2 ชุดสำหรับทิศทาง x และทิศทาง y
    • ขนาด Sobel kernel คือ 3 x 3 และใช้ sample 9 จุดรอบหนึ่งพิกเซล
    • ดูการทำงานของ Sobel filter เพิ่มเติมได้ที่ blog post on Sobel filters
  • แหล่งที่มาของความไม่ต่อเนื่อง

    • วิธีทั่วไปคือค้นหาความไม่ต่อเนื่องจาก depth texture, normal texture และ color texture ที่ render pipeline สร้างสำหรับฉาก
    • Edge detection pass จะ sample texture เหล่านี้และตรวจจับความไม่ต่อเนื่องด้วย operator ที่กล่าวถึงข้างต้น
    • edge ที่สร้างขึ้นแบบนี้อาจถูกวาดเพราะพบความไม่ต่อเนื่องในหนึ่งในสาม buffer
    • วิธีนี้มีการควบคุมรายวัตถุต่ำ เพราะเส้นขอบจะถูกใช้กับวัตถุทั้งหมดที่เขียนลงใน buffer นั้น
    • การอนุญาตให้มีแหล่งความไม่ต่อเนื่องหลายแหล่งช่วยสร้างระบบเส้นขอบที่ robust กว่า
    • edge บางเส้นตรวจพบจากทั้งสามแหล่ง
    • edge จำนวนมากตรวจพบจากการมีส่วนร่วมของแหล่งใดแหล่งหนึ่งเท่านั้น
    • สามารถกำหนด weight และ threshold ต่างกันให้แต่ละแหล่งได้ เพื่อควบคุมผลลัพธ์ทางภาพของเส้นขอบ
  • การ modulate edge detection

    • หากใช้เพียง edge detection operator กับ buffer ความไม่ต่อเนื่อง อาจได้ผลลัพธ์ที่ปราศจาก artifact ได้ยาก
    • ใน render pipeline จำนวนมาก depth buffer ถูกใช้งานแบบ non-linear
    • เมื่อวัตถุสองชิ้นที่อยู่ใกล้กล้องห่างกัน 1m ความต่างของ depth จะมากกว่าวัตถุสองชิ้นที่อยู่ไกลและห่างกัน 1m
    • เพื่อชดเชยเรื่องนี้ สามารถ modulate threshold สำหรับการตรวจจับความไม่ต่อเนื่องของ depth ด้วย depth buffer เองได้
    • geometry ที่อยู่ใกล้ต้องมีความไม่ต่อเนื่องของค่า depth มากขึ้นก่อนจะถูกตรวจจับเป็น edge
    • อาจเกิด edge ที่ไม่ต้องการเมื่อมี grazing angle เล็ก ๆ ได้เช่นกัน
    • สามารถ modulate ด้วย Fresnel mask ที่สร้างจาก dot product ของ normal vector N กับทิศทางการมอง V
    • mask นี้เป็นชนิดเดียวกับที่ใช้ในวิธี rim effect
    • สามารถใช้เทคนิค modulation อื่นได้เช่นกัน แต่การเลือกขึ้นอยู่กับ visual effect ที่ต้องการ
  • แหล่งความไม่ต่อเนื่องแบบ custom

    • สามารถส่ง แหล่งความไม่ต่อเนื่องแบบ custom ให้ outline shader ได้ด้วย
    • สิ่งนี้คือ render texture ที่สร้างขึ้นโดยตรงระหว่างกระบวนการเรนเดอร์ และบรรจุข้อมูล custom สำหรับใช้สร้างเส้นขอบ
    • เพราะสามารถควบคุมได้โดยตรงว่าวัตถุใดถูกเขียนลงใน custom buffer จึงควบคุมได้ด้วยว่าวัตถุใดจะได้รับเส้นขอบ
    • ตัวอย่างเช่น เรนเดอร์ vertex color ของ mesh ลงใน texture เพื่อสร้างแหล่งความไม่ต่อเนื่อง
    • อีกวิธีคือการระบายสีพื้นผิวตาม world position หรือสร้าง custom buffer ที่รวมข้อมูลจาก depth buffer และ normal buffer
    • ดูข้อมูลเพิ่มเติมได้ที่ Linework section map

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

 
GN⁺ 2025-01-05
ความคิดเห็นจาก Hacker News
  • บทความ Jump Flood Algorithm ที่ลิงก์ไว้นั้นยอดเยี่ยมมาก: https://bgolus.medium.com/the-quest-for-very-wide-outlines-b...
    น่าสนใจที่ได้คิดถึงหลายแนวทางซึ่งใช้ได้ในระดับพิกเซล/เท็กเซล และตรงนี้ Signed Distance Field (SDF) ก็ถูกใช้ในวิธีที่ชาญฉลาดเพื่อช่วยทำงานหลายอย่างแทน
    ผลลัพธ์ที่สร้างเส้นขอบตามความหนาที่ต้องการได้ในเวลาเชิงเส้นนั้นน่าทึ่งเมื่อเทียบกับวิธีคำนวณแบบ brute force สำหรับเส้นขอบหนา ๆ
    ขอแนะนำ SDF อย่างยิ่ง ไม่ว่าจะเป็นแบบอิงเวกเตอร์ แบบอิงฟังก์ชันเหมือนงานของ Inigo Quilez หรือแบบแรสเตอร์อิงเท็กเซล/ว็อกเซลเหมือนในบทความ
    Houdini ก็รองรับ raster SDF ได้ดีและมีชุดเครื่องมือ SDF ที่สุกงอมแล้ว ดังนั้นแม้แต่เวอร์ชันฟรีก็น่าลองดู

    • Jump Flood Algorithm มีความซับซ้อน O(N log N) เมื่อ N คือจำนวนพิกเซล และยังมีอัลกอริทึม O(N) ที่ดีกว่าซึ่งขนานงานเป็นหน่วยแถว/คอลัมน์ได้ด้วย: https://news.ycombinator.com/item?id=36809404
      ข้อเสียคือถ้าจะรันบน GPU ต้องมีการเขียนแบบสุ่มเข้าถึง หรือก็คือ compute shader
      ถ้าใช้ CPU ได้ ก็มี implementation อยู่หลายตัว
      JavaScript: https://parmanoir.com/distance/
      C: https://github.com/983/df
      C++: https://github.com/opencv/opencv/blob/4.x/modules/imgproc/sr...
      Python: https://github.com/pymatting/pymatting/blob/afd2dec073cb08b8...
    • ในโปรเจกต์ส่วนตัว ผมใช้ เส้นขอบแบบ JFA/SDF มากที่สุด
      เพราะคุณภาพดี และสามารถเรนเดอร์เอฟเฟกต์ตามระยะทางได้ เช่น เส้นขอบที่เต้นเป็นจังหวะ
      เครื่องมือวาดเส้น 3D ตัวนี้ก็เก็บ SDF ไว้ในเท็กซ์เจอร์ขนาดเล็กแล้ว sample ตอนรันไทม์: https://x.com/alexanderameye/status/1663523972485357569
      SDF ทรงพลังจริง ๆ
    • SDF ยังเข้ากับโครงสร้างของ circle marching ได้ดีด้วย ซึ่งเป็นแนวทางหนึ่งในการเร่ง global illumination ให้ใกล้เคียงเรียลไทม์ แม้จะมีข้อจำกัดอยู่บ้าง
      ถ้าขยายแนวทางเดียวกันไปถึง radiance cascades ก็อาจเร็วขึ้นได้อีก จึงค่อนข้างน่าสนใจ
  • สักวันหนึ่งอยากเจาะลึก กราฟิก 3D แบบมีสไตล์เฉพาะ ในฐานะโปรเจกต์วิจัยและพัฒนา
    ช่วงหลังมีความคืบหน้าไปพอสมควร แต่ยังดูเหมือนมีผลไม้ที่เก็บได้ง่ายเหลืออยู่อีกมาก
    สงสัยว่าเมื่อกล้องถอยห่างออกไป จะลดรายละเอียดของโมเดล 3D แบบ toon rendering อย่างไร และจะเปลี่ยนผ่านระหว่างรูปลักษณ์ที่มีสไตล์จัดกับที่มีสไตล์น้อยกว่าให้เป็นธรรมชาติได้อย่างไร
    อีกปัญหาคือจะเรนเดอร์ฉากหลังสีน้ำของแอนิเมชัน 2D วาดมือให้ดูน่าเชื่อในฉาก 3D ได้ไหม และจะทำให้รอยพู่กันกับเท็กซ์เจอร์กระดาษใน screen space เคลื่อนไหวอย่างนุ่มนวลได้อย่างไร
    ยังเป็นคำถามเปิดอยู่ว่าควรแสดงองค์ประกอบอย่างควัน เปลวไฟ ต้นไม้ หญ้า โคลน ฝน ขน และน้ำ ในเกม 3D แบบมีสไตล์เฉพาะอย่างไร
    ยังสงสัยด้วยว่าจะทำให้การดัดแปลงโมเดลเล็กน้อยเพื่อให้ดูดีขึ้นจากมุมกล้องปัจจุบันแบบแอนิเมชันวาดมือ เป็นระบบอัตโนมัติในเกมที่มีกล้องอิสระได้ไหม
    คำถามใหญ่คือ mesh editor และ background editor ในอุดมคติสำหรับ renderer แบบมีสไตล์ควรเป็นอย่างไร และจำเป็นต้องมีพื้นผิว 3D กับ rig ที่ถูกต้องตามฟิสิกส์จริง ๆ ไหม หรือจะนิยามให้เป็นนามธรรมกว่านั้นได้ไหม
    ก็น่าสนใจเช่นกันว่าจะเรนเดอร์ pixel art ย้อนยุคจากโมเดล 3D แบบเรียบง่ายเพื่อสร้างเกม 2D เชิง procedural ได้ไหม และเมื่อ mesh สองชิ้นบังเอิญตัดกัน ก็อาจใช้การทำสไตล์ให้จุดตัดนั้นสะดุดตาน้อยลงได้
    แค่คำถามเหล่านี้ก็น่าจะเติมเต็มอาชีพของคนสิบคนได้แล้ว แต่ผมว่ากลับเป็นเรื่องดีเสียอีก

    • ฉากหลังสีน้ำมีหลายเทคนิค และงานฝั่ง Blender ที่ใช้ Blender geometry nodes เป็นสิ่งที่สะดุดตาผมที่สุดเป็นการส่วนตัว: https://www.youtube.com/watch?v=ljjUoup2uTw
      ฟิลเตอร์ Kuwahara ก็ดูน่าเชื่อถือพอสำหรับคนส่วนใหญ่
      เครื่องมือแก้ไขสำหรับ renderer แบบมีสไตล์ แค่ Blender + Rigify + shape keys + เวทมนตร์จาก drivers อีกนิดหน่อยก็เพียงพอกับความต้องการของผมแล้ว
      การทำ texture ใน Blender น่ารำคาญ แต่ถ้าระดับงานอดิเรกก็พอทนได้ และถ้าต้องการการควบคุม non-photorealistic rendering มากขึ้น fork ของ DillonGoo Studio อาจเหมาะกว่า: https://www.dillongoostudios.com/gooengine
      การทำ pixel art จากโมเดล 3D ผมเคยลองด้วยวิธีเรนเดอร์แอนิเมชัน/โมเดลที่ความละเอียดต่ำ ผลลัพธ์โอเค แต่ต้องลองผิดลองถูก
      จำได้ว่าเคยมีตัวอย่างที่ทำ post-processing ซับซ้อนขึ้นเพื่อกำจัดสิ่งอย่าง pixel flicker ด้วย
    • ถึงจะไม่ใช่ 3D แต่สตูดิโอทำ data visualization และแผนที่ชื่อ Stamen เคยสร้าง renderer แผนที่สีน้ำ ที่สวยงามไว้: https://maps.stamen.com/watercolor/#14/52.3718/4.8958
    • อาจพูดได้ไม่เต็มปากว่าเป็น pixel art ย้อนยุค แต่ตั้งแต่ Commandos 2 เป็นต้นมา เขาสร้างตัวละคร ยานพาหนะ ฯลฯ เป็น โมเดล 3D แล้วเรนเดอร์เป็นสไปรต์ 2D ตอน runtime จากนั้นนำไปผสมกับสไปรต์ 2D และฉากหลังที่เรนเดอร์ไว้ล่วงหน้า
    • ตอนทำเกม 3D pixel art ต้องปรับ ขนาดพิกเซล เมื่อกล้องซูมออก
      ความละเอียดต่ำ: https://x.com/Navy_Green/status/1525564342975995904
      การทำให้เสถียร: https://x.com/Navy_Green/status/1693820282245431540
    • ถ้าต้องการตัวอย่างการทำ pixel art จากโมเดล 3D ดู Dead Cells ได้: https://www.gamedeveloper.com/production/art-design-deep-div...
  • เริ่มอาชีพด้วยแอป VR แต่ไม่นานก็ย้ายไปทำเว็บเพราะตลาดดีกว่า
    พอเห็นบทความแบบนี้แล้วก็คิดถึงสายงานนั้น
    งานที่เกี่ยวกับ กราฟิก 3D, collision, shader มีความรู้สึกเหมือนเวทมนตร์บางอย่างที่หาได้ยากในสายอื่น
    จริง ๆ แล้วมันเหมือนกับการสร้างโลกและจำลองฟิสิกส์ขึ้นมา และคณิตศาสตร์ก็ใช้งานได้จริงและโผล่มาบ่อยกว่าสาขาโปรแกรมมิงอื่นมาก

    • ผมย้ายสวนทางจากเว็บดีเวลลอปไปสายเกม และมันสนุกกับน่าสนใจอย่างที่ได้ยินจริง ๆ
      ความพึงพอใจในการทำงานสูงกว่ามาก และหัวข้อก็มีความลึกกับเพดานให้ไปต่อสูงกว่า
      ทำมา 4 ปีแล้ว ยังไม่เคยชนกำแพงความเบื่อแบบตอนทำเว็บดีเวลลอปเลย
      ไม่มีอะไรดีไปกว่าการมาทำงานวันจันทร์ เปิด engine editor แล้วมองโลกเล็ก ๆ ที่กำลังทำอยู่ถูกเรนเดอร์ขึ้นมา พร้อมคิดว่าจะใส่ฟีเจอร์เจ๋ง ๆ อะไรต่อไป
    • ในเว็บดีเวลลอป ถ้าผู้คนดูดโค้ดทั้งหมดไปป้อน neural network ส่วนที่น่าเบื่อและน่ารำคาญอาจลดลง แล้วเหลืองานที่น่าสนใจไว้
      แต่ในทางกลับกัน ในสาย technical art ถ้าดูดผลงานไปป้อน neural network ส่วนที่น่าสนใจและให้ความรู้สึกคุ้มค่าจะหายไป และสำหรับงานมืออาชีพก็จะเหลือแต่งานน่าเบื่อเดิม ๆ หรืออาจเพิ่มขึ้นด้วยซ้ำ
      มูลค่าของชุดทักษะทั้งหมดก็ลดลง และคนในวงการเทคทำท่าเหมือนรู้เรื่องงานนั้นดีกว่าคนที่ทำจริง ๆ พร้อมตำหนิว่าเราไม่ยินดีที่บริษัท Silicon Valley เอาผลงานราคา C- ออกมาขายในราคา F โดยไม่จ่ายค่าตอบแทนให้คนที่สร้างข้อมูลต้นทาง และทำลายตลาด
      ชั่งข้อดีข้อเสียแล้ว ผมคิดว่าการย้ายไปทำเว็บดีเวลลอปเป็นทางเลือกที่ถูกต้อง
  • ในเกม Astral Divide ของผม ผมสร้างเทคนิคที่ไม่ได้อยู่ในบทความนี้
    คล้ายกับ Blurred Buffer แต่ไม่ทำพาสเบลอ และใช้เส้นขอบที่เกิดจาก anti-aliasing แทน
    วาดวัตถุเป็นสีขาวทึบบนพื้นหลังสีดำโปร่งใส แล้วใน fragment shader ก็กรองเฉพาะพิกเซลที่ช่อง alpha ไม่ได้ทึบสนิทหรือโปร่งใสสนิท โดยใช้ค่า threshold ที่ hardcode ไว้
    ผลลัพธ์ออกมาดีพอสมควร ต้นทุนด้านประสิทธิภาพก็ถูก และการ implement ก็ง่ายมาก

    • ทริกแบบนั้นให้ความรู้สึกน่าพอใจจริง ๆ
      เมื่อก่อนตอน implement เครื่องมือครอป ในซอฟต์แวร์ตัดต่อวิดีโอ ระหว่างแก้ไขต้องทำให้พื้นที่นอกส่วนที่ถูกครอปดูเบลอชั่วคราว
      ไม่อยากใส่พาสเบลอแพง ๆ อีก เลยแค่เปลี่ยน mipmap bias ให้เรนเดอร์ texture ความละเอียดต่ำลง แล้ว texture filtering ก็ทำงานให้ฟรีทั้งหมด
      ลองเทียบกับเอฟเฟกต์เบลอเวลาครอปที่คล้ายกันใน PowerPoint แล้วแทบเหมือนกัน แถม PowerPoint ยังมี color banding นิดหน่อย แต่ implementation ของผมไม่มี
      ในทำนองเดียวกัน ตอนที่ไม่มี anti-aliasing แบบเต็มความละเอียด พื้นที่ส่วนใหญ่ของ canvas เป็นสี่เหลี่ยม 2D ที่หมุนได้ เลยเห็นขอบเป็นขั้นบันได
      แทนที่จะเปิด anti-aliasing เต็มหน้าจอ ผมขยายสี่เหลี่ยมทุกอันออกเล็กน้อยและย่อพิกัด UV ลงตามสัดส่วน ทำให้ขอบที่มองเห็นอยู่ด้านในของสี่เหลี่ยม 3D จริง ๆ แล้วครั้งนี้ texture filtering ก็แก้ให้ฟรีอีกเช่นกัน
    • ถ้าตั้งพิกเซลพวกนั้นเป็นสีทึบสีเดียว จะไม่เกิด อาการขอบหยักเป็นขั้นบันได เหรอ?
      หรือเป็นวิธีที่คงค่า alpha ไว้แล้วตั้งแค่สี ทำให้เส้นขอบดูจางแต่ไม่เป็นขั้นบันได?
  • โน้ตยอดเยี่ยมมาก
    ช่วงนี้ผมกำลังหาแนวทาง edge detection อยู่ แล้วเจอวิธีดี ๆ จากผู้พัฒนา Mars First Logistics: https://www.reddit.com/r/Unity3D/comments/taq2ou/improving_e...

  • บทความก็ดีมาก และผลลัพธ์ก็ดูดีมาก: https://ameye.dev/notes/rendering-outlines/edge-detection/co...
    ดูเหมือนฉากหนึ่งจากการ์ตูนเนเธอร์แลนด์ Franka

    • เป็นการสร้างภาพหนึ่งช่องจาก Tintin The Black Island ขึ้นมาใหม่
  • เป็นบทความที่ยอดเยี่ยมจริง ๆ และ ประสบการณ์การอ่าน ก็ยอดเยี่ยมด้วย
    อธิบายแนวคิดยาก ๆ ด้วยภาษาที่ใครก็เข้าใจได้ แผนภาพและตัวอย่างก็ดี อีกทั้งระยะห่างและ typography ยังทำให้อ่านง่ายในระดับสูงสุด
    อยากรู้ว่าอะไรเป็นแรงบันดาลใจให้สร้างธีมปัจจุบัน และเคยคิดจะทำแพลตฟอร์มเผยแพร่คอนเทนต์ที่เน้นวิศวกรบ้างไหม

  • ในซอฟต์แวร์ technical art คือรักแรกของผมอย่างแท้จริง
    อยากให้ Godot มี pipeline สำหรับ compute shader ของเอฟเฟกต์ post-processing ที่ใช้ง่ายกว่านี้
    การตั้งค่า compositor plugin ตอนนี้มี boilerplate ค่อนข้างเยอะ
    repository นี้เป็นตัวอย่างที่ดีของ post-processing ใน Godot: https://github.com/sphynx-owner/JFA_driven_motion_blur_demo

  • ผมจำได้ว่าเห็นเอฟเฟกต์นี้ครั้งแรกใน Wacky Races บน Dreamcast
    ตอนนั้นมีการโปรโมตเยอะว่าเป็นเกมแรกที่นำเอฟเฟกต์นี้มาใช้ และนักพัฒนาแทบจะเป็นผู้คิดค้นมันขึ้นมา
    ไม่รู้ว่านั่นเป็นเรื่องจริงหรือเป็นการพูดเกินจริงเพื่อการตลาด แต่ในฐานะเกมเมอร์ ผมมั่นใจว่าประสบการณ์แรกของผมคือตอนนั้น

    • ผมคิดว่าคนจำนวนมากคงคิดค้นเอฟเฟกต์นั้นขึ้นเองในช่วงเวลาใกล้ ๆ กัน
      พูดตรง ๆ Dreamcast เป็นฮาร์ดแวร์แรกที่สามารถทำเอฟเฟกต์นี้แบบเรียลไทม์ด้วยคุณภาพสูงได้
      ผมพัฒนา เอฟเฟกต์ cel shading ให้เกม Looney Tunes: Space Race บน Dreamcast และทำได้ทันทีในสัปดาห์แรกที่ได้รับ dev kit ของ Dreamcast
      Infogrames Sheffield ผู้สร้าง Wacky Racers เห็น implementation เวอร์ชันแรก ๆ ของเรา แล้วเพิ่มเอฟเฟกต์ที่คล้ายกันเข้าไปในเกมของตัวเอง
      มันดูดี แต่ถูกใส่เข้ามาช่วงท้ายการผลิต จึงไม่ได้ถูก optimize ให้เข้ากับเอฟเฟกต์นั้นเหมือนเกมของเรา
      ทีม Jet Grind Radio ก็สร้างเอฟเฟกต์เดียวกันขึ้นเอง และวางจำหน่ายก่อนเรา
      algorithm เหมือนกันเป๊ะ แต่การใช้งานต่างกัน ฝั่งนั้นยอมรับเส้นขอบที่ไม่สม่ำเสมอ หนา และขรุขระอย่างเต็มที่ ส่วน Sheffield กับเราพยายามสู้กับลักษณะนั้นเพื่อให้เข้ากับสไตล์งานศิลป์ที่สม่ำเสมอและดั้งเดิมกว่า
      ประมาณหนึ่งปีต่อมา ใน Dragons Lair 3D บน Xbox ดูเหมือนมีใครบางคนหาวิธีรัน cel shading แบบ edge detection แบบเรียลไทม์ได้
      ผมเคยทำ implementation ทดลองของแนวทางนั้นบน Dreamcast แต่ประสิทธิภาพไม่พอเลยสำหรับใช้กับตัวละครหลายตัวพร้อมกันขณะรันเกม
      ไม่รู้ว่าเป็นเพราะ Xbox แรงกว่า หรือ algorithm ฉลาดกว่า แต่ผลลัพธ์นั้นปฏิเสธไม่ได้
      ถ้าอยากทำเกมที่ดูเหมือนการ์ตูนวาดมือจริง ๆ ส่วนตัวผมยังคิดว่าวิธีนั้นเป็นวิธีที่ให้คุณภาพดีที่สุด
      สักวันอยากหาข้ออ้างมาทำมันใหม่อีกครั้ง และตอนนี้ประสิทธิภาพคงไม่ใช่ปัญหาแล้ว
    • ดูเหมือนว่าจะออกพร้อม ๆ กับ Jet Set Radio และเกมนั้นก็เป็นของ Dreamcast พร้อมเอฟเฟกต์คล้ายกันด้วย
      เป็นความบังเอิญที่น่าทึ่งทีเดียว
  • ผมต้องเริ่ม shader programming และการเรนเดอร์ 3D ให้ได้แล้ว
    บทความแบบนี้ดีจริง ๆ และอยากให้ตัวเองใช้ shader ได้จริง

    • ผมเรียนด้วย Shadron เมื่อราว 8 ปีก่อน