2 คะแนน โดย GN⁺ 2024-10-12 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • นี่คือการสร้างระบบที่ทำให้ Bad Apple!! เล่นแบบเรียลไทม์บนเอนจินที่ช้าของ Minecraft ด้วย ความละเอียด 512×384, 20fps และภาพระดับเทา เหมือนต้นฉบับ
  • แกนสำคัญคือให้ structure block ในโหมด LOAD แทนที่ตัวเองด้วยโครงสร้างของเฟรมถัดไป และใช้สัญญาณนาฬิกาที่เหลื่อมเฟสกันเพื่อข้ามขีดจำกัด 10Hz ของ redstone
  • หากใช้หนึ่งบล็อกแทนหนึ่งพิกเซล จะต้องอัปเดตทั้งหมด 768 ชังก์ที่ 20Hz จึงลดภาระโดยใช้ custom texture, model และ blockstate ใน resource pack เพื่อให้หนึ่งบล็อกแสดงข้อมูลบนหน้าจอได้มากขึ้น
  • คอขวดไม่ได้อยู่ที่ redstone หรือแสง แต่เป็น setBlock และการจัดการอีเวนต์ จึงลดปริมาณการอัปเดตด้วย delta coding และการแทนที่สไปรต์ 4×4 ที่เปลี่ยนบ่อย
  • คุณภาพสุดท้ายถูกปรับด้วยภาพระดับเทา 6 สี, blue-noise dithering และการชดเชยนอยส์จากการบีบอัดแบบสูญเสียข้อมูล แต่ปัญหาการลดต้นฉบับ 30fps ลงมาเป็น 20fps ยังแก้ได้ไม่หมด

เงื่อนไขเพื่อให้เล่นได้ใกล้เคียงต้นฉบับ

  • เป้าหมายคือเล่น Bad Apple!! ใน Minecraft ให้ใกล้ต้นฉบับมากที่สุด
    • วิดีโอเล่นที่ 20fps
    • ความละเอียดเป็น 512×384 เท่ากับแอนิเมชันต้นฉบับ
    • ไม่ใช่แค่ขาวดำ แต่เป็น ภาพระดับเทา
    • บน CPU และ GPU สมัยใหม่สามารถดูได้ที่ 20fps จริงโดยไม่ต้องอัดวิดีโอแล้วเร่งความเร็ว
    • ไม่ใช้ command block
  • ทางลัดที่ง่ายเกินไปถูกตัดออกจากเงื่อนไขการทำงาน
    • ไม่ใช้ม็อดเป็นวิธีหลักในการสร้าง ยกเว้นม็อดเพิ่มประสิทธิภาพสำหรับทดสอบบนเครื่องสเปกต่ำ
    • ไม่ใช้ command block, /setblock, หรือ datapack
    • ไม่ใช้ animated texture
  • บนอุปกรณ์สเปกต่ำอาจต้องใช้ VulkanMod หรือ Sodium
    • ควรหลีกเลี่ยง C2ME เพราะระบบ autosave ทำให้ประสิทธิภาพตก

ข้อจำกัดที่งานก่อนหน้าเจอ

  • งานสร้าง Bad Apple!! ก่อนหน้านี้ส่วนใหญ่ยังติดอยู่กับหน้าจอเล็กหรือการเรนเดอร์ที่ช้า
    • งานของ catlord5 ทำได้ถึง 512×384 และ 30fps แต่เรนเดอร์ช้ากว่าปกติราว 40 เท่า
    • งานเรียลไทม์ที่ใช้ redstone บางชิ้นทำได้เพียง 5fps หรือความละเอียดต่ำ
  • Minecraft ไม่ได้ช้าแค่เอนจินจำลองสถานการณ์ แต่เอนจินเรนเดอร์ก็ช้าเช่นกัน และยิ่งมีจำนวนชังก์มากภาระก็ยิ่งสูง
    • ชังก์ขนาด 16×16×16 มีต้นทุนการเรนเดอร์ใหม่สูง ไม่ว่าข้างในจะมีอะไร
    • การลดจำนวนชังก์ที่หน้าจอพาดผ่านจึงสำคัญมาก
  • redstone มีปัญหาตรงที่ตัวสร้างนาฬิกาแบบใช้งานจริงมักปล่อยสัญญาณที่ 10Hz จึงเอามาใช้กับการเล่น 20fps ตรง ๆ ได้ยาก
    • redstone dust เป็นองค์ประกอบหลักที่ไม่มีดีเลย์ แต่กินประสิทธิภาพสูง

