การสร้างลูกโลก 3 มิติในโลกสิ่งมหัศจรรย์

แนะนำโลก 3 มิติของ World Wonders

หากคุณดูเว็บไซต์ Google World Wonders ที่เพิ่งเปิดตัวไปเมื่อเร็วๆ นี้บนเบราว์เซอร์ที่รองรับ WebGL คุณอาจเห็นรูปลูกโลกหมุนอยู่ที่ด้านล่างของหน้าจอ บทความนี้จะอธิบายวิธีการทำงานของโลกและสิ่งที่เราใช้สร้างโลก

เพื่อให้คุณเห็นภาพรวมคร่าวๆ โลกสิ่งมหัศจรรย์คือโลกที่เราปรับแต่งเล็กน้อยของ WebGL Globe โดยทีม Google Data Arts เรานำลูกโลกดั้งเดิมมาถอดบิตกราฟแท่งออก เปลี่ยนตัวปรับเฉดสี เพิ่มเครื่องหมาย HTML ที่คลิกได้สวยๆ และรูปเรขาคณิตแบบทวีปธรรมชาติจากการสาธิต GlobeTweeter ของ Mozilla (ขอบคุณมากสำหรับ Cedric Pinson!) ทั้งหมดเพื่อสร้างลูกโลกแบบเคลื่อนไหวที่สวยงามซึ่งเข้ากับรูปแบบสีของเว็บไซต์และเพิ่มระดับความทันสมัยให้กับเว็บไซต์

บรีฟงานการออกแบบสำหรับลูกโลกคือแผนที่แบบเคลื่อนไหวที่สวยงาม พร้อมด้วยเครื่องหมายที่สามารถคลิกได้วางที่ด้านบนของแหล่งมรดกโลก ด้วยเหตุผลนี้ ฉันจึงเริ่มมองหาสิ่งที่เหมาะสม สิ่งแรกที่คุณนึกถึงคือ WebGL Globe ที่ทีม Google Data Arts สร้างขึ้น เป็นลูกโลกที่ดูเท่ คุณต้องการอะไรอีกไหม

การตั้งค่า WebGL Globe

ขั้นตอนแรกในการสร้างวิดเจ็ตลูกโลกคือการดาวน์โหลด WebGL Globe แล้วเริ่มต้นใช้งาน WebGL Globe ใช้งานออนไลน์ที่ Google Code ซึ่งการดาวน์โหลดและเรียกใช้ก็ทำได้ง่าย ดาวน์โหลดและแตกไฟล์ zip จากนั้นใส่ cd และเรียกใช้เว็บเซิร์ฟเวอร์พื้นฐาน: python -m SimpleHTTPServer (โปรดทราบว่าตัวเลือกนี้ไม่ได้เปิด UTF-8 โดยค่าเริ่มต้น คุณสามารถใช้นี้) ตอนนี้เมื่อไปที่ http://localhost:8000/globe/globe.html คุณควรจะเห็น WebGL Globe

เมื่อ WebGL Globe เริ่มทำงานแล้ว ก็ถึงเวลาตัดส่วนที่ไม่จำเป็นออกทั้งหมด ฉันแก้ไข HTML เพื่อย่อส่วน UI และนำการตั้งค่ากราฟแท่งโลกออกจากฟังก์ชันการเริ่มต้นลูกโลก ในตอนท้ายของกระบวนการดังกล่าว ผมมี WebGL Globe เปล่าๆ บนหน้าจอ คุณจะหมุนไปรอบๆ ก็ได้ แค่นี้ก็ดูดีแล้ว

เพื่อตัดส่วนที่ไม่จำเป็น ผมได้ลบองค์ประกอบ UI ทั้งหมดออกจาก index.html ของโลกและแก้ไขสคริปต์เริ่มต้นให้มีลักษณะดังต่อไปนี้

if(!Detector.webgl){
  Detector.addGetWebGLMessage();
} else {
  var container = document.getElementById('container');
  var globe = new DAT.Globe(container);
  globe.animate();
}

การเพิ่มเรขาคณิตของทวีป

เราอยากให้กล้องอยู่ใกล้กับพื้นผิวโลก แต่เมื่อทดสอบโลกที่ซูมเข้าโดยไม่พบความละเอียดของพื้นผิวก็เห็นได้ชัดเจน เมื่อซูมเข้า พื้นผิวของ WebGL Globe จะกลายเป็นบล็อกและเบลอ เราอาจใช้ภาพที่ใหญ่ขึ้น แต่นั่นจะทำให้โลกในการดาวน์โหลดและเรียกใช้ภาพช้าลง เราจึงเลือกใช้ภาพแทนเวกเตอร์ของทวีปและพรมแดน

สำหรับรูปทรงเรขาคณิตบนพื้นดิน ผมใช้การสาธิต GlobeTweeter แบบโอเพนซอร์ส และโหลดโมเดล 3 มิติลงใน Three.js เมื่อโมเดลโหลดและแสดงผลแล้ว ก็ถึงเวลาเริ่มปรับแต่งรูปลักษณ์ของโลก ปัญหาแรกคือโมเดลพื้นโลกมีรูปทรงกลมไม่พอที่จะเป็นระนาบเดียวกันกับ WebGL Globe ผมจึงเขียนอัลกอริทึมการแยกตาข่ายแบบรวดเร็วที่ทำให้โมเดลทวีปมีรูปทรงกลมมากขึ้น

