ข้อมูลเบื้องต้นเกี่ยวกับลูกโลก 3 มิติของสิ่งมหัศจรรย์ของโลก
หากเคยดูเว็บไซต์สถานที่น่าทึ่งทั่วโลกของ Google ที่เพิ่งเปิดตัวในเบราว์เซอร์ที่รองรับ WebGL คุณอาจเห็นลูกโลกหมุนเก๋ๆ ที่ด้านล่างของหน้าจอ บทความนี้จะอธิบายวิธีการทำงานของลูกโลกและสิ่งที่ใช้ในการสร้าง
ข้อมูลโดยย่อคือ ลูกโลกสถานที่มหัศจรรย์ของโลกเป็นลูกโลก WebGL เวอร์ชันที่ปรับแต่งมาอย่างมากโดยทีมศิลปะจากข้อมูลของ Google เรานำลูกโลกเดิมมา ตัดส่วนแผนภูมิแท่งออก เปลี่ยนชิดเดอร์ เพิ่มเครื่องหมาย HTML ที่คลิกได้เก๋ๆ และเรขาคณิตทวีป Natural Earth จากเดโม GlobeTweeter ของ Mozilla (ขอขอบคุณ Cedric Pinson เป็นอย่างยิ่ง) ทั้งหมดนี้เพื่อสร้างลูกโลกแบบเคลื่อนไหวที่ดูดีซึ่งเข้ากับรูปแบบสีของเว็บไซต์และเพิ่มความซับซ้อนให้กับเว็บไซต์อีกระดับ
ข้อมูลสรุปการออกแบบสำหรับลูกโลกคือให้มีแผนที่ภาพเคลื่อนไหวที่ดูดีพร้อมเครื่องหมายที่คลิกได้ซึ่งวางไว้บนมรดกโลก เมื่อพิจารณาถึงสิ่งดังกล่าวแล้ว เราจึงเริ่มมองหาสิ่งที่เหมาะสม สิ่งแรกที่นึกถึงคือลูกโลก WebGL ที่ทีมศิลปะจากข้อมูลของ Google สร้างขึ้น มันเป็นลูกโลกที่ดูเท่ มีอะไรให้เราช่วยอีกไหม
การตั้งค่าลูกโลก WebGL
ขั้นตอนแรกในการสร้างวิดเจ็ตลูกโลกคือการดาวน์โหลด WebGL Globe และทำให้พร้อมใช้งาน โลก WebGL พร้อมให้ใช้งานทางออนไลน์ที่ Google Code และดาวน์โหลดและใช้งานได้ง่าย ดาวน์โหลดและแตกไฟล์ zip แล้วไปที่โฟลเดอร์นั้นเพื่อเรียกใช้เว็บเซิร์ฟเวอร์พื้นฐาน: python -m SimpleHTTPServer
(โปรดทราบว่าตัวเลือกนี้ไม่ได้เปิด UTF-8 โดยค่าเริ่มต้น แต่คุณใช้ตัวเลือกนี้ได้) ตอนนี้คุณควรเห็นลูกโลก WebGL เมื่อไปที่ http://localhost:8000/globe/globe.html
เมื่อลูกโลก WebGL ทำงานได้ เราก็ถึงเวลาตัดส่วนที่ไม่จำเป็นทั้งหมดออก เราแก้ไข HTML เพื่อตัดข้อมูล UI ออกและนำการตั้งค่ากราฟแท่งโลกออกจากฟังก์ชันการเริ่มต้นโลก เมื่อสิ้นสุดกระบวนการดังกล่าว ฉันมีลูกโลก WebGL พื้นฐานมากบนหน้าจอ คุณหมุนไปมาก็ได้และดูเท่ดี แต่แค่นั้น
เราได้ตัดสิ่งที่ไม่จำเป็นออกโดยลบองค์ประกอบ UI ทั้งหมดจาก index.html ของโลกและแก้ไขสคริปต์เริ่มต้นให้มีลักษณะดังนี้
if(!Detector.webgl){
Detector.addGetWebGLMessage();
} else {
var container = document.getElementById('container');
var globe = new DAT.Globe(container);
globe.animate();
}
การเพิ่มเรขาคณิตของทวีป
เราต้องการให้กล้องอยู่ใกล้กับพื้นผิวของบอลโลก แต่เมื่อทดสอบการซูมบอลโลกเข้า ความละเอียดของพื้นผิวก็ปรากฏให้เห็นอย่างชัดเจน เมื่อซูมเข้า พื้นผิวของบอลโลก WebGL จะกลายเป็นก้อนๆ และเบลอ เราอาจใช้ภาพขนาดใหญ่กว่านี้ได้ แต่จะทำให้การดาวน์โหลดและเรียกใช้ลูกโลกช้าลง เราจึงเลือกใช้การนำเสนอแบบเวกเตอร์ของมวลแผ่นดินและพรมแดน
สำหรับเรขาคณิตของมวลแผ่นดิน เราใช้การสาธิต GlobeTweeter แบบโอเพนซอร์สและโหลดโมเดล 3 มิติในนั้นไปยัง Three.js เมื่อโหลดและแสดงผลโมเดลแล้ว ก็ถึงเวลาเริ่มขัดเกลารูปลักษณ์ของลูกโลก ปัญหาแรกคือโมเดลมวลแผ่นดินของโลกกลมไม่พอที่จะวางซ้อนกับโลก WebGL ได้ เราจึงเขียนอัลกอริทึมการแยกเมชอย่างรวดเร็วซึ่งทำให้โมเดลมวลแผ่นดินกลมมากขึ้น
เมื่อใช้โมเดลมวลแผ่นดินทรงกลม เราสามารถวางโมเดลให้เอียงออกจากพื้นผิวโลกได้เล็กน้อย เพื่อสร้างทวีปที่ลอยอยู่โดยวาดเส้นขอบด้วยเส้นสีดำ 2 พิกเซลใต้ทวีปเพื่อใช้เป็นเงา นอกจากนี้ เรายังทดสอบการใช้เส้นขอบสีนีออนเพื่อให้ภาพดูคล้ายกับ Tron
เมื่อเรนเดอร์ลูกโลกและมวลแผ่นดินแล้ว เราเริ่มทดลองใช้ภาพลูกโลกในลักษณะต่างๆ เนื่องจากเราต้องการภาพโมโนโครมที่ดูเรียบง่าย เราจึงใช้ลูกโลกและมวลแผ่นดินที่เป็นสีเทา นอกจากเส้นขอบนีออนที่กล่าวถึงข้างต้นแล้ว เรายังลองใช้ลูกโลกสีเข้มที่มีมวลแผ่นดินสีเข้มบนพื้นหลังสีอ่อน ซึ่งดูเท่ดีทีเดียว แต่คอนทราสต์ต่ำเกินไปจนอ่านได้ยากและไม่เข้ากับความรู้สึกของโปรเจ็กต์ เราจึงทิ้งแบบนั้นไป
อีกแนวคิดหนึ่งที่เรามีสำหรับรูปลักษณ์ของลูกโลกคือทำให้ดูเหมือนพอร์ซเลนเคลือบ เรายังไม่ได้ลองใช้ฟีเจอร์นี้เนื่องจากเขียน Shader ให้ดูเป็นพอร์ซเลนไม่ได้ (เครื่องมือแก้ไขวัสดุภาพน่าจะดี) สิ่งที่ใกล้เคียงที่สุดที่เราลองคือลูกโลกสีขาวที่ส่องสว่างซึ่งมีมวลแผ่นดินสีดํา ค่อนข้างเรียบร้อยแต่คอนทราสต์สูงเกินไป และดูไม่ค่อยดีเท่าไหร่ อีกหนึ่งรายการที่จะทิ้ง
ชิเดอร์ในลูกโลกสีดําและขาวใช้แสงสะท้อนจากด้านหลังที่ไม่เป็นความจริง ความสว่างของภาพโลกขึ้นอยู่กับระยะทางของพื้นผิวตั้งฉากกับระนาบหน้าจอ ดังนั้นพิกเซลตรงกลางลูกโลกที่ชี้ไปที่หน้าจอจึงมืด ส่วนพิกเซลที่ขอบลูกโลกจะสว่าง เมื่อใช้กับพื้นหลังสีอ่อน คุณจะได้ภาพลูกโลกสะท้อนพื้นหลังที่สว่างแบบกระจายแสง ซึ่งให้ลุคที่ดูคลาสสิก ลูกโลกสีดํายังใช้พื้นผิวลูกโลก WebGL เป็นแผนที่ความมันวาวด้วย เพื่อให้ชั้นชายฝั่ง (พื้นที่น้ำตื้น) ดูเหมือนจะสะท้อนแสงเมื่อเทียบกับส่วนอื่นๆ ของลูกโลก
ต่อไปนี้คือลักษณะของชิเดอร์มหาสมุทรสำหรับลูกโลกสีดํา เวิร์กเทกซ์เชดเดอร์พื้นฐานมากและฟร็กเมนต์เชดเดอร์ที่ "ดูดีจัง ปรับนิดปรับหน่อย"
'ocean' : {
uniforms: {
'texture': { type: 't', value: 0, texture: null }
},
vertexShader: [
'varying vec3 vNormal;',
'varying vec2 vUv;',
'void main() {',
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'vNormal = normalize( normalMatrix * normal );',
'vUv = uv;',
'}'
].join('\n'),
fragmentShader: [
'uniform sampler2D texture;',
'varying vec3 vNormal;',
'varying vec2 vUv;',
'void main() {',
'vec3 diffuse = texture2D( texture, vUv ).xyz;',
'float intensity = pow(1.05 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) ), 4.0);',
'float i = 0.8-pow(clamp(dot( vNormal, vec3( 0, 0, 1.0 )), 0.0, 1.0), 1.5);',
'vec3 atmosphere = vec3( 1.0, 1.0, 1.0 ) * intensity;',
'float d = clamp(pow(max(0.0,(diffuse.r-0.062)*10.0), 2.0)*5.0, 0.0, 1.0);',
'gl_FragColor = vec4( (d*vec3(i)) + ((1.0-d)*diffuse) + atmosphere, 1.0 );',
'}'
].join('\n')
}
สุดท้ายเราเลือกภาพโลกสีเข้มที่มีมวลแผ่นดินสีเทาอ่อนซึ่งได้รับแสงจากด้านบน การออกแบบนี้ตรงกับบรีฟการออกแบบมากที่สุด ทั้งยังดูดีและอ่านง่าย นอกจากนี้ การที่ลูกโลกมีคอนทราสต์ต่ำเล็กน้อยยังทำให้เครื่องหมายและเนื้อหาที่เหลือโดดเด่นมากขึ้นเมื่อเทียบกับลูกโลก เวอร์ชันด้านล่างใช้มหาสมุทรสีดําสนิท ขณะที่เวอร์ชันที่ใช้งานจริงมีมหาสมุทรสีเทาเข้มและเครื่องหมายที่แตกต่างออกไปเล็กน้อย
การสร้างเครื่องหมายด้วย CSS
เมื่อพูดถึงเครื่องหมาย เมื่อโลกและมวลแผ่นดินทำงานได้ ฉันก็เริ่มทำงานกับเครื่องหมายตำแหน่ง เราตัดสินใจใช้องค์ประกอบ HTML สไตล์ CSS สำหรับเครื่องหมาย เพื่อให้สร้างและจัดรูปแบบเครื่องหมายได้ง่ายขึ้น และอาจนําเครื่องหมายมาใช้ซ้ำในแผนที่ 2 มิติที่ทีมกําลังทําอยู่ ตอนนั้นฉันยังไม่ทราบวิธีง่ายๆ ในการทําให้เครื่องหมาย WebGL คลิกได้ และไม่ต้องการเขียนโค้ดเพิ่มเติมสําหรับการโหลด / สร้างโมเดลเครื่องหมาย เมื่อมองย้อนกลับไป เครื่องหมาย CSS ทำงานได้ดี แต่มีแนวโน้มที่จะพบปัญหาด้านประสิทธิภาพเป็นครั้งคราวเมื่อคอมโพสิเตอร์และเรนเดอร์เออร์ของเบราว์เซอร์อยู่ในช่วงที่เปลี่ยนแปลง จากมุมมองประสิทธิภาพ การใช้เครื่องหมายใน WebGL น่าจะเป็นตัวเลือกที่ดีกว่า แต่มาร์กเกอร์ CSS ก็ช่วยประหยัดเวลาในการพัฒนาได้มากเช่นกัน
เครื่องหมาย CSS ประกอบด้วย div 2 รายการที่มีตำแหน่งแบบสัมบูรณ์ด้วยพร็อพเพอร์ตี้การเปลี่ยนรูปแบบ CSS พื้นหลังของเครื่องหมายเป็นการไล่สี CSS และส่วนสามเหลี่ยมของเครื่องหมายคือ div ที่หมุน โดยใช้เงาเล็กน้อยเพื่อให้เครื่องหมายโดดเด่นจากพื้นหลัง ปัญหาที่ใหญ่ที่สุดเกี่ยวกับเครื่องหมายคือต้องทําให้เครื่องหมายมีประสิทธิภาพดีพอ ฟังดูน่าเศร้า แต่การวาด Div 2-3 โหลที่เคลื่อนไหวไปรอบๆ และเปลี่ยน z-index ในทุกเฟรมเป็นวิธีที่ดีในการทริกเกอร์ข้อผิดพลาดในการเรนเดอร์ของเบราว์เซอร์ทุกประเภท
วิธีที่เครื่องหมายซิงค์กับฉาก 3 มิตินั้นไม่ซับซ้อนเกินไป เครื่องหมายแต่ละรายการมี Object3D ที่เกี่ยวข้องในฉาก Three.js ซึ่งใช้ติดตามเครื่องหมาย หากต้องการรับพิกัดพื้นที่หน้าจอ เราจะใช้เมทริกซ์ Three.js สำหรับลูกโลกและเครื่องหมาย แล้วคูณกับเวกเตอร์ 0 จากข้อมูลนี้ เราจะได้ตำแหน่งฉากของเครื่องหมาย หากต้องการดูตำแหน่งบนหน้าจอของเครื่องหมาย เราจะฉายตำแหน่งของฉากผ่านกล้อง เวกเตอร์ที่ฉายผลที่ได้จะมีพิกัดพื้นที่หน้าจอสำหรับเครื่องหมาย ซึ่งพร้อมใช้งานใน CSS
var mat = new THREE.Matrix4();
var v = new THREE.Vector3();
for (var i=0; i<locations.length; i++) {
mat.copy(scene.matrix);
mat.multiplySelf(locations[i].point.matrix);
v.set(0,0,0);
mat.multiplyVector3(v);
projector.projectVector(v, camera);
var x = w * (v.x + 1) / 2; // Screen coords are between -1 .. 1, so we transform them to pixels.
var y = h - h * (v.y + 1) / 2; // The y coordinate is flipped in WebGL.
var z = v.z;
}
ท้ายที่สุดแล้ว วิธีที่เร็วที่สุดคือการใช้การเปลี่ยนรูปแบบ CSS เพื่อย้ายเครื่องหมาย ไม่ใช้การทำให้ทึบแสงจางลงเนื่องจากจะทริกเกอร์เส้นทางที่ช้าใน Firefox และเก็บเครื่องหมายทั้งหมดไว้ใน DOM โดยไม่นำออกเมื่ออยู่หลังลูกโลก เรายังทดสอบการใช้การเปลี่ยนรูปแบบ 3 มิติแทน Z-Index ด้วย แต่ด้วยเหตุผลบางอย่าง การเปลี่ยนรูปแบบดังกล่าวจึงทํางานไม่ถูกต้องในแอป (แต่ทํางานใน Test Case ที่ลดขนาดแล้ว ไปหาคำตอบกัน) และเราเหลือเวลาอีก 2-3 วันก่อนการเปิดตัว จึงต้องปล่อยส่วนนั้นไว้สำหรับการบำรุงรักษาหลังการเปิดตัว
เมื่อคลิกเครื่องหมาย รายการเครื่องหมายจะขยายเป็นรายชื่อสถานที่ที่คลิกได้ ทั้งหมดนี้เป็น DOM ของ HTML ปกติ จึงเขียนได้ง่ายมาก ลิงก์และการแสดงผลข้อความทั้งหมดจะทำงานโดยที่เราไม่ต้องดำเนินการใดๆ เพิ่มเติม
การบีบอัดขนาดไฟล์
เมื่อเดโมทํางานและเชื่อมต่อกับส่วนอื่นๆ ของเว็บไซต์สิ่งมหัศจรรย์ของโลกแล้ว ยังมีปัญหาใหญ่อีก 1 ข้อที่ต้องแก้ไข เมชรูปแบบ JSON สำหรับมวลแผ่นดินของโลกมีขนาดประมาณ 3 เมกะไบต์ ไม่เหมาะสำหรับหน้าแรกของเว็บไซต์แสดงผลงาน แต่ข้อดีคือเมื่อบีบอัดเมชด้วย gzip ขนาดลดลงเหลือ 350 KB แต่ 350 KB ยังถือว่าใหญ่อยู่ หลังจากส่งอีเมลไป 2 ฉบับ เราก็สามารถรับสมัคร Won Chun ซึ่งทำงานเกี่ยวกับการบีบอัดเมช Google Body ขนาดใหญ่มาช่วยเราบีบอัดเมช เขาบีบอัดเมชจากรายการสามเหลี่ยมแบบแบนขนาดใหญ่ที่ระบุเป็นพิกัด JSON ให้เป็นพิกัด 11 บิตที่บีบอัดแล้วซึ่งมีสามเหลี่ยมที่จัดทำดัชนี และทำให้ไฟล์มีขนาดลดลงเหลือ 95 KB เมื่อใช้การบีบอัดไฟล์ GZIP
การใช้เมชที่บีบอัดไม่เพียงช่วยประหยัดแบนด์วิดท์ แต่ยังช่วยให้แยกวิเคราะห์เมชได้เร็วขึ้นด้วย การเปลี่ยนตัวเลขสตริง 3 เมกะไบต์ให้เป็นตัวเลขเนทีฟนั้นต้องใช้ความพยายามมากกว่าการแยกวิเคราะห์ข้อมูลไบนารี 100 KB เป็นอย่างมาก การลดขนาดหน้าเว็บลง 250 KB นั้นยอดเยี่ยมมากและทำให้เวลาในการโหลดครั้งแรกต่ำกว่า 1 วินาทีในการเชื่อมต่อ 2 Mbps เร็วขึ้นและเล็กลง เจ๋งสุดๆ
ขณะเดียวกัน เราลองโหลด Shapefile ของ Natural Earth ต้นฉบับซึ่งเป็นที่มาของเมช GlobeTweeter เราโหลด Shapefile ได้ แต่การแสดงผลเป็นมวลแผ่นดินแบบราบต้องทำการแบ่งพื้นที่เป็นสามเหลี่ยม (โดยมีรูสำหรับทะเลสาบ) ฉันได้รูปทรงที่แบ่งเป็นสามเหลี่ยมโดยใช้ utils ของ THREE.js แต่ไม่มีรู และเมชที่ได้จะมีขอบที่ยาวมาก ซึ่งทำให้ต้องแยกเมชออกเป็นสามเหลี่ยมที่เล็กลง สรุปสั้นๆ คือเราไม่สามารถทำให้ใช้งานได้ทันเวลา แต่สิ่งที่ยอดเยี่ยมคือรูปแบบ Shapefile ที่บีบอัดเพิ่มเติมจะทำให้คุณมีโมเดลมวลแผ่นดินขนาด 8 KB ไม่เป็นไร ไว้คราวหน้า
งานในอนาคต
สิ่งหนึ่งที่อาจต้องใช้เวลาเพิ่มเติมคือทำให้ภาพเคลื่อนไหวของเครื่องหมายดูดีขึ้น ตอนนี้เมื่อดวงอาทิตย์ลับขอบฟ้า ผลที่ได้จะดูไม่ค่อยสมจริง นอกจากนี้ การมีภาพเคลื่อนไหวเจ๋งๆ สำหรับการเปิดเครื่องหมายก็น่าจะดี
ในแง่ประสิทธิภาพ สิ่งที่ขาดหายไป 2 อย่างคือการเพิ่มประสิทธิภาพอัลกอริทึมการแยกตาข่ายและทำให้เครื่องหมายทำงานได้เร็วขึ้น นอกเหนือจากนี้ ทุกอย่างก็เรียบร้อยดี เยี่ยมไปเลย
สรุป
บทความนี้จะอธิบายวิธีที่เราสร้างลูกโลก 3 มิติสำหรับโปรเจ็กต์สถานที่น่าทึ่งของโลกของ Google เราหวังว่าคุณจะชอบตัวอย่างและลองสร้างวิดเจ็ตลูกโลกที่กําหนดเอง