1 คะแนน โดย GN⁺ 2024-07-22 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • CSS box-shadow โดยปกติเป็นพร็อพเพอร์ตีที่ใช้เพิ่มมิติความลึกให้ UI แต่ถ้านำมาซ้อนกันหลายร้อยถึงหลายพันรายการบน div เดียว มันจะทำงานคล้าย API สำหรับวาดภาพ ชนิดหนึ่ง
  • แม้จะมีข้อจำกัดว่าเป็นกล่อง แต่ด้วยมุมโค้งมน การต่อเงาหลายรายการเป็นเชน และการผสมสี·เบลอ·ความโปร่งใส ก็สามารถสร้างจุดวงกลมและเอฟเฟกต์แบบเลเยอร์ได้
  • ในการทดลอง blur และความโปร่งใส ทำให้จำนวนรายการที่แอนิเมตได้ลดลงอย่างมาก และเมื่อเอาสองอย่างนี้ออก โน้ตบุ๊ก M1 ก็สามารถวาด box-shadow ได้หลายพันรายการ
  • การจำลองลูกบอล พิกเซลของภาพ จุดบนผิวคิวบ์·ทรงกลม และ visualization ที่ตอบสนองต่อเพลง ล้วนถูกทำด้วยวิธีอัปเดต สตริง box-shadow ในทุกเฟรม
  • ตอนท้ายแสดงให้เห็นว่า ray tracer แบบใช้ CPU ซึ่งทำงานขนานด้วย Web Workers สามารถเรนเดอร์ฉากได้ด้วย div เดียวและ box-shadow เท่านั้น

ใช้ box-shadow เหมือนเครื่องมือวาดภาพ

  • CSS box-shadow เป็นรูปแบบหนึ่งของ drop shadow ซึ่งเดิมใช้วาดเงาด้านหลังรูปภาพหรือองค์ประกอบ UI เพื่อเพิ่มมิติความลึก
  • drop shadow ทั่วไปจะเลื่อน raster ของภาพไปตามแกน x/y แล้ววาดด้วยสีเดียวไว้ด้านหลังต้นฉบับเพื่อสร้างมิติความลึก
  • CSS filter: drop-shadow() รับค่าออฟเซ็ต x/y และสี และรองรับค่า blur เพิ่มเติมได้ด้วย
  • การซ้อนฟิลเตอร์ drop shadow หลายชั้นช่วยเพิ่มมิติความลึกให้กับองค์ประกอบบนหน้าจอได้

คุณลักษณะและข้อจำกัดของ box-shadow

  • “Box” ใน box-shadow หมายความว่ารูปร่างของเงาถูกจำกัดอยู่กับ bounding box ของคอนเทนเนอร์
  • ข้อจำกัดนี้อาจดูจำกัดมาก แต่ UI ส่วนใหญ่ประกอบด้วยกล่อง จึงมีประโยชน์ใน UI จริง
  • การทำงานของ CSS box-shadow รองรับการปรับแต่งเชิงคณิตศาสตร์เพื่อวาดกล่องมุมโค้งมนได้อย่างประหยัด และกล่องโค้งมนอาจดูเหมือนวงกลมได้
  • นักออกแบบสามารถใช้ box-shadow เพื่อหลีกเลี่ยงการเพิ่มขนาดดาวน์โหลด โดยไม่ต้องใช้ภาพที่เรนเดอร์ไว้ล่วงหน้า
  • div เดียวสามารถต่อ box-shadow หลายรายการเป็นเชนได้ และตัวอย่างก็ใช้วิธีนี้เพื่อประกอบสีและเงา
  • สามารถทดลองปรับ border-radius ได้ที่ CSS border-radius generator