ด้วยแบบจำลองทวีปทรงกลม ผมสามารถวางตำแหน่งดาวนี้ให้ห่างจากผิวโลกเพียงเล็กน้อย และสร้างทวีปลอยที่มีเส้นสีดำขนาด 2px อยู่ใต้เงาเพื่อเรียงต่อกัน และฉันได้ทดลองสร้างเส้นขอบสีนีออนให้เป็นสไตล์คล้ายๆ กับ Tron

ผมเริ่มทดลองมุมมองต่างๆ สำหรับโลกใบนี้พร้อมกับการแสดงภาพโลกและทวีป เนื่องจากเราอยากได้ภาพโมโนโครมที่เรียบง่าย ฉันเลยต้องติดอยู่กับโลกและทวีปสีเทา นอกจากภาพคร่าวๆ ที่เป็นขอบแสงนีออนแล้ว ผมได้ลองใช้โลกอันมืดมิดที่มีทวีปสีเข้มบนพื้นหลังสีอ่อน ซึ่งดูดีมากๆ เลย แต่คอนทราสต์ต่ำจนอ่านไม่ออก ไม่เหมาะกับลักษณะของโปรเจ็กต์ ฉันเลยตัดเนื้อหาแบบนั้นทิ้งไป

อีกความคิดหนึ่งที่คิดไว้ตั้งแต่แรกเริ่มก็คือการทำโลกให้ดูเหมือนเครื่องลายครามเคลือบ พอดีว่าลองแล้วไม่ได้เพราะไม่ได้เขียนตัวปรับเฉดสีเพื่อทำภาพสีพอร์ซเลน (ถ้าตัดต่อเป็นตัวแก้ไขวัสดุภาพก็น่าจะดีนะ) สิ่งที่ใกล้ที่สุดที่ฉันลองคือลูกโลกสีขาวเรืองแสงที่มีทวีปสีดำ ค่อนข้างเป็นระเบียบ แต่มีคอนทราสต์สูง แล้วก็ไม่ได้ดูดีสุดๆ เลย ขออีกเรื่องสำหรับขำๆ นะ

ตัวให้เฉดสีในลูกโลกสีขาวและสีดำกำลังใช้แสงปลอมแบบกระจายแสง ค่าความสว่างของโลกขึ้นอยู่กับระยะห่างของพื้นผิวที่อยู่ในระดับปกติของระนาบหน้าจอ ดังนั้น พิกเซลตรงกลางโลกที่ชี้ไปยังหน้าจอจึงมืด และพิกเซลที่ขอบลูกโลกจะมีสีสว่าง เมื่อใช้ร่วมกับพื้นหลังสีอ่อน คุณจะเห็นภาพที่โลกกำลังสะท้อนพื้นหลังสว่างแบบกระจาย ทำให้ห้องดูหรูหรามีระดับ ลูกโลกสีดำยังใช้พื้นผิวโลก 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-3 รายการที่กำหนดตำแหน่งแบบสัมบูรณ์ด้วยคุณสมบัติ Transform ของ CSS พื้นหลังของเครื่องหมายเป็นการไล่ระดับสี CSS และส่วนสามเหลี่ยมของเครื่องหมายคือ div ที่หมุน ตัวทำเครื่องหมายจะมีเงาตกกระทบเล็กๆ ที่เด่นจากพื้นหลัง ปัญหาใหญ่ที่สุดเกี่ยวกับเครื่องหมายก็คือช่วยให้เครื่องหมายเหล่านี้ทำงานได้ดีพอ เศร้าเพราะอาจฟังดู การวาด div ไม่กี่สิบตัวที่เคลื่อนที่ไปมาและเปลี่ยนดัชนี Z ในทุกเฟรมเป็นวิธีที่ดีในการทริกเกอร์ปัญหาการแสดงผลของเบราว์เซอร์ทุกประเภท

วิธีซิงค์เครื่องหมายกับฉาก 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 Transforms เพื่อเคลื่อนย้ายเครื่องหมาย ไม่ใช่ใช้การเฟดความทึบแสงเนื่องจากจะเป็นการทริกเกอร์เส้นทางที่ช้าใน Firefox และเก็บตัวทำเครื่องหมายทั้งหมดใน DOM ไว้ ไม่ใช่ลบเครื่องหมายออกเมื่อหล่นไปตามโลก นอกจากนี้เรายังได้ทดลองใช้การแปลงแบบ 3 มิติแทนดัชนี Z แต่ด้วยเหตุผลบางอย่าง ระบบจึงใช้งานไม่ได้ในแอป (แต่ใช้ในกรอบการทดสอบที่ลดลงได้ ก็หยุดอยู่ได้) และเราใช้เวลาอีก 2-3 วันนับตั้งแต่การเปิดตัว ณ จุดนั้นจึงต้องทิ้งไว้ในส่วนนั้นเพื่อบำรุงรักษาหลังเปิดตัว