การทดลองวิธีเก็บข้อมูล

  • วิธี hopper line เป็นการเก็บข้อมูลแบบคลาสสิก โดยดึงไอเท็มจากกล่องผ่าน hopper แล้วอ่านด้วย comparator
    • hopper ส่งไอเท็มได้ทุก 0.4 วินาที จึงมีเฟรมเรตสูงสุดเพียง 2.5fps
    • ถ้าจะไปถึง 20fps ต้องใช้ hopper หลายชุดขนานกัน และจะติดปัญหาการจัดวางกับต้นทุนการจำลอง
  • ยังลองใช้ jukebox กับ music disc เพื่ออ่านค่า 1 ถึง 15 และเก็บข้อมูลได้ เกือบ 4 บิต
    • ถ้ากระจายบิตไปตามแกนเวลา ก็อาจเล็ง 20fps ได้ด้วย hopper 2 ตัว
    • แต่ลอจิก redstone และ dust ที่ต้องใช้สำหรับ bit shift ช้าเกินไป จนแม้แต่ต้นแบบก็ยังประสิทธิภาพไม่พอ
  • repeater delay line เป็นวิธีง่าย ๆ ที่ให้แต่ละพิกเซลมีเส้น repeater ของตัวเองแล้วปล่อยค่าตามเวลา
    • สามารถปูกระเบื้องแบบ 1×1 พิกเซลได้
    • แต่แม้งานเดิมที่เล็กกว่าเป้าหมายมากก็ยังต้องเร่งอีก 20 เท่า จึงไม่เพียงพอ

ส่งต่อเฟรมด้วย structure block

  • structure block (structure block) สามารถบันทึกพื้นที่ด้วย SAVE และโหลดไปยังตำแหน่งอื่นด้วย LOAD
    • แม้จะหาไม่ได้ในโหมด survival แต่ก็ไม่ได้แทนทุกอย่างด้วยคำสั่งเดียวแบบ command block
    • สามารถเปิดใช้งานด้วยสัญญาณ redstone ได้
  • หัวใจสำคัญคือ structure block ในโหมด LOAD สามารถโหลดพื้นที่ที่ ทับซ้อนกับตัวเอง ได้
    • ถ้าแทน structure block ปัจจุบันด้วย structure block ตัวถัดไป ก็จะกลายเป็นระบบที่โหลดเฟรมถัดไปทุกครั้งที่ถูกกระตุ้น
  • แต่ถ้าแทนที่ตรง ๆ structure block ใหม่จะตรวจจับสัญญาณ redstone รอบข้างทันทีและเปิดตัวเองแบบเรียกซ้ำ
    • การเรียกซ้ำนี้จะดำเนินต่อไปจนชนฮาร์ดลิมิตของ Minecraft
    • กระบวนการปลดพลังงานของ redstone dust ก็ยังไม่ทันจบ ทำให้ค้างสถานะพลังงานผิดปกติไว้
  • จึงใส่ดีเลย์ 1 redstone tick ด้วย repeater เพื่อกันการเปิดใช้งานแบบเรียกซ้ำ
    • โครงสร้างต้องถูกสร้างในสถานะ /tick freeze เพื่อบันทึกตอน repeater ยังปิดอยู่
    • หลังจากโหลดแล้วจึงค่อยขยับไปยังโครงสร้างถัดไปหนึ่ง redstone tick ให้หลัง

การจัดการ tick เพื่อให้ได้ 20fps

  • Minecraft มีทั้ง game tick และ redstone tick
    • เอนจินเกมคำนวณฟิสิกส์ใหม่ที่ 20Hz
    • องค์ประกอบ redstone โดยทั่วไปจะตั้งเวลาอัปเดตเป็นหน่วย 0.1 วินาที หรือ 10Hz
  • แต่อีเวนต์จริงถูกประมวลผลทุก 0.05 วินาที และจังหวะอินพุตของผู้ใช้ก็อาจทำให้การตอบสนองของ redstone เลื่อนไปตามเฟสนั้นได้
  • เพื่อให้ได้ 20fps จึงใช้โครงสร้าง 4 ชุด
    • โครงสร้างสีแดงกับสีเหลืองเป็นนาฬิกา 10Hz ชุดหนึ่ง
    • โครงสร้างสีน้ำเงินกับสีเขียวเป็นนาฬิกา 10Hz อีกชุดหนึ่ง
    • ถ้าเริ่มสองนาฬิกานี้ให้เหลื่อมเฟสกัน สีทั้งสี่จะสลับแสดงที่ตำแหน่งเดิมที่ 20Hz
  • เพื่อเริ่มนาฬิกาทั้งสองให้มีเฟสต่างกันอย่างเสถียร จึงอาศัยบั๊กเก่าที่เมื่อ piston ดัน redstone block ผ่านอินพุตผู้ใช้โดยตรงจะใช้เวลา 3 game tick