วิธีใช้ box-shadow ที่ “ผิด”

  • ในงานออกแบบ UI ทั่วไป เงาถูกใช้เป็นองค์ประกอบเสริมร่วมกับระยะว่าง padding typography และ accessibility เพื่อแยกสถานะและการโต้ตอบ
  • แต่ box-shadow สามารถถูกมองเหมือน painting API ที่ระบายสี่เหลี่ยมจำนวนเท่าใดก็ได้บนหน้าจอ และเลือกใส่ blur ได้
  • การทดลองแรกเริ่มจาก มินิมัลอาร์ตในบทความก่อนหน้า และสร้างผลลัพธ์ทางภาพได้ด้วยการตั้งค่าบล็อกสีแบบง่าย ๆ
  • หลังจากนั้น จึงสร้างเครื่องมือ visualization เพื่อดูว่า box-shadow จำนวนมากขึ้น blur และความโปร่งใสส่งผลต่อประสิทธิภาพอย่างไร
    • สร้างสตริง box-shadow ขนาดใหญ่แล้วตั้งค่าให้ div เดียว
    • แอนิเมชันเริ่มจากการเปลี่ยนสตริง box-shadow ทุก 300ms แล้วปล่อยให้ transition: all จัดการ
    • วิธีนี้มี jank และช้ากว่าการตั้งค่า box-shadow ในทุกเฟรม
  • ตัวอย่าง box-shadow 100 รายการให้แตะเพื่อรีมิกซ์พาเลตสี และแสดงประวัติพาเลต 10 รายการล่าสุดทางซ้าย
  • เมื่อใช้ blur จำนวนรายการที่แอนิเมตได้ลดลง และสีโปร่งใสก็ทำให้จำนวนที่วาดได้ลดลงอย่างมากเช่นกัน
  • ขนาดของ div ก็ส่งผลต่อประสิทธิภาพ และระหว่างแอนิเมชันดูเหมือนว่าจะมี software rasterizer เข้ามาเกี่ยวข้อง
  • หากไม่ใช้ความโปร่งใสและ blur โน้ตบุ๊ก M1 สามารถวาด box-shadow ได้หลายพันรายการ

สร้างการจำลองลูกบอลด้วย box-shadow

  • box-shadow หมุนไม่ได้ แต่ใช้ border-radius ทำให้ดูเหมือนวงกลมได้
  • เงาทรงกลมหลายรายการถูก扱เหมือนลูกบอล และสเกลขนาดตามค่า z เพื่อสร้างมิติ 3D ปลอม
  • การทำงานคืออัปเดตสถานะเกมใน requestAnimationFrame และตั้งค่า สตริง box-shadow ขนาดใหญ่ให้ div ในทุกเฟรม
  • กระบวนการเรนเดอร์เป็นดังนี้
    • เรียงลูกบอลตามค่า z
    • คำนวณขนาดตามค่า z
    • แปลงตำแหน่ง x/y, spread และสีของลูกบอลแต่ละลูกให้เป็นรายการ box-shadow
    • เชื่อมรายการด้วยคอมมาให้เป็นสตริงเดียว
  • ตัวอย่างลูกบอล 50 ลูกให้ลากเพื่อขยับลูกบอล และทำให้เด้งอยู่ในกล่อง
  • การสเกลแบบ 3D ปลอมช่วยให้เกิดมิติความลึกได้ระดับหนึ่ง แต่เมื่อลูกบอลเข้าใกล้ “กล้อง” วงกลมอาจดูแตกได้
    • เพราะ div ที่ใช้เป็นฐานสร้าง box-shadow เล็กเกินไปเมื่อเทียบกับวิธีสเกล
    • แก้ได้ด้วยการเพิ่มขนาดคอนเทนเนอร์ แต่ยิ่งคอนเทนเนอร์ใหญ่ ประสิทธิภาพก็ยิ่งช้าลง
  • เวอร์ชันที่ทำให้ลูกบอลชนกันใช้การตรวจชนแบบ n^2 และสะท้อนเฉพาะความเร็วเมื่อชนกัน
    • ไม่ได้จำลองปฏิสัมพันธ์ทางฟิสิกส์จริง
    • เพื่อให้ดูง่าย จึงตรึงตำแหน่ง z ให้เป็น 2D
  • ยังสร้างตัวอย่างที่ลูกบอลพยายามกลับไปยังตำแหน่งเริ่มต้นแบบสุ่ม และเมื่อใช้แรงสัมผัสดึงลูกบอลเข้ามา จะเกิดเอฟเฟกต์เหมือนดึงชิ้นส่วนออกจากฟองน้ำ