เมื่อคุณคลิกที่เครื่องหมาย เครื่องหมายจะขยายออกเป็นรายการชื่อสถานที่ที่คลิกได้ นี่เป็นเนื้อหา HTML DOM ปกติทั้งหมด ดังนั้นจึงเขียนได้ง่ายมาก ลิงก์และการแสดงผลข้อความทั้งหมดจะทำงานได้โดยไม่ต้องดำเนินการเพิ่มเติมในส่วนของเรา

การบีบขนาดไฟล์

ในระหว่างที่การสาธิตทำงานและเชื่อมต่อกับส่วนที่เหลือของเว็บไซต์ World Wonders แล้วยังคงมีปัญหาใหญ่ที่ต้องแก้ไข ตาข่ายรูปแบบ JSON สำหรับทวีปของโลกมีขนาดประมาณ 3 เมกะไบต์ ไม่เหมาะกับหน้าแรกของเว็บไซต์ Showcase ข้อดีคือการบีบอัด Mesh ด้วย gzip ทำให้ไฟล์มีขนาดลดลงถึง 350 kB แต่ 350 kB ก็ยังค่อนข้างใหญ่นะ หลังจากนั้น 2-3 ฉบับ เราได้เชิญ Won Chun ผู้ทำหน้าที่บีบอัดตาข่ายของ Google บอดี้ขนาดใหญ่มาให้ เพื่อให้เราช่วยบีบอัดตาข่ายได้ เขาบีบ Mesh ออกจากรายการรูปสามเหลี่ยมแบนๆ ที่กำหนดเป็นพิกัด JSON เพื่อบีบอัดพิกัด 11 บิตกับสามเหลี่ยมที่จัดทำดัชนี และเพิ่มขนาดไฟล์เล็กลงเหลือ 95 kB ในรูปแบบ gzip

การใช้ Mesh ที่บีบอัดไม่เพียงช่วยประหยัดแบนด์วิดท์ แต่ Mesh ยังแยกวิเคราะห์ได้เร็วขึ้นด้วย การเปลี่ยนตัวเลข 3 เมกะไบต์ให้เป็นเลขพื้นฐานนั้นต้องใช้ความคุ้มค่ามากกว่าการแยกวิเคราะห์ข้อมูลไบนารี 100 KB และผลที่ได้คือขนาดหน้าเว็บที่ลดลง 250 kB ได้นั้นนับว่าเก่งมากและมีเวลาในการโหลดครั้งแรกต่ำกว่า 1 วินาทีผ่านการเชื่อมต่อ 2 Mbps สูตรเร็วขึ้น เล็กลง เจ๋งขึ้น!

ในเวลาเดียวกัน ผมยังพยายามโหลด Natural Earth Shapefile ต้นฉบับที่ได้มาจากตาข่ายของ GlobeTweeter ฉันจัดการโหลด Shapefile ได้ แต่การแสดงผลเป็นทวีปแบนต้องอาศัยการสามเหลี่ยม (มีรูสำหรับทะเลสาบ สแนท) ฉันได้รูปทรงเป็นสามเหลี่ยมโดยใช้ THREE.js utils แต่ไม่ใช่หลุม และตาข่ายที่ได้มีขอบที่ยาวมาก ซึ่งจำเป็นต้องแยกตาข่ายออกเป็น 3 ส่วนเล็กๆ สรุปสั้นๆ ก็คือ ผมไม่สามารถทำให้มันทำงานได้ทันเวลา แต่สิ่งที่ดีก็คือรูปแบบ Shapefile ที่บีบอัดเพิ่มเติมจะทำให้คุณได้รับโมเดลแลนด์มาร์กขนาด 8 kB เอ่อ ไว้คราวหน้านะ

งานในอนาคต

สิ่งหนึ่งที่อาจต้องใช้ความพยายามมากขึ้นคือการทำให้ภาพเคลื่อนไหวของตัวทำเครื่องหมายดีขึ้น ตอนนี้เมื่อมองข้ามขอบฟ้า ผลที่ได้ก็จะดูตะลึงเล็กน้อย นอกจากนี้ การมีภาพเคลื่อนไหวเจ๋งๆ ไว้เปิดเครื่องหมายก็เป็นสิ่งที่ดี

ประสิทธิภาพ 2 อย่างที่ขาดไปคือการเพิ่มประสิทธิภาพอัลกอริทึมการแยก Mesh และทำให้เครื่องหมายแสดงได้เร็วขึ้น นอกจากนั้นแล้ว สิ่งที่น่าสนใจ ฮึ่มมม!

สรุป

ในบทความนี้ เราได้อธิบายถึงวิธีที่เราสร้างโลก 3 มิติสำหรับโครงการ Google World Wonders เราหวังว่าคุณจะชอบตัวอย่างเหล่านี้และจะพยายามสร้างวิดเจ็ตโลกที่กำหนดเองของคุณ

รายการอ้างอิง