เทคนิค resource pack เพื่อลดจำนวนชังก์

  • ถ้าใช้หนึ่งบล็อกแทนหนึ่งพิกเซล หน้าจอ 512×384 จะกินพื้นที่ 24 ชังก์แนวตั้ง และ 32 ชังก์แนวนอน
    • รวมเป็น 768 ชังก์ ที่ต้องอัปเดตต่อเนื่องที่ 20Hz
    • ยังไปชนกับระยะเรนเดอร์สูงสุด 32 ชังก์ของ vanilla ด้วย จึงไม่สมจริง
  • จึงใช้ custom texture ใน resource pack เพื่อเปลี่ยนพื้นผิวของหลายบล็อก และใส่ subpixel หลายจุดไว้ในบล็อกเดียว
    • บล็อกแปรผัน 16 แบบเทียบเท่ากับ 4 บิต
    • ถ้าใช้ 256 บล็อกกับสีเพิ่ม ก็แสดงภาพระดับเทาได้แทนที่จะเป็นแค่ขาวดำ
  • วิธีนี้ลดความละเอียดในระดับบล็อกลง 4 เท่า เหลือการแสดงผลที่ 256×192 บล็อก
    • จำนวนชังก์ที่ต้องอัปเดตลดลงเหลือ 192 ชังก์
    • แต่สำหรับการอัปเดต 20Hz ก็ยังเป็นภาระหนักอยู่ดี

คิวเรนเดอร์และ delta coding

  • เอนจินเรนเดอร์ของ Minecraft ให้ความสำคัญกับการอัปเดตชังก์รอบตัวผู้เล่นก่อน
    • มีหลายเธรดสร้างชังก์พร้อมกัน และจะดึงจากคิวอัปเดตโดยเริ่มจากชังก์ที่ใกล้ผู้เล่น
    • หากมีชังก์ใกล้ ๆ N ชังก์ที่อัปเดตต่อเนื่องที่ 20Hz ก็อาจทำให้ระบบมัวแต่จัดการชังก์พวกนั้นและไม่เรนเดอร์ชังก์ที่เหลือ
  • จากการตรวจด้วย Spark พบว่าคอขวดอยู่ที่การอัปเดตทั่วไปมากกว่า redstone หรือแสง
    • โดยเฉพาะ setBlock และ event handler
  • ทางแก้คือ ลดจำนวนการอัปเดต จึงใช้ delta coding ที่อัปเดตเฉพาะบล็อกที่เปลี่ยนไปจากเฟรมก่อน
    • เพราะเฟรมส่วนใหญ่ไม่ได้เปลี่ยนมาก จึงมีโอกาสเพิ่มประสิทธิภาพในทางทฤษฎี
  • structure block โหลดได้ครั้งละสูงสุด 48×48×48 บล็อก จึงแบ่งหน้าจอเป็น 6×4 ซับสกรีนขนาด 48×48
    • ดึงเฟรมออกมาด้วย ffmpeg
    • อ่านภาพด้วย Python Pillow
    • สร้างไฟล์ NBT ด้วย nbtlib
  • ต้นแบบแรกใช้เวลารันหนึ่งรอบประมาณ 7 นาที และต้องใช้ /tick freeze, ปุ่ม 24 ปุ่ม และ /tick unfreeze แต่ก็พิสูจน์ว่าใช้งานได้
    • ถึงอย่างนั้น delta coding อย่างเดียวก็ยังไม่เร็วพอ

การเพิ่มประสิทธิภาพด้วย model และ blockstate

  • โมเดลของ Minecraft นิยามรูปทรงบล็อกด้วย cuboid และกำหนดพิกัดได้เกินช่วง (0,0,0) ถึง (16,16,16) ไปจนถึง -16 ถึง 32
    • ถ้าตั้งอย่างเหมาะสม บล็อกเดียวจะถูกเรนเดอร์ให้เหมือนมีขนาดใหญ่ขึ้นได้ถึง 3 เท่า และแทนพื้นที่ 9 บล็อกได้
    • แต่จำนวนบล็อกที่พอจะรองรับทุกชุดค่าผสมไม่พอ จึงใช้ได้เฉพาะกรณีที่พบได้บ่อย เช่น พื้นที่ 6×6 สีดำสนิท
  • จากบล็อกที่ใช้งานได้ราว 600 แบบ มี 256 แบบถูกใช้กับการแสดง subpixel พื้นฐาน และนำบล็อกที่เหลือบางส่วนมาใช้เพื่อเพิ่มประสิทธิภาพ
  • แนวทางสุดท้ายคือแบ่งหน้าจอเป็นเซลล์บล็อก 2×2 และมองสไปรต์ 4×4 พิกเซลของแต่ละเซลล์เป็นตัวเลือกสำหรับแทนด้วยบล็อกเดียว
    • คำนวณความต่างระหว่างสองเฟรมที่ต่อเนื่องกัน
    • เพิ่มคะแนนให้เวอร์ชันก่อนและหลังของเซลล์ที่เปลี่ยน
    • ในฉากที่เปลี่ยนเร็ว เซลล์ที่มีพิกเซลเปลี่ยนมากจะได้คะแนนสูงกว่า
    • จากนั้นจัดสรรบล็อกที่มีอยู่ให้กับสไปรต์ที่ได้คะแนนสูงก่อน
  • เพื่อให้เกินข้อจำกัดจำนวนบล็อกพื้นฐาน จึงไปสำรวจ blockstates
    • บล็อกอย่าง oak_log เลือกโมเดลต่างกันได้ตามพร็อพเพอร์ตี
    • บล็อกอย่าง grindstone ใช้ชุดพร็อพเพอร์ตีหลายตัวเป็นคีย์ได้
  • เมื่อนำ blockstate variant จากแอสเซ็ตพื้นฐานมาแยกออก แล้วคัดพร็อพเพอร์ตีที่ควบคุมไม่ได้ทิ้ง จำนวนโมเดลที่เข้าถึงได้ก็เพิ่มจากประมาณ 600 เป็น 1700
    • จำนวนสีเพิ่มเป็น 6 สี
    • จำนวนบล็อกที่ใช้เพิ่มประสิทธิภาพได้เพิ่มเป็น 400 แบบ