ภาพและ point cloud 3D

  • การทดลองถัดมาคือการแมปพิกเซลของภาพเป็นจุดบนระนาบ 2D แล้ววาดแต่ละจุดด้วย box-shadow
  • อ่านตำแหน่งและสีของพิกเซลภาพแล้วเก็บไว้ใน state.particles โดยใช้แต่ละพิกเซลเป็นอนุภาคหนึ่งตัว
  • ซอร์สอยู่ใน ตัวอย่าง CodeSandbox
  • ตัวอย่างนี้เรนเดอร์ box-shadow หลายพันรายการในพื้นที่ 3D และเมื่อลากก็โต้ตอบได้เหมือนทำให้ภาพแตกกระจาย
  • เวอร์ชัน live แบบหมุนอัตโนมัติให้ใช้งานผ่าน /box-shadow/v3?width=80&size=5&autoRotate=1 พร้อมคำเตือนเรื่องการใช้แบตเตอรี่เมื่อรัน
  • ตัวอย่างที่มีจำนวนสูงกว่านั้นอยู่ที่ประมาณ 12,000 box-shadow และในระดับนี้เริ่มเห็นอาการกระตุก
  • บน M1 รองรับ box-shadow ได้จำนวนมากมาก แต่เดสก์ท็อป, iPhone และ Android รุ่นเก่าไม่สามารถรับระดับเดียวกันได้

วางจุดบนผิวคิวบ์และทรงกลม

  • มีการทดลองต่อด้วยการฉายจุดให้กระจายสม่ำเสมอบนผิว mesh
  • คิวบ์ทำโดยวางจุดตามแต่ละหน้า
    • สมมติว่าทุกด้านมีความยาวเท่ากัน
    • วนผ่านแต่ละหน้าแล้วเติมจุดตามขนาดที่กำหนด
    • เพิ่มจำนวนจุดเพื่อเพิ่มความละเอียดของคิวบ์ได้
  • ตัวอย่างคิวบ์โต้ตอบได้ด้วยการแตะ และเพิ่มแสงเล็ก ๆ ที่ตามตำแหน่งเมาส์เพื่อเพิ่มมิติความลึก
  • การคำนวณแสงไม่ถูกต้องแม่นยำ และใช้ “magic constants” หลายค่า
  • ฟังก์ชันแมปอนุภาคของคิวบ์และตัวช่วยคณิตศาสตร์หลายตัวได้มาด้วย gypity
    • ผลลัพธ์แรกเป็นการกระจายแบบสุ่ม
    • ความพยายามครั้งที่สองจึงได้การวางจุดสม่ำเสมอบนผิวคิวบ์
  • การกระจายสม่ำเสมอบนผิวทรงกลมซับซ้อนกว่า จึงใช้วิธี spiral discretization
    • แนวคิดคือวางจุดให้สม่ำเสมอบนเส้นที่พันรอบทรงกลมจากล่างขึ้นบน
    • เปรียบได้กับเชือกที่พันรอบลูกบอล
  • ใช้การแมปเชิงเส้นนี้เชื่อมกับอาร์เรย์ค่าความถี่ของเพลงเพื่อสร้างแอนิเมชันด้วย
  • จุดต่าง ๆ ใช้แอนิเมชันพาเลตแบบไล่สีที่ interpolate ตามเวลา และส่วนนี้ยากกว่างานก่อนหน้า
  • ตัวอย่างทรงกลมที่มีจำนวนรอบน้อยแสดงให้เห็นว่าเกลียวประมาณการกระจายสม่ำเสมอได้ แต่ถ้าจำนวนรอบไม่พอ ภาพจะดูแตก

