เติมชีวิตชีวาให้มิดเดิลเอิร์ธด้วย WebGL บนมือถือ
ที่ผ่านมา การนำประสบการณ์การใช้งานมัลติมีเดียที่เน้นการโต้ตอบ บนเว็บ และมัลติมีเดียมาใช้อุปกรณ์เคลื่อนที่และแท็บเล็ตถือเป็นเรื่องท้าทายมาโดยตลอด ข้อจำกัดหลักๆ ได้แก่ ประสิทธิภาพ ความพร้อมใช้งานของ API ข้อจำกัดของเสียง HTML5 บนอุปกรณ์ และการขาดการเล่นวิดีโอในหน้าที่ราบรื่น
เมื่อต้นปีนี้ เราเริ่มต้นโครงการร่วมกับเพื่อนๆ จาก Google และ Warner Bros. เพื่อสร้างประสบการณ์การใช้งานบนเว็บบนอุปกรณ์เคลื่อนที่เป็นอันดับแรกสำหรับภาพยนตร์เรื่องใหม่ Hobbit เรื่อง The Hobbit: The Desolation of Smaug การสร้างการทดลอง Chrome บนอุปกรณ์เคลื่อนที่ที่เน้นมัลติมีเดียเป็นงานที่สร้างแรงบันดาลใจและท้าทายอย่างมาก
ประสบการณ์การใช้งานนี้ได้รับการปรับให้เหมาะกับ Chrome สำหรับ Android บนอุปกรณ์ Nexus ใหม่ ซึ่งตอนนี้เราสามารถเข้าถึง WebGL และ Web Audio ได้แล้ว อย่างไรก็ตาม ประสบการณ์ส่วนใหญ่สามารถเข้าถึงได้บนอุปกรณ์และเบราว์เซอร์ที่ไม่ใช่ WebGL รวมไปถึงการใช้การประสานแบบเร่งฮาร์ดแวร์และภาพเคลื่อนไหว CSS
ประสบการณ์ทั้งหมดอิงจากแผนที่ของมิดเดิลเอิร์ธ ตลอดจนสถานที่และตัวละครจากภาพยนตร์ฮอบบิท การใช้ WebGL ช่วยให้เราสามารถสร้างสรรค์และสำรวจโลกอันอุดมสมบูรณ์ของไตรภาค Hobbit และทำให้ผู้ใช้สามารถควบคุมประสบการณ์ได้
ความท้าทายของ WebGL บนอุปกรณ์เคลื่อนที่
ประการแรก คำว่า "อุปกรณ์เคลื่อนที่" เป็นคำกว้างๆ ข้อมูลจำเพาะของอุปกรณ์อาจแตกต่างกันอย่างมาก ดังนั้น ในฐานะนักพัฒนาซอฟต์แวร์ คุณจะต้องตัดสินใจว่าต้องการสนับสนุนอุปกรณ์จำนวนมากขึ้นที่ประสบการณ์การใช้งานซับซ้อนน้อยลง หรือจะต้องจำกัดอุปกรณ์ที่รองรับไว้เพียงอุปกรณ์ที่สามารถแสดงโลก 3 มิติที่สมจริงมากขึ้นอย่างที่เราทำในเคสนี้ สำหรับ "การเดินทางผ่านมิดเดิลเอิร์ธ" เรามุ่งเน้นที่อุปกรณ์ Nexus และสมาร์ทโฟน Android ยอดนิยม 5 รุ่น
ในการทดลอง เราใช้ three.js ตามที่เราได้ทำโครงการ WebGL ก่อนหน้านี้บางโปรเจ็กต์ เราเริ่มการใช้งานโดยการสร้างเกม Trollshaw เวอร์ชันเริ่มต้นที่อาจจะทำงานได้ดีบนแท็บเล็ต Nexus 10 หลังจากทดสอบอุปกรณ์ในเบื้องต้น เราก็ได้บันทึกการเพิ่มประสิทธิภาพที่ดูเหมือนๆ กับแล็ปท็อปสเปคต่ำตามปกติ
- ใช้โมเดล Low-poly
- ใช้พื้นผิวความละเอียดต่ำ
- ลดจำนวนการเรียกโฆษณาให้ได้มากที่สุดโดยการผสานเรขาคณิต
- ลดความซับซ้อนของวัสดุและการจัดแสง
- นำเอฟเฟกต์ของโพสต์ออกและปิดการลบรอยหยัก
- เพิ่มประสิทธิภาพ JavaScript
- แสดงผล Canvas ของ WebGL ด้วยขนาดครึ่งหนึ่งและปรับขนาดด้วย CSS
หลังจากนำการเพิ่มประสิทธิภาพเหล่านี้ไปใช้กับเกมเวอร์ชันแรกอย่างคร่าวๆ เราก็มีอัตราเฟรมที่ 30FPS คงที่อย่างที่เราพอใจ ในจุดนี้เป้าหมายของเราคือการปรับปรุงภาพโดยไม่ส่งผลเสียต่ออัตราเฟรม เราลองใช้กลเม็ดหลายอย่าง บางลูกก็ได้ผลจริง แต่บางข้อก็ไม่ได้ส่งผลกระทบอย่างที่เราหวังไว้
ใช้โมเดล Low-poly
มาเริ่มกันที่รูปแบบ การใช้โมเดลแบบ Low Poly จะช่วยในเรื่องเวลาในการดาวน์โหลด รวมถึงเวลาที่ใช้ในการเริ่มต้นฉากด้วย เราพบว่าเราสามารถเพิ่มความซับซ้อนได้อย่างมากโดยไม่ส่งผลกระทบต่อประสิทธิภาพมากนัก โมเดลโทรลล์ที่เราใช้ในเกมนี้มีใบหน้าประมาณ 5,000 ใบหน้า และมีฉากประมาณ 40,000 ใบหน้าและทำงานได้ดี
สำหรับสถานที่อื่น (ที่ยังไม่เปิดตัว) จากประสบการณ์ เราพบว่ามีผลกระทบมากขึ้นต่อประสิทธิภาพจากการลดรูปหลายเหลี่ยม ในกรณีดังกล่าว เราโหลดวัตถุที่เป็นรูปหลายเหลี่ยมระดับล่างสำหรับอุปกรณ์เคลื่อนที่มากกว่าวัตถุที่เราโหลดสำหรับเดสก์ท็อป การสร้างชุดโมเดล 3 มิติแบบต่างๆ ต้องมีงานเพิ่มเติมและไม่จำเป็นต้องทำเสมอไป ทั้งนี้ขึ้นอยู่กับว่าโมเดลเริ่มต้นมีความซับซ้อนเพียงใด
เมื่อทำงานในฉากขนาดใหญ่ที่มีวัตถุจำนวนมาก เราพยายามวางกลยุทธ์ในการแบ่งรูปทรงเรขาคณิต วิธีนี้ช่วยให้เราเปิดและปิด Mesh ที่ไม่สำคัญนักได้อย่างรวดเร็ว เพื่อค้นหาการตั้งค่าที่ใช้ได้กับอุปกรณ์เคลื่อนที่ทุกเครื่อง จากนั้น เราจะเลือกผสานเรขาคณิตใน JavaScript ขณะรันไทม์เพื่อเพิ่มประสิทธิภาพแบบไดนามิก หรือรวมเข้าด้วยกันในขั้นตอนก่อนการผลิตเพื่อบันทึกคำขอ
ใช้พื้นผิวความละเอียดต่ำ
เพื่อลดเวลาที่ใช้ในการโหลดบนอุปกรณ์เคลื่อนที่ เราเลือกโหลดพื้นผิวต่างๆ ที่มีขนาดเล็กกว่าครึ่งหนึ่งของพื้นผิวบนเดสก์ท็อป เราพบว่าอุปกรณ์ทั้งหมดสามารถจัดการกับขนาดพื้นผิวได้ถึง 2048x2048 พิกเซล และอุปกรณ์ส่วนใหญ่รองรับขนาด 4096x4096 พิกเซลได้ ดูเหมือนว่าการค้นหาพื้นผิวของแต่ละพื้นผิวจะไม่เป็นปัญหาเมื่ออัปโหลดไปยัง GPU ขนาดรวมของพื้นผิวต้องพอดีกับหน่วยความจำ GPU เพื่อหลีกเลี่ยงการเพิ่มและดาวน์โหลดพื้นผิวอยู่ตลอดเวลา แต่นี่อาจไม่ใช่ปัญหาใหญ่สำหรับประสบการณ์การใช้งานเว็บส่วนใหญ่ อย่างไรก็ตาม การรวมพื้นผิวให้เป็นภาพต่อเรียงให้น้อยที่สุดเท่าที่จะเป็นไปได้คือสิ่งจำเป็นที่จะช่วยลดจำนวนการดึงความสนใจ ซึ่งเป็นสิ่งที่มีผลอย่างมากต่อประสิทธิภาพบนอุปกรณ์เคลื่อนที่
ลดความซับซ้อนของวัสดุและการจัดแสง
การเลือกใช้วัสดุยังส่งผลต่อประสิทธิภาพอย่างมาก และต้องมีการจัดการอย่างชาญฉลาดบนอุปกรณ์เคลื่อนที่ การใช้ MeshLambertMaterial
(ต่อการคำนวณแสงของจุดยอดมุม) ใน 3.js แทน MeshPhongMaterial
(ต่อการคำนวณแสง Texel) เป็นสิ่งหนึ่งที่เราใช้ในการเพิ่มประสิทธิภาพ โดยพื้นฐานแล้ว เราพยายามใช้ตัวเฉดสีแบบง่ายๆ โดยมีการคำนวณแสงให้น้อยที่สุดเท่าที่จะทำได้
หากต้องการดูว่าวัสดุที่คุณใช้ส่งผลต่อประสิทธิภาพของฉากอย่างไร คุณสามารถลบล้างเนื้อหาของฉากด้วย MeshBasicMaterial
วิธีนี้จะช่วยให้คุณเห็นการเปรียบเทียบที่ดี
scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});
เพิ่มประสิทธิภาพ JavaScript
เมื่อสร้างเกมสำหรับอุปกรณ์เคลื่อนที่ GPU อาจไม่ใช่อุปสรรคที่ใหญ่ที่สุดเสมอไป CPU มักจะใช้เวลามากมาย โดยเฉพาะฟิสิกส์และภาพเคลื่อนไหวโครงร่าง เคล็ดลับอย่างหนึ่งที่ช่วยได้ในบางครั้งคือการใช้เฉพาะการคำนวณราคาแพงเหล่านี้กับเฟรมอื่นๆ เท่านั้น ทั้งนี้ขึ้นอยู่กับการจำลอง คุณสามารถใช้เทคนิคการเพิ่มประสิทธิภาพ JavaScript ที่มีอยู่เมื่อพูดถึงการรวบรวมออบเจ็กต์ การรวบรวมขยะ และการสร้างออบเจ็กต์
การอัปเดตวัตถุที่จัดสรรล่วงหน้าให้วนซ้ำแทนการสร้างวัตถุใหม่เป็นขั้นตอนสำคัญในการหลีกเลี่ยง "อาการสะอึก" ของขยะในเกม
ลองพิจารณาโค้ดแบบนี้
var currentPos = new THREE.Vector3();
function gameLoop() {
currentPos = new THREE.Vector3(0+offsetX,100,0);
}
ลูปเวอร์ชันที่ปรับปรุงแล้วนี้จะหลีกเลี่ยงการสร้างออบเจ็กต์ใหม่ที่ต้องเก็บข้อมูลขยะ:
var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
currentPos.copy(originPos).x += offsetX;
//or
currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}
เครื่องจัดการเหตุการณ์ควรอัปเดตเฉพาะพร็อพเพอร์ตี้ให้มากที่สุดเท่าที่จะทำได้ และให้แฮนเดิลลูปการแสดงผล requestAnimationFrame
อัปเดตพื้นที่งาน
เคล็ดลับอีกอย่างคือการเพิ่มประสิทธิภาพและ/หรือคำนวณการดำเนินการแคสต์เรย์ล่วงหน้า ตัวอย่างเช่น หากต้องการติดวัตถุเข้ากับ Mesh ในระหว่างการเคลื่อนที่แบบเส้นทางแบบคงที่ คุณสามารถ "บันทึก" ตำแหน่งในการวนซ้ำ 1 รอบ จากนั้นอ่านข้อมูลจากข้อมูลนี้แทนการแคสต์แบบ Ray กับ Mesh ได้ หรืออย่างที่เราทำในประสบการณ์ Rivendell ให้ใช้ Ray-cast เพื่อหาการโต้ตอบกับเมาส์ด้วย Mesh แบบ Low-poly ที่มองไม่เห็น โดยทั่วไปการค้นหาการชนกันบน High-poly Mesh จะช้ามากและควรหลีกเลี่ยงแบบ Game Loop
แสดงผล Canvas ของ WebGL ด้วยขนาดครึ่งหนึ่งและปรับขนาดด้วย CSS
ขนาดของผืนผ้าใบ WebGL อาจเป็นพารามิเตอร์เดียวที่มีประสิทธิภาพสูงสุดที่คุณสามารถปรับเปลี่ยนเพื่อเพิ่มประสิทธิภาพได้ ยิ่งคุณใช้ผืนผ้าใบวาดฉาก 3 มิติมีขนาดใหญ่เท่าไร ก็จะต้องวาดพิกเซลทุกเฟรมมากขึ้นเท่านั้น แน่นอนว่าจะส่งผลต่อประสิทธิภาพ Nexus 10 ที่มีจอแสดงผลความหนาแน่นสูง 2560x1600 พิกเซล จะต้องพุชจำนวนพิกเซลมากกว่าแท็บเล็ตความหนาแน่นต่ำถึง 4 เท่า เราใช้กลเม็ดในการตั้งผืนผ้าใบให้มีขนาดครึ่งหนึ่ง (50%) แล้วปรับขนาดให้เท่ากับขนาดที่ต้องการ (100%) ด้วยการแปลงรูปแบบ CSS 3D ที่เร่งการแสดงผลด้วยฮาร์ดแวร์ ข้อเสียของภาพนี้คือภาพแบบพิกเซลที่เส้นบางๆ อาจกลายเป็นปัญหา แต่หน้าจอความละเอียดสูงนั้นไม่ได้แย่ขนาดนั้น คุ้มค่ากับประสิทธิภาพที่เพิ่มขึ้นอย่างแน่นอน
ใช้วัตถุเป็นองค์ประกอบที่ใช้สร้างสรรค์
ในการสร้างเขาวงกตขนาดใหญ่ของปราสาท Dol Guldur และหุบเขาแห่ง Rivendell ที่ไม่มีวันสิ้นสุด เราได้สร้างชุดโมเดล 3 มิติบล็อกนำกลับมาใช้ใหม่ การใช้ออบเจ็กต์ซ้ำช่วยให้เรามั่นใจได้ว่าออบเจ็กต์ได้รับการสร้างอินสแตนซ์และอัปโหลดตั้งแต่จุดเริ่มต้นของประสบการณ์ และไม่อยู่ตรงกลาง
ใน Rivendell เรามีส่วนพื้นดินจำนวนหนึ่งที่เปลี่ยนตำแหน่งเป็น Z ลึกอย่างต่อเนื่องขณะที่เส้นทางของผู้ใช้ดำเนินไป เมื่อผู้ใช้ผ่านส่วนต่างๆ เหล่านี้ ก็จะได้รับการเปลี่ยนตำแหน่งในระยะไกล
สำหรับปราสาท Dol Guldur เราอยากให้มีการสร้างเขาวงกตขึ้นมาใหม่สำหรับทุกเกม วิธีการคือ เราได้สร้างสคริปต์ที่สร้างเขาวงกตขึ้นมาใหม่
การผสานโครงสร้างทั้งหมดเข้าด้วยกันเป็นจุดศูนย์กลางขนาดใหญ่ตั้งแต่ต้นจะส่งผลให้เกิดฉากใหญ่และด้อยประสิทธิภาพ ในการแก้ปัญหานี้ เราจึงตัดสินใจที่จะซ่อนและแสดงองค์ประกอบพื้นฐานโดยขึ้นอยู่กับว่าองค์ประกอบนั้นมองเห็นอยู่หรือไม่ ตั้งแต่แรกเริ่ม เรามีแนวคิดเกี่ยวกับการใช้สคริปต์ 2D raycaster แต่สุดท้ายเราใช้ 3.js frustrum culling ในตัว เรานำสคริปต์ raycaster มาใช้ใหม่เพื่อขยาย "อันตราย" ที่ผู้เล่นกำลังเผชิญอยู่
สิ่งสำคัญถัดไปที่ต้องจัดการคือการโต้ตอบของผู้ใช้ บนเดสก์ท็อปคุณใช้การป้อนข้อมูลด้วยเมาส์และแป้นพิมพ์ ในอุปกรณ์เคลื่อนที่ ผู้ใช้ของคุณจะโต้ตอบกับการแตะ ปัด บีบ วางแนวของอุปกรณ์ ฯลฯ
การใช้การโต้ตอบด้วยการสัมผัสในประสบการณ์การใช้งานเว็บบนอุปกรณ์เคลื่อนที่
การเพิ่มการสนับสนุนทางการสัมผัสไม่ใช่เรื่องยาก เรามีบทความดีๆ ให้อ่านเกี่ยวกับหัวข้อนี้ แต่ยังมีเรื่องเล็กๆ น้อยๆ ที่ทำให้ซับซ้อนขึ้นได้
คุณใช้ได้ทั้งการแตะและเมาส์ Chromebook Pixel และแล็ปท็อปอื่นๆ ที่เปิดใช้ระบบสัมผัสจะรองรับทั้งเมาส์และหน้าจอสัมผัส ข้อผิดพลาดที่พบบ่อยอย่างหนึ่งคือการตรวจสอบว่าอุปกรณ์มีการเปิดใช้ระบบสัมผัสหรือไม่ จากนั้นเพิ่มเฉพาะ Listener เหตุการณ์การแตะเท่านั้น และไม่เพิ่มใดๆ สำหรับเมาส์
อย่าอัปเดตการแสดงผลใน Listener เหตุการณ์ บันทึกเหตุการณ์การแตะไปยังตัวแปรแทน และตอบสนองกับเหตุการณ์ในลูปการแสดงผล requestAnimationFrame ซึ่งจะช่วยเพิ่มประสิทธิภาพและรวมกิจกรรมที่ขัดแย้ง ตรวจสอบว่าคุณใช้ออบเจ็กต์ซ้ำแทนการสร้างออบเจ็กต์ใหม่ใน Listener เหตุการณ์
อย่าลืมว่าแอปนี้เป็นมัลติทัช: event.touches เป็นการโต้ตอบหลากหลายแบบ ในบางกรณีก็ให้ลองไปที่ event.targetTouches หรือ event.changedTouches แทน และโต้ตอบด้วยสัมผัสที่คุณสนใจเท่านั้น หากต้องการแยกการแตะออกจากการปัด เราจะใช้การหน่วงเวลาก่อนจะตรวจสอบว่าการแตะขยับ (ปัด) หรือยังอยู่ (การแตะ) ในการบีบนิ้ว เราจะวัดระยะห่างระหว่างการสัมผัสเริ่มต้น 2 ครั้งและดูว่าการเปลี่ยนแปลงนั้นเปลี่ยนแปลงไปอย่างไรเมื่อเวลาผ่านไป
ในโลก 3 มิติ คุณจะต้องตัดสินใจว่ากล้องจะตอบสนองอย่างไรต่อเมาส์และการเลื่อน วิธีหนึ่งที่พบบ่อยในการเพิ่มการเคลื่อนที่ของกล้องคือการติดตามการเคลื่อนที่ของเมาส์ ซึ่งทำได้ด้วยการควบคุมโดยตรงโดยใช้ตำแหน่งของเมาส์หรือการเคลื่อนที่แบบเดลต้า (การเปลี่ยนตำแหน่ง) คุณไม่ต้องการใช้งานอุปกรณ์เคลื่อนที่เหมือนกับเบราว์เซอร์บนเดสก์ท็อปเสมอไป เราได้ทดสอบอย่างละเอียดเพื่อตัดสินใจว่าสิ่งใดเหมาะกับแต่ละเวอร์ชัน
เมื่อใช้งานหน้าจอขนาดเล็กและหน้าจอสัมผัส คุณจะพบว่านิ้วมือและกราฟิกการโต้ตอบของ UI ของผู้ใช้มักจะบดบังสิ่งที่คุณต้องการแสดง นี่คือสิ่งที่เราคุ้นเคยในการออกแบบแอปที่มาพร้อมเครื่อง แต่ที่ผ่านมาเราไม่เคยคิดถึงประสบการณ์การใช้งานบนเว็บมาก่อน เรื่องนี้เป็นความท้าทายอย่างยิ่งสำหรับนักออกแบบและนักออกแบบ UX
สรุป
ประสบการณ์โดยรวมของเราจากโปรเจ็กต์นี้คือ WebGL บนอุปกรณ์เคลื่อนที่ทำงานได้ดี โดยเฉพาะอย่างยิ่งในอุปกรณ์ระดับไฮเอนด์รุ่นใหม่ เมื่อพูดถึงประสิทธิภาพ ดูเหมือนว่าจำนวนรูปหลายเหลี่ยมและขนาดพื้นผิวจะส่งผลกระทบกับเวลาในการดาวน์โหลดและการเริ่มต้นเป็นส่วนใหญ่ และเนื้อหาดังกล่าว ตัวปรับเฉดสีและขนาดของผืนผ้าใบ WebGL คือส่วนที่สำคัญที่สุดในการเพิ่มประสิทธิภาพการทำงานบนมือถือ อย่างไรก็ตาม ผลลัพธ์นี้คือผลรวมของส่วนต่างๆ ที่ส่งผลกระทบต่อประสิทธิภาพ ดังนั้นทุกๆ อย่างที่คุณทำได้เพื่อเพิ่มประสิทธิภาพให้กับจำนวน
การกำหนดเป้าหมายอุปกรณ์เคลื่อนที่ยังหมายความว่าคุณต้องคุ้นเคยกับการพิจารณาเกี่ยวกับการโต้ตอบด้วยการสัมผัส และไม่ใช่แค่เรื่องขนาดพิกเซลเท่านั้น แต่ยังเป็นขนาดจริงของหน้าจอด้วย ในบางกรณี เราต้องเลื่อนกล้อง 3 มิติเข้าไปใกล้ขึ้นเพื่อดูสิ่งที่เกิดขึ้นจริงๆ
การทดสอบเปิดตัวแล้วและเป็นเส้นทางที่ยอดเยี่ยม หวังว่าคุณจะชอบนะ
อยากลองใช้ดูไหม เลือกการเดินทางสู่มิดเดิลเอิร์ธของคุณเอง