เสียงและอุปกรณ์เริ่มเล่น

  • เพลงถูกจัดการด้วย resource pack โดยเปลี่ยนเสียงของ music disc
    • เวลาการเล่นของแผ่นจะคงเดิมแม้เปลี่ยนเสียงก็ตาม
    • จึงเลือกแผ่น Relic ที่มีความยาวใกล้กับ “Bad Apple!!” มากที่สุด
    • และแก้ assets/minecraft/lang/en_us.json เพื่อให้คำบรรยายในเกมขึ้นว่า “Now Playing: Bad Apple!!”
  • เชื่อมปุ่ม, dropper, hopper และ jukebox เข้าด้วยกัน เพื่อให้กดปุ่มครั้งเดียวแล้วแผ่นถูกใส่เข้า jukebox และเริ่มเล่น
    • เมื่อเล่นจบ hopper จะส่งแผ่นกลับเข้า dropper เพื่อเตรียมเล่นรอบถัดไป
  • แต่เพราะ quasiconnectivity สัญญาณ redstone ที่ jukebox ปล่อยระหว่างเล่นทำให้สถานะของ hopper และ dropper เพี้ยน
    • จึงทำให้ redstone dust จากปุ่มไปอัปเดต dropper ตอนกด
    • แล้วให้ repeater เปิด dropper ซ้ำอีกครั้งหลังผ่านไปหนึ่ง redstone tick เพื่อใส่แผ่นเข้าไป
  • เพื่อส่งสัญญาณจากจุดรับชมไปยังอุปกรณ์หลังจอ จึงสร้าง สายทันที ที่อิง structure block
    • structure block จะโหลด redstone torch ที่มีพลังงานไปยังช่วงถัดไป และใน tick ถัดไปมันจะดับเพราะ redstone block
    • พัลส์นี้จะเปิดใช้งาน structure block ถัดไปและส่งต่อสัญญาณ
    • เนื่องจาก structure block ครอบคลุมได้สูงสุด 48 บล็อก จึงสามารถส่งสัญญาณเริ่มไปยังกริดซับสกรีน 48×48 ได้
  • จากผู้ชมไปถึงอุปกรณ์หลังจอมีระยะราว 150 บล็อก จึงเชื่อมด้วยโครงสร้างแยกที่รีเซ็ตได้
    • มันส่งสัญญาณด้วยการสร้างและลบชุด structure block กับ redstone block แบบลูกโซ่
    • เพราะบันทึกโครงสร้างนี้ด้วยมือใน creative ได้ยาก จึงสร้างไฟล์ structure ด้วยไลบรารี Python
  • กลไกสุดท้ายถูกรวมไว้ใน กล่องขนาด 4×2×3 ที่กดใช้งานได้จากปุ่มด้านนอก