ray tracer ด้วย box-shadow

  • ความคิดที่ว่าถ้าวาดสามเหลี่ยมด้วย box-shadow ได้ ก็แทบจะสร้าง CGI ได้ทุกอย่าง นำไปสู่การทดลอง ray tracing
  • เป้าหมายคือการวาดฉากด้วย div เดียวและ box-shadow เท่านั้น
  • ตัวอย่างหลังจากนั้นใช้ทั้งการเรนเดอร์ live ความละเอียดต่ำและการเรนเดอร์ภาพความละเอียดสูง โดยมีบางตัวอย่างที่ไม่จำกัดความละเอียด จึงต้องระวังเมื่อรัน
  • ray tracer และ ray marcher เป็นวิธีสร้างภาพที่แม่นยำแต่ช้า และใช้กันทั่ววงการ CGI
  • ที่นี่ใช้ tracer แบบใช้ CPU แทน GPU
    • เพราะถ้าใช้ GPU จุดประสงค์ของการทดลองจะอ่อนลง และการทำงานก็ยากขึ้นด้วย
  • tracer แรกถูกทำให้เรนเดอร์เฉพาะทรงกลม และ data model ของฉากประกอบด้วยกล้อง ทรงกลมหลายลูก และข้อมูลวัสดุ
  • โค้ดเริ่มต้นที่ได้จาก gypity ทำงานแบบเรียลไทม์ได้ แต่มีปัญหา
    • ในการปรับแต่ง sampling ขาดโค้ดที่บวก bias coefficient ที่จำเป็น
    • มีคอมเมนต์ที่เกี่ยวข้องอยู่ แต่ไม่มีบรรทัดจริง และแก้ไขหลังจากไปค้นหาเทคนิค sampling

progressive rendering และ Three.js

  • หลังจากนั้นโค้ดเปลี่ยนเป็นโครงสร้าง progressive rendering
  • progressive rendering แบ่งการคำนวณ ray จำนวนมากออกไปทำหลายเฟรม เพื่อแสดงกระบวนการที่ภาพเรนเดอร์ค่อย ๆ เข้าใกล้ “ground truth”
  • โครงสร้างนี้เข้ากันได้ดีกับฟังก์ชันขยับกล้องแบบโต้ตอบ
  • กล้องและ orbit controls ใช้ไลบรารี Three.js
    • เพื่อไม่ต้องทำคณิตศาสตร์เมทริกซ์และการรองรับมือถือเอง
  • เวอร์ชันนี้เรนเดอร์ได้เฉพาะทรงกลม และทุกองค์ประกอบในฉากเป็นทรงกลมที่ถูกสเกลในระดับหนึ่ง
  • ตัวอย่าง โดยพื้นฐานรันที่ประมาณ มากกว่า 6% เล็กน้อย ของความละเอียดเต็ม
  • ยิ่งอยู่ห่างจากหน้าจอ ฉากความละเอียดต่ำยิ่งดูคมขึ้น และเมื่อมองใกล้ ความละเอียดที่ไม่พอก็ชัดเจนขึ้น
  • สามารถปรับ resolution scale, pixelSize, จำนวน ray bounce และจำนวน sample สูงสุดได้ด้วย query parameter
  • เพิ่มความละเอียดและ sample แล้วผลลัพธ์ดีขึ้น แต่จะช้ามาก

