สวัสดี ผมชื่อ Michael Chang ทำงานในทีมศิลปะข้อมูลของ Google เมื่อเร็วๆ นี้ เราได้เปิดตัว 100,000 Stars ซึ่งเป็นเวอร์ชันทดลองของ Chrome ที่แสดงภาพดาวที่อยู่ใกล้เคียง โปรเจ็กต์นี้สร้างขึ้นด้วย THREE.js และ CSS3D ในกรณีศึกษานี้ เราจะอธิบายกระบวนการค้นพบ แชร์เทคนิคการเขียนโปรแกรม และปิดท้ายด้วยแนวคิดสำหรับการปรับปรุงในอนาคต
หัวข้อที่เราจะพูดถึงจะค่อนข้างกว้างและต้องใช้ความรู้เกี่ยวกับ THREE.js อยู่บ้าง แต่เราหวังว่าคุณจะยังสนุกกับบทความนี้ในฐานะข้อมูลสรุปทางเทคนิค คุณข้ามไปยังหัวข้อที่สนใจได้โดยใช้ปุ่มสารบัญทางด้านขวา ก่อนอื่น เราจะแสดงการเรนเดอร์ของโปรเจ็กต์ ตามด้วยการจัดการชิดเดอร์ และสุดท้ายคือวิธีใช้ป้ายกำกับข้อความ CSS ร่วมกับ WebGL
Discovering Space
หลังจากสร้าง Small Arms Globe เสร็จไม่นาน ฉันได้ทดลองใช้การสาธิตอนุภาค THREE.js ที่มีระยะความชัด เราพบว่าสามารถเปลี่ยน "ขนาด" ที่ตีความของฉากได้โดยการปรับปริมาณของเอฟเฟกต์ที่ใช้ เมื่อใช้เอฟเฟกต์ความชัดลึกในลักษณะที่รุนแรงมาก วัตถุที่อยู่ไกลๆ จะเบลอมาก คล้ายกับลักษณะการทำงานของการถ่ายภาพแบบ Tilt-Shift ที่ให้ภาพลวงตาเหมือนกำลังมองดูฉากผ่านกล้องจุลทรรศน์ ในทางกลับกัน การลดเอฟเฟกต์จะทำให้ดูเหมือนว่าคุณกำลังมองเข้าไปในห้วงอวกาศ
เราเริ่มค้นหาข้อมูลที่สามารถใช้เพื่อแทรกตำแหน่งของอนุภาค ซึ่งนำเราไปยังฐานข้อมูล HYG ของ astronexus.com ซึ่งเป็นการรวบรวมแหล่งข้อมูล 3 แห่ง (Hipparcos, Yale Bright Star Catalog และ Gliese/Jahreiss Catalog) พร้อมด้วยพิกัด xyz ของพิกัดเชิงเส้น 3 มิติที่คำนวณไว้ล่วงหน้า มาเริ่มกันเลย
ใช้เวลาประมาณ 1 ชั่วโมงในการแฮ็กข้อมูลดวงดาวในอวกาศ 3 มิติ เซ็ตข้อมูลนี้มีดาวทั้งหมด 119,617 ดวง การนำเสนอดาวแต่ละดวงด้วยอนุภาคจึงไม่ใช่ปัญหาสำหรับ GPU สมัยใหม่ นอกจากนี้ยังมีดาวที่ระบุแยกต่างหากอีก 87 ดวง เราจึงสร้างเครื่องหมาย CSS วางซ้อนโดยใช้เทคนิคเดียวกับที่อธิบายไว้ใน Small Arms Globe
ในช่วงเวลานี้ ฉันเพิ่งเล่นซีรีส์ Mass Effect จบ ในเกมนี้ ผู้เล่นได้รับเชิญให้สำรวจกาแล็กซีและสแกนดาวเคราะห์ต่างๆ รวมถึงอ่านเกี่ยวกับประวัติศาสตร์สมมติที่ฟังดูคล้ายกับวิกิพีเดีย เช่น สายพันธุ์ใดที่อาศัยอยู่บนดาวเคราะห์ ประวัติศาสตร์ทางธรณีวิทยาของดาวเคราะห์ และอื่นๆ
เมื่อทราบข้อมูลจริงมากมายเกี่ยวกับดวงดาว ก็อาจนำเสนอข้อมูลจริงเกี่ยวกับกาแล็กซีในลักษณะเดียวกันได้ เป้าหมายสูงสุดของโปรเจ็กต์นี้คือทำให้ข้อมูลนี้มีชีวิตชีวา ให้ผู้ชมสำรวจกาแล็กซีในแบบ Mass Effect เพื่อเรียนรู้เกี่ยวกับดวงดาวและการกระจายตัวของดวงดาว และหวังว่าจะสร้างแรงบันดาลใจให้รู้สึกถึงความน่าทึ่งและน่าอัศจรรย์ของอวกาศ ในที่สุด
เราขอเกริ่นก่อนว่าเราไม่ใช่นักดาราศาสตร์ และนี่เป็นงานวิจัยสมัครเล่นซึ่งได้รับคำแนะนำจากผู้เชี่ยวชาญภายนอก โปรเจ็กต์นี้ควรตีความว่าเป็นพื้นที่ที่ศิลปินตีความ
การสร้างกาแล็กซี
แผนของเราคือการสร้างโมเดลกาแล็กซีแบบเป็นขั้นตอนซึ่งสามารถใส่ข้อมูลดาวในบริบทได้ และหวังว่าจะให้มุมมองอันน่าทึ่งเกี่ยวกับตำแหน่งของเราในดาราจักรทางช้างเผือก
ในการสร้างทางช้างเผือก เราสร้างอนุภาค 100,000 อนุภาคและวางไว้ในเกลียวโดยจำลองการก่อตัวของแขนกังหัน ฉันไม่ได้กังวลมากนักเกี่ยวกับรายละเอียดของการก่อตัวของแขนก้นหอย เนื่องจากนี่จะเป็นโมเดลการนำเสนอแทนที่จะเป็นโมเดลทางคณิตศาสตร์ อย่างไรก็ตาม เราพยายามทำให้จำนวนแขนก้นหอยถูกต้องมากขึ้นหรือน้อยลง และบิดไปใน "ทิศทางที่ถูกต้อง"
ในโมเดลทางช้างเผือกเวอร์ชันหลังๆ เราลดการเน้นการใช้อนุภาคและหันมาใช้ภาพกาแล็กซีแบบระนาบแทนเพื่อใช้ร่วมกับอนุภาค หวังว่าจะทำให้ภาพดูเป็นภาพถ่ายมากขึ้น ภาพจริงคือภาพของกาแล็กซีเกลียว NGC 1232 ที่อยู่ห่างจากเราประมาณ 70 ล้านปีแสง ซึ่งมีการดัดแปลงภาพให้ดูเหมือนทางช้างเผือก
เราตัดสินใจตั้งแต่เนิ่นๆ ว่าจะใช้ 1 หน่วย GL ซึ่งโดยพื้นฐานแล้วคือพิกเซลใน 3 มิติ เป็น 1 ปีแสง ซึ่งเป็นแบบแผนที่ใช้ตำแหน่งแบบเดียวกันสำหรับทุกอย่างที่แสดงเป็นภาพ แต่น่าเสียดายที่ทำให้เกิดปัญหาความแม่นยำอย่างรุนแรงในภายหลัง
อีกแนวทางหนึ่งที่เราเลือกคือหมุนทั้งฉากแทนที่จะเคลื่อนกล้อง ซึ่งเราเคยทำในโปรเจ็กต์อื่นๆ 2-3 โปรเจ็กต์ ข้อดีอย่างหนึ่งคือทุกอย่างวางอยู่บน "แท่นหมุน" เพื่อให้การลากเมาส์ไปทางซ้ายและขวาหมุนวัตถุที่เป็นปัญหา แต่การซูมเข้าเป็นเพียงการเปลี่ยน camera.position.z
ขอบเขตการมองเห็น (หรือ FOV) ของกล้องก็เป็นแบบไดนามิกเช่นกัน เมื่อดึงกล้องออก มุมมองจะกว้างขึ้นเพื่อเก็บภาพกาแล็กซีได้มากขึ้น ในทางกลับกัน เมื่อเคลื่อนเข้าหาดาว ขอบเขตการมองเห็นจะแคบลง วิธีนี้ช่วยให้กล้องมองเห็นสิ่งที่มีขนาดเล็กมาก (เมื่อเทียบกับกาแล็กซี) โดยการบีบ FOV ให้เล็กลงจนคล้ายกับแว่นขยายอันทรงพลังโดยไม่ต้องจัดการกับปัญหาการครอบตัดใกล้กับเครื่องบิน
จากตรงนี้ ฉันสามารถ "วาง" ดวงอาทิตย์ไว้ที่ระยะห่างจากใจกลางกาแล็กซีได้ นอกจากนี้ เรายังแสดงภาพขนาดสัมพัทธ์ของระบบสุริยะได้ด้วย โดยการทำแผนที่รัศมีของหน้าผา Kuiper (แต่สุดท้ายเราเลือกแสดงภาพเมฆ Oort แทน) ในระบบสุริยะจำลองนี้ ฉันยังเห็นภาพวงโคจรของดวงโลกแบบง่ายและรัศมีจริงของดวงอาทิตย์เพื่อเปรียบเทียบได้ด้วย
ดวงอาทิตย์แสดงผลได้ยาก ฉันต้องใช้เทคนิคกราฟิกแบบเรียลไทม์เท่าที่รู้ พื้นผิวของดวงอาทิตย์เป็นพลาสม่าร้อนๆ ที่ปั่นป่วน และจำเป็นต้องสั่นไหวและเปลี่ยนแปลงไปเมื่อเวลาผ่านไป ซึ่งจำลองผ่านพื้นผิวบิตแมปของภาพอินฟราเรดจากพื้นผิวของดวงอาทิตย์ โปรแกรมเปลี่ยนสีพื้นผิวจะทำการค้นหาสีตามโทนสีเทาของพื้นผิวนี้ และทำการค้นหาในแถบสีแยกต่างหาก เมื่อการค้นหานี้เปลี่ยนไปตามช่วงเวลา ก็จะทำให้เกิดภาพบิดเบี้ยวคล้ายลาวา
มีการใช้เทคนิคที่คล้ายกันกับโคโรนาของดวงอาทิตย์ ยกเว้นว่าจะเป็นการ์ดสไปรท์แบบแบนซึ่งหันหน้าเข้าหากล้องเสมอโดยใช้ https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js
เปลวสุริยะสร้างขึ้นผ่านเวิร์กเทกซ์และฟร็กเมนต์เชดเดอร์ที่ใช้กับทรงกลมรีที่หมุนอยู่รอบๆ ขอบของพื้นผิวดวงอาทิตย์ เวิร์กเท็กเจอร์มีฟังก์ชันเสียงรบกวนซึ่งทำให้เวิร์กเท็กเจอร์มีลักษณะเป็นก้อน
ตรงจุดนี้เราเริ่มพบปัญหาการต่อสู้กับ Z เนื่องจากความแม่นยำของ GL ตัวแปรทั้งหมดสำหรับความแม่นยำได้รับการกำหนดไว้ล่วงหน้าใน THREE.js เราจึงไม่สามารถเพิ่มความแม่นยำได้จริงโดยที่ไม่ต้องทำงานอย่างหนัก ปัญหาความแม่นยำไม่รุนแรงเท่าบริเวณต้นทาง อย่างไรก็ตาม เมื่อฉันเริ่มจำลองระบบดาวอื่นๆ ปัญหานี้จึงเกิดขึ้น
เราใช้วิธีแก้ปัญหาบางอย่างเพื่อลดปัญหาการต่อสู้กันของพื้นผิว Material.polygonoffset ของ THREE คือพร็อพเพอร์ตี้ที่ช่วยให้แสดงผลรูปหลายเหลี่ยมในตำแหน่งที่รับรู้แตกต่างกัน (เท่าที่ฉันเข้าใจ) ซึ่งใช้เพื่อบังคับให้ระนาบโคโรนาแสดงผลบนพื้นผิวของดวงอาทิตย์เสมอ ด้านล่างนี้มีการเรนเดอร์ "รัศมี" ของดวงอาทิตย์เพื่อให้แสงที่คมชัดซึ่งเคลื่อนออกจากทรงกลม
ปัญหาอีกอย่างหนึ่งที่เกี่ยวข้องกับความแม่นยำคือโมเดลดาวจะเริ่มสั่นเมื่อคุณซูมเข้าฉาก ในการแก้ไขปัญหานี้ เราจึงต้อง "รีเซ็ต" การหมุนฉาก แล้วหมุนโมเดลดาวและแผนที่สภาพแวดล้อมแยกกันเพื่อให้ดูเหมือนว่าคุณกำลังโคจรรอบดาว
การสร้างแสงสะท้อนจากเลนส์
ภาพพื้นที่ทำงานเป็นภาพที่ผมรู้สึกว่าใช้แสงแฟลร์มากเกินไปได้ THREE.LensFlare มีไว้เพื่อวัตถุประสงค์นี้ เพียงใส่รูปหกเหลี่ยมแบบ Anamorphic เล็กน้อยและเพิ่มสไตล์ของ JJ Abrams ลงไป ข้อมูลโค้ดด้านล่างแสดงวิธีสร้างวัตถุดังกล่าวในฉาก
// This function returns a lesnflare THREE object to be .add()ed to the scene graph
function addLensFlare(x,y,z, size, overrideImage){
var flareColor = new THREE.Color( 0xffffff );
lensFlare = new THREE.LensFlare( overrideImage, 700, 0.0, THREE.AdditiveBlending, flareColor );
// we're going to be using multiple sub-lens-flare artifacts, each with a different size
lensFlare.add( textureFlare1, 4096, 0.0, THREE.AdditiveBlending );
lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
// and run each through a function below
lensFlare.customUpdateCallback = lensFlareUpdateCallback;
lensFlare.position = new THREE.Vector3(x,y,z);
lensFlare.size = size ? size : 16000 ;
return lensFlare;
}
// this function will operate over each lensflare artifact, moving them around the screen
function lensFlareUpdateCallback( object ) {
var f, fl = this.lensFlares.length;
var flare;
var vecX = -this.positionScreen.x _ 2;
var vecY = -this.positionScreen.y _ 2;
var size = object.size ? object.size : 16000;
var camDistance = camera.position.length();
for( f = 0; f < fl; f ++ ) {
flare = this.lensFlares[ f ];
flare.x = this.positionScreen.x + vecX * flare.distance;
flare.y = this.positionScreen.y + vecY * flare.distance;
flare.scale = size / camDistance;
flare.rotation = 0;
}
}
วิธีง่ายๆ ในการเลื่อนพื้นผิว
สําหรับ "ระนาบการวางแนวเชิงพื้นที่" ระบบได้สร้าง THREE.CylinderGeometry() ขนาดใหญ่และวางไว้ที่ศูนย์กลางของดวงอาทิตย์ หากต้องการสร้าง "คลื่นแสง" ที่แผ่ออกไปด้านนอก ฉันได้แก้ไขระยะออฟเซ็ตของพื้นผิวตามช่วงเวลาดังนี้
mesh.material.map.needsUpdate = true;
mesh.material.map.onUpdate = function(){
this.offset.y -= 0.001;
this.needsUpdate = true;
}
map
คือพื้นผิวของวัสดุ ซึ่งได้รับฟังก์ชัน onUpdate ที่คุณเขียนทับได้ การตั้งค่าออฟเซตจะทำให้ "เลื่อน" พื้นผิวไปตามแกนนั้น และการใส่ค่า needsUpdate = true ซ้ำๆ จะบังคับให้ลักษณะการทำงานนี้วนซ้ำ
การใช้แถบสี
ดาวแต่ละดวงมีสีแตกต่างกันไปตาม "ดัชนีสี" ที่นักดาราศาสตร์กำหนดไว้ โดยทั่วไปแล้ว ดาวสีแดงจะเย็นกว่าและดาวสีน้ำเงิน/ม่วงจะร้อนกว่า แถบสีขาวและสีส้มกลางอยู่ในไล่ระดับสีนี้
เมื่อแสดงผลดาว ฉันต้องการกำหนดสีให้กับอนุภาคแต่ละอนุภาคตามข้อมูลนี้ วิธีดำเนินการคือใช้ "แอตทริบิวต์" กับวัสดุเชดเดอร์ที่ใช้กับอนุภาค
var shaderMaterial = new THREE.ShaderMaterial( {
uniforms: datastarUniforms,
attributes: datastarAttributes,
/_ ... etc _/
});
var datastarAttributes = {
size: { type: 'f', value: [] },
colorIndex: { type: 'f', value: [] },
};
การกรอกอาร์เรย์ colorIndex จะทำให้แต่ละอนุภาคมีสีที่ไม่ซ้ำกันในโปรแกรมเปลี่ยนสี โดยปกติแล้วเราจะส่งผ่าน vec3 สี แต่ในกรณีนี้เราจะส่งผ่าน float สำหรับการค้นหาแถบสีในท้ายที่สุด
แถบสีมีลักษณะดังนี้ แต่ฉันต้องการเข้าถึงข้อมูลสีบิตแมปจาก JavaScript โดยวิธีที่เราใช้คือโหลดรูปภาพไปยัง DOM ก่อน จากนั้นวาดรูปภาพลงในองค์ประกอบ Canvas แล้วเข้าถึงบิตแมป Canvas
// make a blank canvas, sized to the image, in this case gradientImage is a dom image element
gradientCanvas = document.createElement('canvas');
gradientCanvas.width = gradientImage.width;
gradientCanvas.height = gradientImage.height;
// draw the image
gradientCanvas.getContext('2d').drawImage( gradientImage, 0, 0, gradientImage.width, gradientImage.height );
// a function to grab the pixel color based on a normalized percentage value
gradientCanvas.getColor = function( percentage ){
return this.getContext('2d').getImageData(percentage \* gradientImage.width,0, 1, 1).data;
}
จากนั้นระบบจะใช้วิธีการเดียวกันนี้ในการระบายสีดาวแต่ละดวงในมุมมองโมเดลดาว
การจัดการกับ Shader
ตลอดทั้งโปรเจ็กต์ เราพบว่าต้องเขียน Shader มากขึ้นเรื่อยๆ เพื่อให้ได้เอฟเฟกต์ภาพทั้งหมด ฉันเขียนโปรแกรมโหลดเชดเดอร์ที่กําหนดเองเพื่อวัตถุประสงค์นี้เนื่องจากเบื่อที่จะต้องใส่เชดเดอร์ไว้ใน index.html
// list of shaders we'll load
var shaderList = ['shaders/starsurface', 'shaders/starhalo', 'shaders/starflare', 'shaders/galacticstars', /*...etc...*/];
// a small util to pre-fetch all shaders and put them in a data structure (replacing the list above)
function loadShaders( list, callback ){
var shaders = {};
var expectedFiles = list.length \* 2;
var loadedFiles = 0;
function makeCallback( name, type ){
return function(data){
if( shaders[name] === undefined ){
shaders[name] = {};
}
shaders[name][type] = data;
// check if done
loadedFiles++;
if( loadedFiles == expectedFiles ){
callback( shaders );
}
};
}
for( var i=0; i<list.length; i++ ){
var vertexShaderFile = list[i] + '.vsh';
var fragmentShaderFile = list[i] + '.fsh';
// find the filename, use it as the identifier
var splitted = list[i].split('/');
var shaderName = splitted[splitted.length-1];
$(document).load( vertexShaderFile, makeCallback(shaderName, 'vertex') );
$(document).load( fragmentShaderFile, makeCallback(shaderName, 'fragment') );
}
}
ฟังก์ชัน loadShaders() จะรับรายการชื่อไฟล์ Shader (คาดว่าจะเป็น .fsh สำหรับ Shader แบบเศษส่วน และ .vsh สำหรับ Shader แบบเวิร์กเทอร์เทกซ์) พยายามโหลดข้อมูล แล้วแทนที่รายการด้วยออบเจ็กต์ ผลลัพธ์สุดท้ายจะอยู่ในยูนิฟอร์ม THREE.js ที่คุณส่งผ่านชิเดอร์ได้ดังนี้
var galacticShaderMaterial = new THREE.ShaderMaterial( {
vertexShader: shaderList.galacticstars.vertex,
fragmentShader: shaderList.galacticstars.fragment,
/_..._/
});
เราอาจใช้ require.js ได้ แต่จะต้องประกอบโค้ดใหม่เพื่อวัตถุประสงค์นี้ โซลูชันนี้แม้จะง่ายกว่ามาก แต่ก็อาจปรับปรุงได้ อาจเป็นส่วนขยาย THREE.js ก็ได้ หากมีคำแนะนำหรือวิธีดำเนินการที่ดีขึ้น โปรดแจ้งให้เราทราบ
ป้ายกำกับข้อความ CSS บน THREE.js
ในโปรเจ็กต์ล่าสุดของเราอย่าง Small Arms Globe เราลองทำป้ายกำกับข้อความให้ปรากฏที่ด้านบนของฉาก THREE.js วิธีการที่เราใช้จะคำนวณตำแหน่งโมเดลสัมบูรณ์ของตำแหน่งที่ต้องการให้ข้อความปรากฏ จากนั้นจะแก้ไขตำแหน่งหน้าจอโดยใช้ THREE.Projector() และสุดท้ายจะใช้ CSS "top" และ "left" เพื่อวางองค์ประกอบ CSS ในตำแหน่งที่ต้องการ
เวอร์ชันแรกๆ ของโปรเจ็กต์นี้ใช้เทคนิคเดียวกันนี้ แต่เราอยากลองใช้วิธีอื่นที่ Luis Cruz อธิบายไว้
แนวคิดพื้นฐานคือจับคู่การเปลี่ยนรูปแบบเมทริกซ์ของ CSS3D กับกล้องและฉากของ THREE จากนั้นคุณจะสามารถ "วาง" องค์ประกอบ CSS ในแบบ 3 มิติได้ราวกับว่าวางไว้บนฉากของ THREE แต่มีข้อจำกัดอยู่ เช่น คุณไม่สามารถวางข้อความไว้ใต้ออบเจ็กต์ THREE.js ซึ่งยังคงเร็วกว่าการพยายามจัดวางโดยใช้แอตทริบิวต์ CSS "top" และ "left" มาก
ดูเดโม (และโค้ดในมุมมองซอร์สโค้ด) ของการดำเนินการนี้ได้ที่นี่ อย่างไรก็ตาม เราพบว่าลําดับของเมทริกซ์มีการเปลี่ยนแปลงไปสําหรับ THREE.js ฟังก์ชันที่อัปเดตแล้ว
/_ Fixes the difference between WebGL coordinates to CSS coordinates _/
function toCSSMatrix(threeMat4, b) {
var a = threeMat4, f;
if (b) {
f = [
a.elements[0], -a.elements[1], a.elements[2], a.elements[3],
a.elements[4], -a.elements[5], a.elements[6], a.elements[7],
a.elements[8], -a.elements[9], a.elements[10], a.elements[11],
a.elements[12], -a.elements[13], a.elements[14], a.elements[15]
];
} else {
f = [
a.elements[0], a.elements[1], a.elements[2], a.elements[3],
a.elements[4], a.elements[5], a.elements[6], a.elements[7],
a.elements[8], a.elements[9], a.elements[10], a.elements[11],
a.elements[12], a.elements[13], a.elements[14], a.elements[15]
];
}
for (var e in f) {
f[e] = epsilon(f[e]);
}
return "matrix3d(" + f.join(",") + ")";
}
เนื่องจากทุกอย่างได้รับการเปลี่ยนรูปแบบแล้ว ข้อความจึงไม่ได้หันหน้าเข้าหากล้องอีกต่อไป ทางออกคือการใช้ THREE.Gyroscope() ซึ่งบังคับให้ Object3D "สูญเสีย" การวางแนวที่รับค่ามาจากฉาก เทคนิคนี้เรียกว่า "การติดป้ายโฆษณา" และ Gyroscope เหมาะสําหรับงานนี้อย่างยิ่ง
สิ่งที่ยอดเยี่ยมมากคือ DOM และ CSS ปกติทั้งหมดยังคงทำงานอยู่ เช่น คุณสามารถวางเมาส์เหนือป้ายกำกับข้อความ 3 มิติเพื่อให้ป้ายกำกับเรืองแสงด้วยเงา
เมื่อซูมเข้า เราพบว่าการปรับขนาดแบบอักษรทำให้เกิดปัญหาในการวางตำแหน่ง ปัญหานี้อาจเกิดจากระยะห่างระหว่างตัวอักษรและการเว้นวรรคของข้อความ อีกปัญหาหนึ่งคือข้อความจะแตกเป็นพิกเซลเมื่อซูมเข้า เนื่องจากโปรแกรมแสดงผล DOM จะถือว่าข้อความที่แสดงผลเป็นสี่เหลี่ยมจัตุรัสที่มีพื้นผิว ซึ่งเป็นสิ่งที่ควรทราบเมื่อใช้วิธีการนี้ เมื่อมองย้อนกลับไป เราน่าจะใช้ข้อความขนาดแบบอักษรยักษ์ไปเลย และนี่อาจเป็นแนวทางสําหรับการสำรวจในอนาคต ในโปรเจ็กต์นี้ เรายังใช้ป้ายกำกับข้อความตำแหน่ง CSS "บน/ซ้าย" ที่อธิบายไว้ก่อนหน้านี้สำหรับองค์ประกอบขนาดเล็กมากที่มาพร้อมกับดาวเคราะห์ในระบบสุริยะ
การเล่นเพลงและการวนซ้ำ
ท่อนเพลงที่เล่นระหว่าง "แผนที่กาแล็กซี่" ของ Mass Effect เป็นผลงานของนักแต่งเพลง Sam Hulick และ Jack Wall จาก Bioware ซึ่งให้อารมณ์อย่างที่ฉันต้องการให้ผู้เข้าชมได้รับ เราต้องการใส่เพลงในโปรเจ็กต์เนื่องจากเราคิดว่าเพลงเป็นส่วนสำคัญของบรรยากาศ ซึ่งช่วยสร้างความรู้สึกประหลาดใจและน่าทึ่งที่เราพยายามนำเสนอ
โปรดิวเซอร์ของเรา Valdean Klump ได้ติดต่อ Sam ซึ่งมีเพลง "การตัดต่อ" จำนวนมากจาก Mass Effect ที่เขาอนุญาตให้เรานำมาใช้ แทร็กนี้มีชื่อว่า "In a Strange Land"
ฉันใช้แท็กเสียงสำหรับการเล่นเพลง แต่แม้ใน Chrome แอตทริบิวต์ "loop" ก็ไม่น่าเชื่อถือ ในบางครั้งก็เล่นวนไม่ได้ สุดท้ายแล้ว เราใช้แฮ็กแท็กเสียงคู่นี้เพื่อตรวจสอบการสิ้นสุดการเล่นและวนไปยังแท็กอื่นเพื่อเล่น สิ่งที่น่าผิดหวังคือภาพนิ่งนี้ไม่ได้วนซ้ำอย่างสมบูรณ์แบบตลอดเวลา แต่เราคิดว่านี่เป็นสิ่งที่ดีที่สุดที่เราทำได้
var musicA = document.getElementById('bgmusicA');
var musicB = document.getElementById('bgmusicB');
musicA.addEventListener('ended', function(){
this.currentTime = 0;
this.pause();
var playB = function(){
musicB.play();
}
// make it wait 15 seconds before playing again
setTimeout( playB, 15000 );
}, false);
musicB.addEventListener('ended', function(){
this.currentTime = 0;
this.pause();
var playA = function(){
musicA.play();
}
// otherwise the music will drive you insane
setTimeout( playA, 15000 );
}, false);
// okay so there's a bit of code redundancy, I admit it
musicA.play();
สิ่งที่ควรปรับปรุง
หลังจากทํางานกับ THREE.js ไปสักพักแล้ว เรารู้สึกว่าข้อมูลของเราผสมกับโค้ดมากเกินไป ตัวอย่างเช่น เมื่อกำหนดวัสดุ พื้นผิว และคำสั่งเรขาคณิตในบรรทัด แท้จริงแล้วฉันกำลัง "โมเดล 3 มิติด้วยโค้ด" ซึ่งเป็นเรื่องที่น่าเสียดายมาก และเป็นสิ่งที่เราพยายามปรับปรุงอย่างมากในอนาคตด้วย THREE.js เช่น การกําหนดข้อมูลวัสดุในไฟล์แยกต่างหาก โดยควรที่จะดูและปรับแต่งได้ในบางบริบท และนํากลับมาไว้ในโปรเจ็กต์หลักได้
เพื่อนร่วมงานของเราอย่าง Ray McClure ยังได้ใช้เวลาสร้าง "เสียงอวกาศ" เจ๋งๆ ขึ้นมาด้วย แต่ก็ต้องตัดออกเนื่องจาก Web Audio API ไม่เสถียรและทำให้ Chrome ขัดข้องเป็นครั้งคราว เราเสียใจกับปัญหาที่เกิดขึ้น แต่เราก็ได้เรียนรู้เกี่ยวกับเสียงมากขึ้นสำหรับผลงานในอนาคต ขณะเขียนนี้ เราได้รับแจ้งว่า Web Audio API ได้รับการแก้ไขแล้ว จึงเป็นไปได้ว่าตอนนี้ปัญหานี้อาจได้รับการแก้ไขแล้ว โปรดคอยติดตามดูในอนาคต
องค์ประกอบการพิมพ์ที่จับคู่กับ WebGL ยังคงเป็นปัญหาอยู่ และเราไม่แน่ใจ 100% ว่าสิ่งที่เรากำลังทำอยู่นี้ถูกต้อง ยังคงให้ความรู้สึกเหมือนการแฮ็ก ในอนาคต THREE เวอร์ชันใหม่ๆ ที่มีโปรแกรมแสดงผล CSS ที่กำลังมาแรงอาจนำมาใช้เพื่อรวม 2 โลกเข้าด้วยกันได้ดียิ่งขึ้น
เครดิต
ขอขอบคุณ Aaron Koblin ที่ให้โอกาสเราทำโปรเจ็กต์นี้ Jono Brandel สำหรับการออกแบบ UI + การใช้งานที่ยอดเยี่ยม การจัดรูปแบบตัวอักษร และการใช้งานบทแนะนำ Valdean Klump สำหรับการตั้งชื่อโปรเจ็กต์และข้อความทั้งหมด Sabah Ahmed ที่ช่วยเราขอสิทธิ์การใช้งานสำหรับแหล่งข้อมูลและรูปภาพจำนวนมาก Clem Wright สำหรับการติดต่อบุคคลที่เหมาะสมสำหรับการเผยแพร่ Doug Fritz สำหรับความเป็นเลิศทางเทคนิค George Brower ที่สอน JS และ CSS ให้ฉัน และ Mr. Doob สำหรับ THREE.js