สวัสดี ผมชื่อ Michael Chang ทำงานร่วมกับทีม Data Arts ที่ Google เมื่อเร็วๆ นี้เราได้ทำการทดลอง 100,000 ดาว ซึ่งเป็นการทดลองของ Chrome ที่แสดงภาพดาวในบริเวณใกล้เคียง โปรเจ็กต์สร้างขึ้นด้วย THREE.js และ CSS3D ในกรณีศึกษานี้ ฉันจะอธิบายเกี่ยวกับกระบวนการค้นหา แชร์เทคนิคบางอย่างในการเขียนโปรแกรม และปิดท้ายด้วยความคิดเห็นเล็กๆ น้อยๆ เพื่อการปรับปรุงในอนาคต
หัวข้อที่กล่าวถึงในที่นี้ค่อนข้างกว้างและต้องอาศัยความรู้เรื่อง THREE.js เราหวังว่าคุณจะยังเพลิดเพลินกับความรู้เบื้องต้นทางเทคนิคได้ คุณข้ามไปยังส่วนที่สนใจได้โดยใช้ปุ่มสารบัญทางด้านขวา ก่อนอื่น เราจะแสดงส่วนการแสดงผลของโปรเจ็กต์ ตามด้วยการจัดการตัวปรับแสงเงา และสุดท้ายวิธีใช้ป้ายกำกับข้อความ CSS ร่วมกับ WebGL
การค้นพบอวกาศ
หลังจากที่เราดำเนินการกับ Small Arms Globe เสร็จแล้วไม่นาน ฉันก็ทำการทดลองใช้อนุภาค THREE.js ที่มีระยะชัดลึก ฉันสังเกตเห็นว่าฉันสามารถเปลี่ยน "สเกล" ที่ตีความแล้วของฉากได้โดยปรับปริมาณของเอฟเฟ็กต์ที่ใช้ เมื่อเอฟเฟกต์ระยะชัดลึกสุดขีด วัตถุที่อยู่ไกลจะพร่ามัวมาก คล้ายกับการทำงานของการถ่ายภาพแบบทิลท์ชิฟท์ในการสร้างภาพลวงตาของการมองภาพในกล้องจุลทรรศน์ ในทางกลับกัน การลดเอฟเฟกต์ทำให้ดูเหมือนว่าคุณกำลังมองออกไปในห้วงลึกของอวกาศ
ฉันเริ่มตามล่าหาข้อมูลที่ฉันสามารถใช้เพื่อแทรกตำแหน่งอนุภาค เส้นทางที่นำฉันไปยังฐานข้อมูล HYG ของ astronexus.com การรวบรวมแหล่งข้อมูล 3 แหล่ง (Hipparcos, Yale Bright Star Catalog และ Gliese/Jahreiss Catalog) พร้อมพิกัด xyz Cartesian ที่คำนวณไว้ล่วงหน้า มาเริ่มกันเลย
ต้องใช้เวลาประมาณ 1 ชั่วโมงในการแฮ็กสิ่งที่สร้างข้อมูลรูปดาวให้ปรากฏในพื้นที่ 3 มิติ ชุดข้อมูลมีดาว 119,617 ดวง ดังนั้นการแสดงดาวแต่ละดวงด้วยอนุภาคจึงไม่ใช่ปัญหาสำหรับ GPU สมัยใหม่ และยังมีดาวที่ระบุแยกกัน 87 ดวง ดังนั้นผมจึงสร้างการวางซ้อนเครื่องหมาย CSS โดยใช้เทคนิคเดียวกับที่อธิบายไว้ใน Small Arms Globe
ระหว่างนี้ฉันเพิ่งดูซีรีส์ Mass Effect จบไป ในเกม ผู้เล่นจะได้รับเชิญให้ออกสำรวจกาแล็กซีและสแกนดาวเคราะห์ต่างๆ และอ่านประวัติของสิ่งมีชีวิตที่ฟังดูเป็นวิกิพีเดีย ซึ่งสมมติสมบูรณ์ เช่น สายพันธุ์ที่เติบโตบนโลก ประวัติศาสตร์ทางธรณีวิทยา และอื่นๆ อีกมากมาย
เมื่อเรารู้ข้อมูลที่มีอยู่จริงเกี่ยวกับดวงดาวมากมาย ก็น่าจะให้ข้อมูลที่แท้จริงเกี่ยวกับกาแล็กซีได้ในลักษณะเดียวกัน เป้าหมายสูงสุดสำหรับโปรเจ็กต์นี้คือการทำให้ข้อมูลนี้มีชีวิต เปิดโอกาสให้ผู้ชมได้สำรวจกาแล็กซี à la Mass Effect เพื่อเรียนรู้เกี่ยวกับดวงดาวและการกระจายตัว และหวังว่าจะเป็นตัวจุดประกายความน่าเกรงขามและความสงสัยเกี่ยวกับอวกาศ ในที่สุด
ฉันควรจะเกริ่นนำส่วนที่เหลือของกรณีศึกษานี้ด้วยการบอกว่าฉันไม่ได้เป็นนักดาราศาสตร์ และนี่เป็นผลงานของงานวิจัยมือสมัครเล่นที่ได้รับการสนับสนุนโดยคำแนะนำจากผู้เชี่ยวชาญภายนอก โปรเจ็กต์นี้ต้องตีความว่าเป็นการตีความด้านอวกาศโดยศิลปินอย่างแน่นอน
การสร้างกาแล็กซี
แผนของผมคือการสร้างแบบจำลองของกาแล็กซีที่สามารถแสดงข้อมูลของดาวได้ในบริบท และหวังว่าจะให้ทัศนียภาพที่ยอดเยี่ยมของสถานที่ของเราในทางช้างเผือก
ในการสร้างทางช้างเผือก ฉันสร้างอนุภาค 100,000 ชิ้นและนำมาวางเป็นเกลียวโดยการจำลองการก่อตัวของแขนกาแล็กซี ฉันไม่ได้กังวลเรื่องเทคนิคการก่อตัวของแขนเกลียวมากนัก เพราะนี่เป็นโมเดลตัวแทน ไม่ใช่รูปแบบทางคณิตศาสตร์ อย่างไรก็ตาม ฉันพยายามทำให้จำนวนแขนเกลียวถูกต้องมากขึ้นหรือน้อยลง และหมุนไปใน "ทิศทางที่ถูกต้อง"
ในแบบจำลองทางช้างเผือกเวอร์ชันต่อๆ ไป ผมได้ลดความสำคัญของการใช้อนุภาคโดยให้ภาพระนาบของกาแล็กซีร่วมไปกับอนุภาคเหล่านี้ โดยหวังว่าจะทำให้มีลักษณะเป็นภาพถ่ายมากขึ้น ภาพจริงเป็นภาพกาแล็กซีกังหัน NGC 1232 ที่อยู่ห่างจากเราประมาณ 70 ล้านปีแสง โดยปรับแต่งให้ดูเหมือนทางช้างเผือก
ผมตัดสินใจตั้งแต่แรกเริ่มที่จะเป็นตัวแทนหน่วย GL หนึ่งหน่วย ซึ่งก็คือพิกเซลใน 3 มิติเป็นปีแสง 1 หน่วย ซึ่งเป็นรูปแบบที่การรวมการวางตำแหน่งโฆษณาไว้เป็นหนึ่งเดียวสำหรับทุกสิ่งที่เห็นภาพ และก็ต้องขออภัยที่เกิดปัญหาเกี่ยวกับความแม่นยำที่ร้ายแรงในภายหลัง
อีกวิธีการหนึ่งที่ผมตัดสินใจคือหมุนทั้งฉากแทนที่จะหมุนกล้อง ซึ่งเป็นสิ่งที่ผมทำแล้วในโครงการอื่นๆ อีก 2-3 โปรเจ็กต์ ข้อดีอย่างหนึ่งคือการวางทุกอย่างลงใน "เทิร์นเทเบิล" การลากเมาส์ไปทางซ้ายและขวาเพื่อหมุนวัตถุที่ต้องการ แต่การซูมเข้าเป็นเพียงการเปลี่ยน Camera.position.z เท่านั้น
ขอบเขตการมองเห็น (หรือ FOV) ของกล้องจะเป็นแบบไดนามิกด้วย เมื่อมองออก ขอบเขตการมองเห็นจะกว้างขึ้น พร้อมเข้าถึงกาแล็กซีมากขึ้นเรื่อยๆ แต่เมื่อเลื่อนเข้าหาดาว ขอบเขตการมองเห็นจะแคบลง ซึ่งช่วยให้กล้องดูสิ่งที่ไม่มีที่สิ้นสุดได้ (เมื่อเทียบกับกาแล็กซี) โดยยัด FOV ลงไปยังแว่นขยายที่เหมือนพระเจ้า โดยไม่ต้องจัดการกับปัญหาภาพหลุดใกล้ระนาบ
จากที่นี่ ผมสามารถ "วาง" ดวงอาทิตย์ที่ห่างจากแกนกลางกาแล็กซี หลายหน่วย ผมยังสามารถแสดงภาพขนาดสัมพัทธ์ของระบบสุริยะด้วยการทำแผนที่รัศมีของหน้าผา Kuiper (สุดท้ายแล้วผมเลือกแสดงภาพเมฆออริก) แทน ภายในระบบสุริยะนี้ ผมยังสามารถดูวงโคจรของโลกอย่างง่าย และรัศมีจริงของดวงอาทิตย์เมื่อเปรียบเทียบกัน
ดวงอาทิตย์แสดงผลได้ยาก ผมต้องใช้เทคนิคกราฟิกแบบเรียลไทม์หลายอย่างที่ผมรู้จัก พื้นผิวของดวงอาทิตย์คือฟองอากาศร้อนของพลาสมา และจำเป็นต้องมีการเคลื่อนตัวของแสงอาทิตย์ที่เปลี่ยนแปลงไปตามกาลเวลา การจำลองนี้จำลองผ่านพื้นผิวบิตแมปของภาพอินฟราเรดของพื้นผิวพลังงานแสงอาทิตย์ ตัวปรับเฉดสีพื้นผิวจะสร้างสีตามโทนสีเทาของพื้นผิวนี้ และดำเนินการค้นหาในระดับสีแยกต่างหาก เมื่อการค้นหานี้ถูกเปลี่ยนเมื่อเวลาผ่านไป จะทําให้เกิดความบิดเบี้ยวในลักษณะลาวา
ส่วนเทคนิคโคโรนาของดวงอาทิตย์ก็ใช้เทคนิคที่คล้ายกัน แต่จะเป็นการ์ดสไปรท์แบบแบนราบที่หันเข้าหากล้องเสมอโดยใช้ https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js
แสงอาทิตย์ถูกสร้างขึ้นผ่านจุดยอดมุมและตัวปรับเฉดสีแบบเศษส่วนที่ใช้กับทรงห่วง โดยหมุนรอบขอบของพื้นผิวพลังงานแสงอาทิตย์ ตัวปรับแสงเงาจุดยอดมีฟังก์ชันนอยส์ที่ทำให้ค่อยๆ ตัดเป็นรูปคล้ายหยด
ตรงนี้เองที่ผมเริ่มพบปัญหาระนาบขัดแย้งเนื่องจากความแม่นยำของ GL ตัวแปรสำหรับความแม่นยำทั้งหมดกำหนดไว้ล่วงหน้าใน THREE.js ผมเลยเพิ่มความแม่นยำให้ไม่ได้ถ้าต้องทำงานอะไรมากมาย ปัญหาเรื่องความแม่นยําไม่ได้เลวร้ายเท่าต้นทาง แต่เมื่อฉันเริ่มสร้างโมเดลระบบดาวอื่นๆ ปัญหาก็เริ่มเกิดขึ้น
ที่ผมใช้ก็มีเคล็ดลับอยู่ 2-3 อย่างเพื่อลดการต่อสู้กับ Z Material.polygonoffset ของ THREE เป็นพร็อพเพอร์ตี้ที่อนุญาตให้แสดงผลรูปหลายเหลี่ยมในตำแหน่งต่างๆ ที่รับรู้ได้ (เท่าที่ฉันเข้าใจ) ซึ่งใช้ในการบังคับให้ระนาบโคโรนาแสดงผลบนพื้นผิวดวงอาทิตย์เสมอ ส่วนเบื้องล่างนี้มี "รัศมี" ของดวงอาทิตย์เพื่อให้แสงที่ส่องออกจากทรงกลม
ปัญหาอีกอย่างที่เกี่ยวข้องกับความแม่นยำคือ นางแบบดาวจะเริ่มกระตุกขณะที่ซูมเข้า เพื่อแก้ไขปัญหานี้ ฉันต้อง "ลบ" การหมุนฉาก แล้วหมุนโมเดลดาวและแผนที่สภาพแวดล้อมแยกกันเพื่อสร้างภาพลวงตาว่าคุณกำลังโคจรรอบดาว
กำลังสร้าง Lensflare
การแสดงภาพอวกาศเป็นจุดที่ผมรู้สึกว่าสามารถหลบเลี่ยงการใช้แสงแฟลร์มากเกินไป THREE.LensFlare ทำหน้าที่นี้ สิ่งที่ผมต้องทำคือใส่หกเหลี่ยมอนามอร์ฟิกและ JJ Abrams 1 ขีด ตัวอย่างด้านล่างแสดงวิธีสร้างขึ้นในฉากของคุณ
// 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 แต่ในตัวอย่างนี้ ผมจะส่งผ่านในแบบลอยเพื่อทำการค้นหาระดับสีในที่สุด
ระดับสีเป็นแบบนี้ แต่ฉันต้องเข้าถึงข้อมูลสีบิตแมปจาก 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;
}
จากนั้นจึงใช้วิธีการเดียวกันนี้ในการระบายสีดาวแต่ละดวงในมุมมองโมเดลดาว
การปรับแสงเงา
ตลอดทำโปรเจ็กต์ ฉันพบว่าฉันต้องเขียนเฉดสีมากขึ้นเพื่อให้เอฟเฟกต์ภาพทั้งหมดสำเร็จ ฉันเขียนตัวโหลดตัวปรับเฉดสีที่กำหนดเองสำหรับวัตถุประสงค์นี้เพราะฉันเบื่อหน่ายกับการมีตัวปรับเฉดสีอยู่ใน 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') );
}
}
_ ผลลัพธ์สุดท้ายจะอยู่ในเครื่องแบบ THREE.js ของคุณ ซึ่งคุณสามารถส่งตัวให้เฉดสีไปที่เครื่องแบบ THREE.js ได้โดยทำดังนี้
var galacticShaderMaterial = new THREE.ShaderMaterial( {
vertexShader: shaderList.galacticstars.vertex,
fragmentShader: shaderList.galacticstars.fragment,
/_..._/
});
ฉันน่าจะใช้ required.js ก็ได้แม้ว่าอาจต้องการประกอบโค้ดบางอย่างอีกครั้งเพื่อวัตถุประสงค์นี้ แม้ว่าจะง่ายกว่านี้ แต่เราสามารถปรับปรุงโซลูชันนี้ ซึ่งอาจจะเป็นส่วนขยาย THREE.js ก็ได้ โปรดแจ้งให้เราทราบหากคุณมีคำแนะนำหรือวิธีพัฒนาให้ดียิ่งขึ้น
ป้ายกำกับข้อความ CSS ที่ด้านบนของ THREE.js
ในโครงการล่าสุดของเรา Small Arms Globe ผมชอบทำให้ป้ายกำกับข้อความปรากฏขึ้นที่ด้านบนของฉาก THREE.js วิธีที่ฉันใช้จะคำนวณตำแหน่งโมเดลสัมบูรณ์ของตำแหน่งที่ฉันต้องการให้ข้อความปรากฏ จากนั้นกำหนดตำแหน่งหน้าจอโดยใช้ THREE.Projector() และสุดท้ายใช้ CSS "top" และ "left" เพื่อวางองค์ประกอบ CSS ในตำแหน่งที่ต้องการ
การทำซ้ำช่วงแรกๆ ในโปรเจ็กต์นี้ใช้เทคนิคเดียวกันนี้ แต่ฉันอยากลองใช้วิธีอื่นที่ Luis Cruz อธิบายไว้
แนวคิดเบื้องต้น: จับคู่การแปลงเมทริกซ์ของ CSS3D เป็นกล้องและฉากของ 3 มิติ จากนั้นคุณจะ "วาง" องค์ประกอบ CSS ในแบบ 3 มิติ ราวกับว่าอยู่บนฉากของ 3 มิติได้ แต่มีข้อจำกัดบางประการ ตัวอย่างเช่น คุณจะไม่สามารถให้ข้อความอยู่ใต้ออบเจ็กต์ 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 "ด้านบน/ซ้าย" ที่อธิบายไว้ก่อนหน้านี้ สำหรับองค์ประกอบขนาดเล็กมากซึ่งมีดาวเคราะห์ควบคู่ในระบบสุริยะ
การเล่นเพลงและการวนซ้ำ
ผลงานเพลงที่เล่นระหว่าง "Galactic Map" ของ Mass Effect คือเพลงของ Sam Hulick และ Jack Wall นักประพันธ์เพลงไบโอแวร์ ซึ่งให้อารมณ์ที่ผมอยากให้ผู้เข้าชมได้สัมผัส เราอยากได้เพลงบางเพลงในโปรเจ็กต์เพราะรู้สึกว่าเป็นส่วนสำคัญของบรรยากาศ ซึ่งก็ช่วยสร้างความน่าเกรงขามและความมหัศจรรย์ที่เราพยายามจะมุ่งหวัง
โปรดิวเซอร์ Valdean Klump ได้ติดต่อแซมที่มีเพลง "ตัดพื้น" จาก Mass Effect มากๆ เขาอนุญาตให้เราใช้ แทร็กมีชื่อว่า "In a Strange Land"
ฉันใช้แท็กเสียงสำหรับการเล่นเพลง อย่างไรก็ตาม แม้แต่ใน Chrome แอตทริบิวต์ "ลูป" ก็ไม่น่าเชื่อถือ บางครั้งก็ใช้การวนซ้ำไม่ได้ สุดท้าย การแฮ็กแบบแท็กเสียงคู่นี้ก็ใช้เพื่อตรวจหาการสิ้นสุดของการเล่นและการวนไปยังแท็กอื่นเพื่อเล่น สิ่งที่น่าผิดหวังคือ ตอนที่ยังนี้ไม่วนซ้ำแบบแนบเนียนตลอดเวลา ผมคิดว่านี่เป็นสิ่งที่ดีที่สุดที่ทำได้
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 ยังได้ใช้เวลาสร้าง "เสียงรบกวนอวกาศ" ที่ยอดเยี่ยมและต้องตัดออกเนื่องจาก API เสียงในเว็บไม่เสถียร ทำให้ Chrome ขัดข้องอยู่บ่อยๆ น่าเสียดายจัง แต่นั่นก็ทำให้เราคิดถึงพื้นที่อย่างเต็มที่สำหรับการทำงานในอนาคต จากการเขียนนี้ เราได้รับแจ้งว่า Web Audio API ได้รับการแพตช์แล้ว จึงเป็นไปได้ที่การทดสอบจะทํางานได้ในตอนนี้ ซึ่งเป็นสิ่งที่รอดูในอนาคต
องค์ประกอบแบบตัวอักษรที่จับคู่กับ WebGL ยังคงเป็นเรื่องที่ท้าทาย และผมก็ไม่แน่ใจ 100% ว่าสิ่งที่เราทำอยู่นี้เป็นวิธีที่ถูกต้อง แต่ก็ยังดูเหมือนถูกแฮ็กอยู่ บางทีเวอร์ชันต่อๆ ไปของ THREE ที่มีตัวแสดงผล CSS ที่กำลังมาแรงสามารถใช้เพื่อรวม 2 โลกเข้าด้วยกันได้ดียิ่งขึ้น
เครดิต
ต้องขอบคุณ Aaron Koblin ที่ให้เราเดินทางไปเมืองด้วยโปรเจ็กต์นี้ Jono Brandel สำหรับการออกแบบ UI ที่ยอดเยี่ยม รวมถึงการใช้งาน การจัดการประเภท และการใช้งานการทัวร์ชม Valdean Klump ให้ชื่อและสำเนาทั้งหมดแก่โครงการ ซาบาห์ อาห์เหม็ดสำหรับการล้างเมตริกสิทธิ์การใช้จำนวนมากสำหรับแหล่งที่มาของข้อมูลและรูปภาพ Clem Wright ที่ติดต่อกลุ่มคนที่เหมาะสมสำหรับการเผยแพร่ Doug Fritz สำหรับความเป็นเลิศทางเทคนิค George Brower ช่วยสอน JS และ CSS ให้ฉัน และแน่นอน Mr. Doob สำหรับ THREE.js