การทำงานของ tracer และคอขวดด้านประสิทธิภาพ

  • การเรนเดอร์ใช้วิธีมาตรฐานคือยิง ray จากมุมมองกล้องสำหรับแต่ละพิกเซล คำนวณสี แล้วเฉลี่ยข้ามเฟรม
  • เนื่องจาก tracer ใช้ Three.js จึงมีการสร้างอ็อบเจ็กต์ใหม่จำนวนมากและกลายเป็น garbage อย่างรวดเร็ว
  • มีการลองนำอ็อบเจ็กต์กลับมาใช้ซ้ำบางส่วน แต่ถ้าจะรีดประสิทธิภาพให้ถึงที่สุด การไม่ใช้ Three.js น่าจะดีกว่า
  • ตาม profiler แล้ว garbage collection ไม่ได้สร้างความแตกต่างมากนัก จึงยังคงใช้ Three.js ต่อไป
  • แนวคิดพื้นฐานของ tracer คือ ray กระทบวัตถุในฉากแล้วเด้งไปจนกว่าจะถึงแสง และคืนค่าสีตามคุณสมบัติของวัตถุและแสง
  • เหตุผลที่ต้องยิง ray จำนวนมากคือ บาง ray ไม่ถึงแหล่งกำเนิดแสง และบาง ray ถึง
  • tracer นี้ใช้โมเดลแสงแบบง่าย
    • ไม่มี BRDF ที่ถูกต้องตามฟิสิกส์
    • ไม่มี texture
    • ไม่มี subsurface scattering
    • ใช้แสง diffuse แบบง่ายและ specular reflection
  • แหล่งเรียนรู้ ray tracing ดูได้ที่ Ray Tracing in One Weekend
  • ต่อมาสามารถเรนเดอร์ plane ได้ และหลังจากแก้ข้อผิดพลาดที่ bit manipulation กับค่า floating point ก็เรนเดอร์สี่เหลี่ยมแสงได้ด้วย

ทำงานขนานด้วย Web Workers

  • การปรับปรุงประสิทธิภาพเลือกใช้มัลติเธรดด้วย Web Workers แทนการแก้ปัญหา GC ของ Three.js
  • ray tracing เหมาะกับการทำงานขนาน เพราะแต่ละการคำนวณคืนผลลัพธ์เดียวและไม่มี side effect
  • worker manager สร้าง pool ของ worker และมีเมธอด render, updateScene เพื่อให้เปลี่ยนฉากระหว่างรันไทม์ได้
  • โค้ด worker เป็นรูปแบบที่คัดลอก tracer เดิมมา และต้องมี data marshaling เพื่อเปลี่ยนฉาก
  • เพื่อลด overhead ของ postMessage worker จะเรนเดอร์ทั้งเฟรม ไม่ใช่พิกเซลเดียว
  • ตัวอย่างเต็มหน้าจอ ทำงานเร็วกว่าเดิมมาก
  • ข้อเสียคือเมื่อกล้องหรือฉากเปลี่ยน ผลลัพธ์ ray ที่คำนวณไว้เดิมทั้งหมดจะใช้ไม่ได้ และหน้าจออาจดูดำ
  • วิธีแก้คือเมื่อเกิด input event ให้ตั้งแฟล็ก isDirty แล้วให้ update loop ตัดสินใจว่าจะล้างเฟรมหรือไม่
  • มีกรณีที่ worker อาจส่งเฟรมที่คำนวณจากข้อมูลฉากเก่ามาทันทีหลังอัปเดตฉาก
    • ใส่ timestamp หรือ scene id ใน postMessage เพื่อทิ้งได้
    • ในการทดลองปล่อยไว้ เพราะข้อมูล ray ที่ผิดของหนึ่งเฟรมจะถูกเฉลี่ยไปในไม่ช้า
  • สุดท้าย ray tracing ด้วย box-shadow ทำงานได้เพียงพอ และซอร์สอยู่ที่ CodeSandbox
  • สถิติประสิทธิภาพแสดงค่าประมาณจำนวน ray รวม จำนวน sample และความละเอียดการเรนเดอร์
  • sample เริ่มต้นหยุดที่ 1200 แต่ปรับตั้งได้
  • มีโมเดลแสงทางเลือกที่เร็วกว่าแต่แม่นยำน้อยกว่าให้ toggle ได้
  • ข้อมูลฉากเป็น JSON จึงแก้ไขเองได้ง่าย