การพรีโปรเซสเฟรมและคุณภาพวิดีโอ

  • งานพรีโปรเซสที่เหลือคือการลดวิดีโอสีเต็มให้เหลือ 6 สี และแปลงวิดีโอ 30fps ให้เป็น 20fps
  • Bad Apple!! ไม่ได้เป็นแค่ขาวดำล้วน แต่ใช้ภาพระดับเทาในหลายฉาก
    • motion blur
    • วัตถุที่มีความสว่างต่างกัน
    • ไล่ระดับสีในฉากเปลี่ยนผ่าน
    • เอฟเฟกต์อย่างไฟ ดวงอาทิตย์ เงา และระลอกน้ำ
  • ถ้าปัดเป็นสีที่ใกล้ที่สุดอย่างเดียวจะเกิด banding
    • dithering ช่วยบรรเทาโดยเปลี่ยนสีคั่นกลางที่แสดงไม่ได้ให้เป็นแพตเทิร์นของสีที่แสดงได้ซึ่งอยู่ติดกัน
  • แต่ dithering แบบ global ที่คุณภาพสูงอาจให้ผลต่างกันมากระหว่างเฟรม
    • สายตามนุษย์จะเห็นความไม่สอดคล้องได้ง่าย
    • และยังสร้างการอัปเดตจำนวนมากเกินกว่าที่ Minecraft จะรับไหว
  • dithering แบบ local เช่น Bayer dithering มีความเสถียรแต่คุณภาพต่ำกว่า ส่วน ordered dithering แบบ blue noise กลายเป็นทางออกตรงกลาง
    • ffmpeg ไม่รองรับ blue-noise dithering
    • จึงครอป blue noise texture ของ Christoph Peters ให้เป็น 512×384 แล้วใช้สคริปต์ Rust ประมวลผล
  • วิดีโอต้นฉบับมาจาก วิดีโอที่อัปโหลดบน Niconico ซึ่งเป็นไฟล์บีบอัดแบบสูญเสียข้อมูลและมีนอยส์
    • นอยส์ในพื้นที่สีดำและสีขาวยิ่งมองเห็นชัดหลังทำ dithering
    • จึงแก้โดยปัดสีที่เกือบดำให้เป็นดำ เกือบขาวให้เป็นขาว และกระจายค่าสีกลางให้ยังคงความต่อเนื่อง
  • ปัญหาการลดจาก 30fps ลงมาเป็น 20fps ยังแก้ไม่ได้สมบูรณ์
    • ถ้าทิ้งทุกเฟรมที่สาม ช่วงการเคลื่อนไหวจะสลับกันไม่เท่ากันระหว่างเฟรมคี่และคู่จนสังเกตได้
    • ปริมาณการอัปเดตก็จะกลายเป็นลวดลายแบบฟันเลื่อย
    • วิดีโอ Bad Apple!! 60fps ออนไลน์จำนวนมากถูกอัปสเกลด้วย AI หรือเครื่องมืออัตโนมัติ และมีอาร์ติแฟกต์เยอะในฉากเปลี่ยนเร็ว

