“Bad Apple!!” ใน Minecraft
(purplesyringa.moe)- นี่คือการสร้างระบบที่ทำให้ 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
- ต้นแบบแรกใช้เวลารันหนึ่งรอบประมาณ 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 ความคิดเห็น
ความคิดเห็นบน 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
Eirin ตั้งใจเลือกตัดการเชื่อมต่อกับดวงจันทร์เพื่อปกป้อง Kaguya
ไม่ค่อยเข้าใจว่าทำไม Bad Apple ถึงกำลังกลายเป็น Hello World โดยพฤตินัยของการเรนเดอร์กราฟิก แต่ดูแบบเรียลไทม์ก็สนุกดี
เคยเห็นเดโมนี้ที่ใช้ Bad Apple แสดงไฮเปอร์มีเดียเฟรมเรตสูงด้วย: https://data-star.dev/examples/bad_apple
ในหลายแง่ Touhou เป็นเหมือนต้นแบบของแฟนดอมอินเทอร์เน็ตยุคใหม่ ต่างจากแฟนดอมก่อนหน้า และวิดีโอ Bad Apple ที่ใช้ออดิโอเดียวกันก็ไม่ถูกสั่งลบ
ข้อสอง รูปแบบ ละครเงา นั้นต่อให้ความละเอียดต่ำแค่ไหนก็ยังจำได้ง่าย เคยเห็นกรณีกริด 3x3 ด้วยซ้ำ ยิ่งไปกว่านั้นเป็นขาวดำ หรือมีแค่สองสี 1/0 ดังนั้นแค่รู้ระดับ “Hello World” ก็แปลงเฟรมเป็นรูปแบบแทบทุกแบบที่จินตนาการได้ง่ายมาก
ทำงานบน 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
ถ้าจินตนาการว่ากำลังฟังบิตเลขคู่ของบัส 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
ตัวมันเองก็ดูดีและเป็นงานศิลปะที่น่าประทับใจอยู่แล้ว ผมคิดว่ามีคุณสมบัติหลายอย่างที่น่าจะถูกใจคนสายเดโมซีนเป็นพิเศษ
ยังมีคลิปวิดีโออื่นที่มีสีด้วย: 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 ปี ที่มีประสบการณ์แบบ “มืออาชีพ” น้อยมากกว่า
ดูก็ไม่ได้เหมือนว่าจะสนใจ 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