สรุป: ทำได้ แต่ไม่แนะนำให้ใช้จริง

  • ด้วย box-shadow ของ div เดียว สามารถวาดได้ตั้งแต่การจำลองลูกบอล point cloud อนุภาคจากภาพ ผิวคิวบ์·ทรงกลม ไปจนถึงฉาก ray tracing
  • อย่างไรก็ตาม การทดลองทั้งหมดใกล้เคียงกับวิธี “ที่ไม่ควรทำ” ซึ่งไม่มีกรณีใช้งานจริง
  • ยังมีพื้นที่ให้ปรับปรุงได้ เช่น เพิ่มการโหลด triangle mesh, acceleration structure และโมเดลแสงที่แม่นยำขึ้น
  • gypity เคยตอบในตอนแรกว่า box-shadow ray tracer เป็นไปไม่ได้ แต่ก็มีตัวอย่างจริงถูกสร้างขึ้นแล้ว
  • CSS ไม่ได้ตรงไปตรงมาเสมอไป แต่บางครั้งก็มีความเป็นไปได้แปลก ๆ ที่เห็นได้ชัดว่าทำงานได้

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

 
GN⁺ 2024-07-22
ความคิดเห็นใน Hacker News
  • ถ้าใช้สีโปร่งใสจะช้าลง เพราะทำให้ การประมวลผลแบบแบตช์ของการวาดด้วย GPU ถูกจำกัด
    การวาดแบบทึบแสงสามารถใช้ depth buffer เพื่อสลับลำดับได้ตามใจ และเพิ่มการประมวลผลแบบแบตช์ให้มากที่สุดได้ แต่กรณีโปร่งใสต้องวาดตาม ลำดับแบบ painter เพื่อให้การ blend ถูกต้อง

    • ผมว่ามันซับซ้อนกว่านั้น เว็บเบราว์เซอร์ก็ใช้ GPU rendering แต่ไม่ใช่ game engine และถ้าวาดวัตถุทั้งหมดบนหน้าจอใหม่ทุกเฟรม หน้าเว็บที่ใหญ่และซับซ้อนก็จะกระตุกได้ง่าย
      โดยเฉพาะ Chromium จะพยายามลดจำนวน layer ทั้งหมดให้เหลือน้อยที่สุด แล้ว render แต่ละ layer เป็น pixel map จากนั้นจึง composite layer ที่มองเห็นในแต่ละเฟรมให้เป็นภาพสุดท้าย ในความเป็นจริงมีหลาย layer ที่พิกเซลไม่ได้เปลี่ยน แค่ตำแหน่งขยับ ดังนั้นแต่ละเฟรมแค่ composite ก็พอ ไม่ต้อง rasterize ใหม่
      ถ้ามี box-shadow แบบไม่มีความโปร่งใสจำนวนมาก Chromium อาจ rasterize ทั้งหมดเป็น layer เดียวได้ในครั้งเดียว แต่ถ้ามี box-shadow แบบโปร่งใสจำนวนมาก ก็อาจทำแต่ละอันเป็น layer แยก ในกรณีนี้อาจไม่มีประสิทธิภาพ แต่ถ้า box-shadow กึ่งโปร่งใสเหล่านั้นต้องเคลื่อนไหวแยกกันบนหน้าเว็บ ก็เป็นวิธีที่จำเป็นเช่นกัน
    • บางกรณี ลำดับแบบ reverse painter ดีกว่าลำดับแบบ painter เพราะสามารถข้ามวัตถุที่ถูกบังทั้งหมดได้
      เริ่มจาก buffer ที่โปร่งใสหมด (α=0.0) แล้ววนผ่านแต่ละพื้นผิวจากหน้าไปหลัง สำหรับแต่ละพิกเซลก็ blend พิกเซลใหม่เข้ากับ buffer เดิมตามสัดส่วน 1.0-buffer.α ก็พอ ถ้า buffer.α == 1.0 ก็ข้ามได้ทั้งหมดเหมือน depth buffer
      อย่างไรก็ตาม คณิตศาสตร์ในกรณีที่มีวัตถุโปร่งใสอีกชิ้นอยู่หลังวัตถุโปร่งใสต้องตรวจสอบอีกครั้ง และกรณีที่พื้นผิวซ้อนกันเป็นวงวนหรือทะลุผ่านกันก็ค่อนข้างยุ่งยาก
    • GPU ก็ไม่ชอบ overdraw ดังนั้นควรหลีกเลี่ยงการซ้อนองค์ประกอบโปร่งใสหลายชั้น
      นี่ก็เป็นเหตุผลที่โดยทั่วไปการวาดสามเหลี่ยมให้มากขึ้นมักดีกว่าการใช้ texture โปร่งใส
    • ขั้นตอนการ sort เองปกติไม่ใช่ปัญหาใหญ่มาก หลายครั้งก็ต้อง sort อยู่แล้วก่อนส่ง draw call ต้นทุนจริงมาจาก overdraw
      การ render แบบทึบแสงสามารถวาดจากหน้าไปหลังได้ จึง render เฉพาะสิ่งที่มองเห็นจริงใน framebuffer สุดท้าย และจำนวนพิกเซลหลัง depth pass จะแปรตามขนาด framebuffer
      การ render แบบโปร่งใสต้องวาดจากหลังมาหน้า และต้อง render ฉากจำนวนมากที่ภายหลังจะถูก polygon อื่นบังบางส่วน ดังนั้นจำนวนพิกเซลที่ผ่าน shader pipeline อาจเพิ่มขึ้นจนแปรตามขนาดของ mesh
      ถ้าเป็นองค์ประกอบที่ไม่ซ้อนทับกัน ความโปร่งใสน่าจะทำให้ช้าลงแทบไม่มีนัยสำคัญ เพราะอย่างไรก็ต้องแตะแต่ละพิกเซลหนึ่งครั้งอยู่แล้ว สิ่งที่เปลี่ยนไปมีแค่สูตรของ shader
    • ยังมีอีกเรื่องที่ต้องพิจารณา โดยเฉพาะบนอุปกรณ์มือถือ memory bandwidth อาจเป็นคอขวดได้
      ถ้าการวาดแบบทึบแสงอยู่ทับการวาดอื่น ในกรณีดีที่สุดสามารถตัดงานวาดที่ซ้อนกันทั้งหมดออกได้ และแม้ในกรณีแย่ที่สุดก็ใช้ bandwidth เท่ากับการวาดแต่ละครั้งเท่านั้น
      ในกรณีความโปร่งใส ถ้าไม่สามารถรวมการคำนวณเข้าด้วยกันได้ ก็ต้องอ่านพื้นที่ที่ซ้อนกันทั้งหมดอีกครั้ง ดังนั้นทุกครั้งที่วาดแบบโปร่งใส bitmap ที่มีขนาดอย่างน้อยสองเท่าของ framebuffer สุดท้ายจะต้องวิ่งผ่าน memory bus
      ถ้าคิดว่าอุปกรณ์มือถือจำนวนมากเคยมี memory bandwidth ไม่พอแม้กระทั่งสำหรับ blit ทั้งหน้าจอสองครั้งภายในเวลาที่ต้องรักษา 60fps นี่ก็เป็นปัญหาใหญ่ทีเดียว
  • เป็นการสำรวจที่สนุกจริง ๆ
    คำว่า layering เป็นคำสำคัญจริง ๆ โปรเจกต์เงาข้อความที่ผมทำเมื่อ 14 ปีก่อน ซึ่งให้เอฟเฟกต์ที่ไร้สาระแต่บางทีก็ดูเท่ ก็มี layering เป็นแกนหลักเช่นกัน: https://paulirish.github.io/mothereffingtextshadow/

    • บทความนี้ก็ดูเหมือนมีหลาย layer และจริง ๆ แล้วน่าจะไม่ได้เป็นแค่บทความเกี่ยวกับ box-shadow
  • เพิ่งมารู้สึกอายตอนย่อหน้าสุดท้ายว่าคำว่า gypity หมายถึง ChatGPT

    • เป็นสำนวนที่ใช้ในช่อง YouTube ของ Primeagen
    • เห็นแบบนี้เลยรู้ว่าคำนั้นไม่ใช่มุกวงในที่เพื่อนร่วมงานสร้างขึ้น แต่กำลังกลายเป็น meme
    • ผมนึกว่าเป็นเครื่องมือฝั่ง client-side อะไรสักอย่างที่เกี่ยวกับ gyp
  • แฮ็กแบบเก่า ๆ ที่ ไม่ค่อยมีประโยชน์จริง นี่ยินดีต้อนรับเต็มที่ แต่ก็ควรจำไว้ว่ามี canvas อยู่แล้ว และงานแบบนี้ canvas ทำได้ง่ายกว่า เร็วกว่า และดีกว่า

    • แต่มูลค่าด้านตลกจะน้อยกว่า เหตุผลที่การทำสิ่งนี้ด้วย box-shadow ล้วน ๆ ตลก ก็เพราะมันไม่ practical ขนาดนั้น
    • Canvas ทำให้ต้องเสียหลายอย่าง โดยเฉพาะด้าน accessibility
    • Canvas ดีกว่าจริง แต่เป็นกรณีของการทำสิ่งที่อยู่ภายในกล่องขนาดคงที่
    • แน่นอนว่าก็เหมือนกับการบอกว่าเรามีคีย์บอร์ดอยู่แล้ว เลยไม่จำเป็นต้องทำเพลงด้วยฟลอปปีดไดรฟ์หรือไก่ยาง
  • visualization เพลงนี่เจ๋งจริง ๆ คิดถึงยุค Winamp เก่า ๆ มาก ตอนที่เปิดเพลงแล้วเปิด visualization เต็มหน้าจอ
    อยากให้เครื่องเล่นเพลงสตรีมมิงสมัยนี้มีฟีเจอร์แบบนี้บ้าง

    • ผมยังใช้ Winamp หรือให้ถูกคือ WACUP พร้อม visualization ของ Milkdrop แทบทุกวัน
      แต่ก็จริงที่เครื่องเล่นเพลงสตรีมมิงเป็นซอฟต์แวร์ที่มีแต่ฟีเจอร์พื้นฐานจริง ๆ
  • สุดท้าย Firefox กับ Chrome ก็ยัง render 1px box-shadow ต่างกันที่การซูมเบราว์เซอร์ 150%
    คงต้องฝากความหวังไว้กับ Baseline 2025

  • อยากรู้ว่ามีแหล่งอ้างอิงให้อ่านเพิ่มเกี่ยวกับ “แฮ็กทางคณิตศาสตร์สำหรับวาดกล่องมนที่คนทำ UI ชอบให้ถูกมาก ๆ” ไหม

  • เป็นแฮ็กประเภทที่ผมชอบเลย
    แทบจะตรงข้ามกับบทความของ Josh Comeau ที่เคยอ่านในหัวข้อนี้: https://www.google.com/search?q=josh+comeau+shadows

  • บทความยอดเยี่ยมที่อาจเป็นบทความดีที่สุดที่ผมอ่านปีนี้ กลับจบด้วย your welcome แทนที่จะเป็น you're
    ต้องรีบแก้แล้วล่ะ หรือไม่ก็ผมอาจไม่เข้าใจมุก

  • ตลอด 30 ปีที่ผ่านมา ผมเขียนโปรแกรมได้ดีขึ้น แต่แทบไม่ได้ทำ graphics เพราะไม่ชอบเกม
    ตอนนี้มองว่านั่นเป็นความผิดพลาดมหาศาล และพยายามตามให้ทันมานานกว่าหนึ่งปีแล้ว แต่มันยากจริง ๆ

    • ผมเริ่มจากทำเกมก่อน แล้วค่อยย้ายไปทำ CRUD หรือโปรแกรม Oracle Forms ง่าย ๆ และได้เงินเดือนสามเท่าของตอนเป็นโปรแกรมเมอร์เกมแทบจะในชั่วข้ามคืน