ผลลัพธ์และงานต่อยอด

  • งานสร้างนี้เริ่มจากความละเอียด 48×36 และ 2 สี จากนั้นขยับผ่าน 128×96 กับ 10 สี, 256×192 จนสุดท้ายไปถึง 512×384 และ 6 สี
  • เคยลองเล่นเพลงด้วย note block ด้วย แต่ถ้าจะทำคุณภาพดีจริงก็น่าจะใหญ่พอเป็นอีกโปรเจกต์หนึ่ง จึงเลิกไป
  • ยังได้คิดเทคนิค structstone ที่ใช้ structure block คล้าย redstone และเริ่มทำต้นแบบคอมพิวเตอร์ด้วยเทคนิคนี้
  • ระหว่างพัฒนามีการใช้ ffmpeg, mpv, image crate ของ Rust, โค้ด decompile ของ Minecraft และเทคนิคการลดขนาด world directory ให้น้อยที่สุด
  • งานทั้งหมดใช้เวลามากกว่าหนึ่งเดือน และการได้ร่วมมือกับเพื่อน ๆ ก็กลายเป็นประสบการณ์แก้ปัญหาในรูปแบบที่ต่างจากโปรเจกต์ทั่วไป

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

 
GN⁺ 2024-10-12
ความคิดเห็นบน Hacker News
  • ได้เรียนรู้เรื่อง คอมพิวเตอร์กราฟิกส์ มากกว่าที่คาดไว้มาก และขอชื่นชมผู้เขียน
    ขอแก้เล็กน้อยอย่างหนึ่ง: ภาพที่ผู้เขียนเรียกว่า “The sun” จริง ๆ แล้วเป็นฉากที่ Eirin [0] กำลังมองดวงจันทร์ ในฉากนั้น [1] Eirin เอื้อมมือไปยังดวงจันทร์ที่ตนถูกเนรเทศออกมา แต่ลังเลแล้วดึงมือกลับ ส่วนฉากถัดมา Kaguya [2] ก็เอื้อมมือไปยังดวงจันทร์เช่นกัน แต่ไม่ลังเล ตามวิกิ Touhou แผนที่จะขโมยดวงจันทร์เป็นของ Eirin ดังนั้นจึงไม่ค่อยแน่ใจว่าสัญลักษณ์นี้หมายถึงอะไรกันแน่
    [0] https://en.touhouwiki.net/wiki/Eirin_Yagokoro
    [1] https://youtu.be/FtutLA63Cp8?t=99
    [2] https://en.touhouwiki.net/wiki/Kaguya_Houraisan

    • เห็นฉากนั้นทีไรก็นึกถึง “the sun” เสมอ เพราะวิดีโอตลกอันนี้: https://www.youtube.com/watch?v=ReblZ7o7lu4
    • คิดว่าน่าจะอ่านผิดนะ ในวิกิบอกว่าเป็นแผนที่จะ seal หรือปิดผนึกเส้นทางระหว่างโลก หรือ Gensokyo กับดวงจันทร์ ไม่ใช่ “steal”
      Eirin ตั้งใจเลือกตัดการเชื่อมต่อกับดวงจันทร์เพื่อปกป้อง Kaguya
  • ไม่ค่อยเข้าใจว่าทำไม Bad Apple ถึงกำลังกลายเป็น Hello World โดยพฤตินัยของการเรนเดอร์กราฟิก แต่ดูแบบเรียลไทม์ก็สนุกดี
    เคยเห็นเดโมนี้ที่ใช้ Bad Apple แสดงไฮเปอร์มีเดียเฟรมเรตสูงด้วย: https://data-star.dev/examples/bad_apple

    • เหตุผลมีสองข้อ ข้อแรก ผู้สร้างต้นฉบับค่อนข้างใจกว้างมากต่อการรีมิกซ์และการใช้งานของแฟน ๆ
      ในหลายแง่ Touhou เป็นเหมือนต้นแบบของแฟนดอมอินเทอร์เน็ตยุคใหม่ ต่างจากแฟนดอมก่อนหน้า และวิดีโอ Bad Apple ที่ใช้ออดิโอเดียวกันก็ไม่ถูกสั่งลบ
      ข้อสอง รูปแบบ ละครเงา นั้นต่อให้ความละเอียดต่ำแค่ไหนก็ยังจำได้ง่าย เคยเห็นกรณีกริด 3x3 ด้วยซ้ำ ยิ่งไปกว่านั้นเป็นขาวดำ หรือมีแค่สองสี 1/0 ดังนั้นแค่รู้ระดับ “Hello World” ก็แปลงเฟรมเป็นรูปแบบแทบทุกแบบที่จินตนาการได้ง่ายมาก
    • มาตรฐาน DOOM มาถึงขั้นนี้แล้ว: https://www.reddit.com/r/Doom/comments/1c0g0mi/i_made_doom_i...
      ทำงานบน CPU ที่โปรแกรมได้เต็มรูปแบบซึ่งสร้างด้วย Redstone สเปกของ IRIS Computer คือ CPU 16 บิต แบบคัสตอม, RAM 8 kB, ROM 64 kB, texture ROM 1 kB, หน้าจอ 96x64 พิกเซล 16 สี, หน่วยคำนวณทศนิยมลอยตัว (add/sub/mult/div/sqrt), คล็อก 173 Redstone tick, ไม่มีการเร่งกราฟิก 3D ด้วยฮาร์ดแวร์, รันโปรแกรมที่เขียนด้วย URCL และเพราะเซิร์ฟเวอร์ MCHPRS ทำให้รันได้ที่ 1 ล้าน tick ต่อวินาที คิดเป็นความเร็วคล็อก 5.8 kHz
    • หนึ่งในลักษณะที่ไม่ค่อยถูกพูดถึงของเพลง Touhou รวมถึงเพลงต้นฉบับ Bad Apple!![1] คือ อย่างน้อยสำหรับผม มันฟังดูเหมือน ตัวแสดงสถานะของบัสข้อมูล มากกว่าดนตรี
      ถ้าจินตนาการว่ากำลังฟังบิตเลขคู่ของบัส 16 บิตที่ต่อเข้ากับเครื่องดนตรี ระหว่างที่ MS-DOS กำลังบูต จะสมเหตุสมผลกว่าการมองว่าเป็นเพลงที่มีจังหวะและห้องเพลงสม่ำเสมอมาก เพลงเหล่านี้แต่งโดยผู้พัฒนาเกม Touhou ซึ่งทำเกมยิงฮาร์ดคอร์สำหรับ PC-88/PC-98 คนเดียว โดยไม่ได้เรียนทฤษฎีดนตรีอย่างเป็นทางการ จึงดูเป็นผลลัพธ์ที่เป็นธรรมชาติ และอาจทำให้วิศวกรฮาร์ดแวร์ฝังตัวรู้สึกคุ้นเคยมากกว่าดนตรีทั่วไป
      อีกปัจจัยหนึ่งคือชุมชน nicovideo.jp / nico-tech ที่พัฒนามาจากวัฒนธรรม 2ch/futaba ผู้ใช้ที่มีความเชี่ยวชาญสูงกว่าค่าตอบแทนหรือความทะเยอทะยานทางการเงินมาก ในตอนนั้นก็มีนักศึกษา STEM จำนวนมาก ทุ่มเทคนิคลงไปกับรีมิกซ์เพื่อความสนุก พ่อมด FPGA ปริศนา ผู้เชี่ยวชาญไดรเวอร์มอเตอร์ และนักตัดต่อวิดีโอ มักโผล่มาแบบกะทันหัน โยนวิดีโอหลอน ๆ ทิ้งไว้แล้วจากไป ซึ่งมันหลุดโลกจริง ๆ ครั้งหนึ่ง Maker Faire Tokyo เคยแยกคนที่น่าสงสัยว่าใส่เสื้อเชิ้ต nico-tech ไปไว้ในโซนกักกันของอีกสถานที่จัดงาน เพื่อเอาใจเว็บเดเวลลอปเปอร์ใส่เสื้อยืดที่พยายามรักษาหน้า เรื่องนั้นทั้งน่าขันและนำไปสู่การเกิดมีตอัป nico-tech และไม่ได้เกิดซ้ำอีก ความหนาแน่นของคุณภาพและปริมาณคอนเทนต์หลุดโลกเหล่านั้นสร้างแรงส่งให้ PV ของ Bad Apple!!
      องค์ประกอบสำคัญสุดท้ายคือ PV เป็นสีเดียว หรือถ้าพูดให้เคร่งครัดคือโทนเทา น่าจะเป็นเหตุผลที่ทำให้กลายเป็นวิดีโอนี้ ไม่ใช่วิดีโออื่น ๆ ในยุคทองของ nicovideo.jp
      1: https://www.youtube.com/watch?v=Yw5HTeT_dis
    • การที่วิดีโอเป็น สีเดียว โดยสมบูรณ์ แต่ก็ลื่นไหลและประณีตมาก ทำให้เกิดความเป็นสองด้านที่น่าสนใจเมื่อนำไปใช้กับโจทย์ทางเทคนิค
      ตัวมันเองก็ดูดีและเป็นงานศิลปะที่น่าประทับใจอยู่แล้ว ผมคิดว่ามีคุณสมบัติหลายอย่างที่น่าจะถูกใจคนสายเดโมซีนเป็นพิเศษ
    • ยังมีตัวเลือกอื่นด้วย งานยุคแรกใน Factorio ที่ใช้วงจรควบคุมไฟ: https://youtu.be/Kry8lbrHjeY และการทำเสียง: https://youtu.be/b_FumvuFRXA
      ยังมีคลิปวิดีโออื่นที่มีสีด้วย: https://youtu.be/mgfwwqwxdxY
  • “Bad Apple บนทุกสิ่ง!” เป็นหนึ่งใน กระแสเนิร์ด ที่ผมชอบ
    ตอนเห็นครั้งแรกบน Genesis/Mega Drive ผมตกใจมากที่ฮาร์ดแวร์อ่อนขนาดนั้นยังทำได้ ชอบดูการทำพอร์ตใหม่ ๆ บนสิ่งที่ประสิทธิภาพไม่พอ ผมคงไม่เก่งการเขียนโปรแกรมระดับต่ำพอ และคงไม่ฉลาดพอจะทำเองได้ แต่ผมนับถือคนที่ทำได้จริง ๆ

  • ส่วนที่บอกว่า “recursion นี้จะจบลงเมื่อ Minecraft ชนขีดจำกัดแบบ hard limit และโชคดีที่แทนที่จะสร้างบล็อกสีแดง ก็จะสร้างบล็อกสีเหลืองขึ้นมา” ทำให้นึกถึง กลิตช์ update suppression สมัยก่อน (https://mcdf.wiki.gg/wiki/Java_Edition:Update_Suppression)
    population suppression ที่จุกจิกกว่านั้น (https://mcdf.wiki.gg/wiki/Java_Edition:Population_Suppressio...) ก็คล้ายกันตรงที่ทำให้เอนจินเกมค้างอยู่ในสถานะกลิตช์ จนทำให้บล็อกตกลงมาทันทีได้

  • อัลกอริทึม dithering ที่ชอบที่สุดสำหรับวิดีโอคือ Yliluoma dithering: https://bisqwit.iki.fi/story/howto/dither/jy/
    มันมีประโยชน์เป็นพิเศษกับคอนเทนต์ grayscale เพราะการหาเมทริกซ์ dithering ที่เหมาะที่สุดจากพาเลตที่ใช้ได้เป็นการคำนวณ exact แบบง่าย ๆ และสามารถเอาผลลัพธ์ใส่ lookup table เพื่อใช้เรนเดอร์แบบเรียลไทม์ได้ ส่วนตัวแล้วโดยเฉพาะกับ gradient ผมว่ามันดูดีกว่า Bayer หรือ random dithering มาก

  • คำพูดทำนองว่า “Redstone dust เป็นหนึ่งในคอมโพเนนต์แทบจะไม่กี่อย่างที่ไม่สร้าง tick delay แต่มันช้ามาก ดูเหมือนว่า Mojang จะไม่มีใครรู้จัก graph algorithms เลย” นั้นแรงเกินไป
    หลังจากบทความที่ต้นฉบับลิงก์ไว้เป็นแหล่งข้อมูล มันก็ช้าน้อยลงมากแล้ว และในช่วง 3 ปีที่ผ่านมาก็มีการปรับปรุงหลายอย่าง รวมถึงอันล่าสุดด้วย Mojang โดนด่าจากทุกทิศทางเยอะมาก เหตุผลที่การทำให้ Redstone ช้าน้อยลงใช้เวลานาน ก็เพราะแค่แตะ Redstone นิดเดียวชุมชนก็โวย และถ้าทำงานที่ไม่ใช่การเพิ่มฟีเจอร์ใหม่ ชุมชนก็โวยเหมือนกัน จึงทำให้ไม่ค่อยคุ้มที่จะทำ การโมโหบนอินเทอร์เน็ตแล้วบอกว่าเขาไม่รู้จัก graph algorithms ไม่ได้ช่วยอะไร Mojang เคยจ้างคนเก่งมาก ๆ จากชุมชน Minecraft อย่าง Panda4994, Kingbdogz, Gnembon มาแล้วหลายครั้ง และมีความเชี่ยวชาญทางเทคนิคพอจะทำสิ่งที่ต้องการได้ สิ่งที่ไม่มีคือเวลาและงบประมาณไม่จำกัด การพยายามดูแลและซิงก์ codebase Java อายุ 15 ปีไปพร้อมกับแอป C++ ข้ามแพลตฟอร์มขนาดใหญ่นั้นยากจริง ๆ เลยอยากให้ใจดีกับเขาหน่อย ผมเหนื่อยกับความเกลียดชังที่ถาโถมมาจากทุกทิศทั้งวัน และแค่อยากให้พูดได้ว่า Minecraft มันเจ๋ง

    • ประโยคทำนองนี้พบได้ค่อนข้างบ่อยในบล็อกโปรแกรมมิง
      เมื่อก่อนมันน่าหงุดหงิดกว่านี้ แต่ภายหลังผมก็รู้ว่ามันไม่ได้เป็นความอวดเก่งเท่าไรนัก หากใกล้เคียงกับความไร้เดียงสาของช่วงอายุ 16–21 ปี ที่มีประสบการณ์แบบ “มืออาชีพ” น้อยมากกว่า
    • ทำไมต้องเป็นแบบนั้นล่ะ? พวกเขามีพลังที่จะ เขียนเอนจินใหม่ด้วย Rust แล้วทำให้มันเป็นอะไรก็ได้ตามที่ต้องการ
      ดูก็ไม่ได้เหมือนว่าจะสนใจ compatibility ระหว่างเวอร์ชันสักเท่าไร
  • ตั้งแต่หลังมัธยมปลายก็ไม่ได้อินกับ Minecraft ถึงขั้นสร้างอุปกรณ์ Redstone จริงจังแล้ว
    ตอนนี้ก็เล่นกับเพื่อนเดือนละไม่กี่ครั้ง เวลาจู่ ๆ อยากสร้างอะไรสักอย่างและออกสำรวจ พอมอง ecosystem ของ Redstone ตอนนี้แล้วมันเปลี่ยนไปจนจำแทบไม่ได้ เลยสงสัยว่าเมื่อผมค่อย ๆ กลายเป็น senior software engineer จะรู้สึกคล้ายกันไหม เวลาผ่านไป แล้วมอง stack ที่ไม่ได้แตะในงานจริงมาหลายปี คงได้ทึ่งว่าเทคโนโลยีเปลี่ยนเร็วแค่ไหน และผู้คนสร้างสิ่งใหม่อะไรบนมันบ้าง

  • ไม่เห็นด้วยกับปฏิกิริยาที่ว่า “แล้ว… แค่นี้เองเหรอ? พอมองย้อนกลับไป ผลลัพธ์ดูเหมือนแทบจะทำได้อย่างง่ายดาย และสงสัยว่าทำไมก่อนหน้านี้ไม่มีใครทำ”
    นี่เป็นบันทึกการพัฒนาที่ยอดเยี่ยม และเป็นบทเรียนเล็ก ๆ ในการแบ่งงานที่ดูท่วมท้นออกเป็น ชิ้นส่วนที่แทบเป็นไปไม่ได้ แต่ยังเป็นไปได้ ชอบมากจริง ๆ อนึ่ง implementation นี้เรนเดอร์ Bad Apple ใน Minecraft แบบ vanilla ที่ 20fps โดยใช้ custom texture แค่หนึ่งอัน กับ custom object definitions ไม่กี่อย่างที่ปรับเพื่ออนุญาตให้มี texture มากขึ้น ที่เหลือนั้นแปลกมากก็จริง แต่ยังเป็น vanilla

    • สิ่งที่น่าประทับใจที่สุดคือ ความกว้างของความรู้ ที่เห็นได้จากแนวทางแก้ปัญหาที่ถูกปัดตกไป
  • ค่อนข้างตลกที่ทุ่มแรงให้ตัววิดีโอจริง ๆ มากขนาดนี้
    ปกติพอผมทำ implementation ของ Bad Apple เสร็จ ก็เหนื่อยเกินกว่าจะคิดเรื่อง dithering หรือ frame rate แล้ว แค่รันผ่าน ffmpeg แล้วก็ถือว่าจบ

  • Bad Apple ที่ทำเป็นโลก Minecraft ก็น่าดูเหมือนกัน: https://www.youtube.com/watch?v=RN3QW9SVnds