Pengantar globe 3D Keajaiban Dunia
Jika telah melihat situs Keajaiban Dunia Google yang baru diluncurkan di browser yang kompatibel dengan WebGL, Anda mungkin melihat globe berputar yang menarik di bagian bawah layar. Artikel ini menjelaskan cara kerja globe dan apa yang kami gunakan untuk membuatnya.
Untuk memberikan ringkasan singkat, globe Keajaiban Dunia adalah versi Globe WebGL yang telah banyak diubah oleh Tim Google Data Arts. Kami mengambil globe asli, menghapus bagian diagram batang, mengubah shader, menambahkan penanda HTML yang dapat diklik dan geometri benua Natural Earth dari demo GlobeTweeter Mozilla (terima kasih banyak kepada Cedric Pinson!) Semuanya untuk membuat globe animasi yang bagus yang cocok dengan skema warna situs dan menambahkan lapisan kecanggihan ekstra ke situs.
Ringkasan desain untuk globe adalah memiliki peta animasi yang terlihat bagus dengan penanda yang dapat diklik yang ditempatkan di atas Situs Warisan Dunia. Dengan mengingat hal itu, saya mulai mencari sesuatu yang sesuai. Hal pertama yang terlintas dalam pikiran adalah Globe WebGL yang dibuat oleh Tim Google Data Arts. Ini adalah globe dan terlihat keren. Apa lagi yang Anda perlukan?
Menyiapkan Globe WebGL
Langkah pertama dalam membuat widget globe adalah mendownload Globe WebGL dan menyiapkan serta menjalankannya. Globe WebGL tersedia secara online di Google Code, dan mudah didownload serta dijalankan. Download dan ekstrak file zip, cd ke dalamnya, lalu jalankan server web dasar: python -m SimpleHTTPServer
. (Perhatikan bahwa UTF-8 tidak diaktifkan secara default; Anda dapat menggunakannya.) Sekarang, jika membuka http://localhost:8000/globe/globe.html
, Anda akan melihat Globe WebGL.
Setelah Globe WebGL siap dan berjalan, saatnya untuk menghapus semua bagian yang tidak diperlukan. Saya mengedit HTML untuk menghapus bagian UI dan menghapus perlengkapan penyiapan diagram batang globe dari fungsi inisialisasi globe. Di akhir proses tersebut, saya memiliki Globe WebGL yang sangat sederhana di layar. Anda dapat memutarnya dan terlihat keren, tetapi hanya itu saja.
Untuk menghapus hal-hal yang tidak diperlukan, saya menghapus semua elemen UI dari index.html globe dan mengedit skrip inisialisasi agar terlihat seperti ini:
if(!Detector.webgl){
Detector.addGetWebGLMessage();
} else {
var container = document.getElementById('container');
var globe = new DAT.Globe(container);
globe.animate();
}
Menambahkan geometri benua
Kami ingin kamera berada dekat dengan permukaan globe, tetapi saat kami menguji globe yang diperbesar, kurangnya resolusi tekstur menjadi terlihat jelas. Jika diperbesar, tekstur Globe WebGL akan menjadi bergerigi dan buram. Kita dapat menggunakan gambar yang lebih besar, tetapi hal itu akan memperlambat proses download dan pengoperasian globe, jadi kita memilih untuk menggunakan representasi vektor daratan dan perbatasan.
Untuk geometri daratan, saya beralih ke demo GlobeTweeter open source dan memuat model 3D di dalamnya ke Three.js. Setelah model dimuat dan dirender, saatnya mulai meningkatkan tampilan globe. Masalah pertama adalah model daratan globe tidak cukup bulat untuk sejajar dengan Globe WebGL, jadi saya akhirnya menulis algoritma pemisahan mesh cepat yang membuat model daratan lebih bulat.
Dengan model daratan bulat, saya dapat menempatkannya hanya sedikit offset dari permukaan globe, sehingga membuat benua mengambang yang digarisbatasi dengan garis hitam 2 piksel di bawahnya untuk semacam bayangan. Saya juga bereksperimen dengan garis batas berwarna neon untuk membuat tampilan seperti Tron.
Dengan globe dan rendering daratan, saya mulai bereksperimen dengan berbagai tampilan untuk globe. Karena kami ingin menggunakan tampilan monokrom yang sederhana, saya tetap menggunakan globe dan daratan dalam skala abu-abu. Selain garis neon yang disebutkan di atas, saya mencoba globe gelap dengan daratan gelap di latar belakang terang, yang sebenarnya terlihat cukup keren. Namun, kontrasnya terlalu rendah sehingga tidak mudah dibaca dan tidak sesuai dengan nuansa project, jadi saya menghapusnya.
Pikiran awal lainnya yang saya miliki untuk tampilan globe adalah membuatnya terlihat seperti porselen glasir. Saya tidak berhasil mencobanya karena tidak berhasil menulis shader untuk membuat tampilan porselen (editor materi visual akan lebih baik). Hal terdekat yang saya coba adalah globe putih yang bercahaya dengan daratan hitam. Desainnya cukup rapi, tetapi kontrasnya terlalu tinggi. Selain itu, tampilannya tidak terlalu bagus. Jadi, satu lagi untuk tempat sampah.
Shader di globe hitam dan putih menggunakan semacam pencahayaan backlit difus palsu. Kecerahan globe bergantung pada jarak normal permukaan ke bidang layar. Jadi, piksel di tengah globe yang mengarah ke layar berwarna gelap dan piksel di tepi globe berwarna terang. Dikombinasikan dengan latar belakang terang, Anda akan mendapatkan tampilan globe yang memantulkan latar belakang cerah yang menyebar, sehingga menciptakan tampilan showroom yang berkelas. Bola dunia hitam juga menggunakan tekstur Bola Dunia WebGL sebagai peta gloss, sehingga paparan benua (area perairan dangkal) terlihat mengkilap dibandingkan dengan bagian lain bola dunia.
Berikut adalah tampilan shader samudra untuk globe hitam. Vertex shader yang sangat mendasar dan shader fragmen "oh, itu terlihat rapi tweak tweak" yang tidak rapi.
'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')
}
Pada akhirnya, kami memilih globe gelap dengan daratan berwarna abu-abu terang yang diterangi dari atas. Desain ini paling mendekati brief desain dan terlihat bagus serta mudah dibaca. Selain itu, globe yang memiliki kontras rendah akan membuat penanda dan konten lainnya lebih terlihat. Versi di bawah menggunakan samudra yang benar-benar hitam, sedangkan versi produksi memiliki samudra abu-abu gelap dan penanda yang sedikit berbeda.
Membuat penanda dengan CSS
Berbicara tentang penanda, dengan globe dan daratan yang berfungsi, saya mulai mengerjakan penanda tempat. Saya memutuskan untuk menggunakan elemen HTML bergaya CSS untuk penanda, agar lebih mudah membuat dan menata gaya penanda, serta berpotensi menggunakan kembali penanda di peta 2D yang sedang dikerjakan tim. Pada saat itu, saya juga tidak tahu cara mudah untuk membuat penanda WebGL dapat diklik dan tidak ingin menulis kode tambahan untuk memuat / membuat model penanda. Setelah dipikirkan kembali, penanda CSS berfungsi dengan baik, tetapi cenderung mengalami masalah performa sesekali saat komposer dan perender browser berada dalam periode fluktuasi. Dari sudut pandang performa, melakukan penanda di WebGL akan menjadi opsi yang lebih baik. Namun, penanda CSS menghemat banyak waktu pengembangan.
Penanda CSS terdiri dari beberapa div yang diposisikan secara absolut dengan properti transformasi CSS. Latar belakang penanda adalah gradien CSS dan bagian segitiga penanda adalah div yang diputar. Penanda memiliki bayangan jatuh kecil untuk memunculkannya dari latar belakang. Masalah terbesar dengan penanda adalah membuatnya berperforma cukup baik. Meskipun terdengar menyedihkan, menggambar beberapa lusin div yang bergerak dan mengubah z-index-nya di setiap frame adalah cara yang cukup baik untuk memicu berbagai masalah rendering browser.
Cara penanda disinkronkan dengan tampilan 3D tidak terlalu rumit. Setiap penanda memiliki Object3D yang sesuai di tampilan Three.js, yang digunakan untuk melacak penanda. Untuk mendapatkan koordinat ruang layar, saya mengambil matriks Three.js untuk globe dan penanda, lalu mengalikan vektor nol dengan matriks tersebut. Dari situ, saya mendapatkan posisi penanda di tampilan. Untuk mendapatkan posisi penanda layar, saya memproyeksikan posisi tampilan melalui kamera. Vektor yang diproyeksikan memiliki koordinat ruang layar untuk penanda, yang siap digunakan di 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;
}
Pada akhirnya, pendekatan tercepat adalah menggunakan transformasi CSS untuk memindahkan penanda, bukan menggunakan peredupan opasitas karena memicu jalur lambat di Firefox dan mempertahankan semua penanda di DOM, bukan menghapusnya saat berada di belakang globe. Kami juga bereksperimen dengan menggunakan transformasi 3D, bukan z-index, tetapi karena alasan tertentu, transformasi 3D tidak berfungsi dengan baik di aplikasi (tetapi berfungsi dalam kasus pengujian yang dikurangi, coba pikirkan), dan kami hanya memiliki waktu beberapa hari sebelum peluncuran, jadi kami harus meninggalkan bagian tersebut untuk pemeliharaan pasca-peluncuran.
Saat Anda mengklik penanda, penanda akan diperluas menjadi daftar nama tempat yang dapat diklik. Ini adalah semua hal DOM HTML normal, jadi sangat mudah ditulis. Semua rendering link dan teks berfungsi tanpa perlu pekerjaan tambahan dari pihak kami.
Memperkecil ukuran file
Setelah demo berfungsi dan terhubung ke seluruh situs World Wonders, masih ada satu masalah besar yang harus diselesaikan. Ukuran mesh berformat JSON untuk daratan bumi adalah sekitar 3 megabita. Tidak cocok untuk halaman depan situs showcase. Untungnya, mengompresi mesh dengan gzip mengurangi ukurannya menjadi 350 kB. Namun, 350 kB masih agak besar. Setelah beberapa email, kami berhasil merekrut Won Chun -- yang bekerja untuk mengompresi mesh Google Body yang besar -- untuk membantu kami mengompresi mesh. Ia mempersempit mesh dari daftar segitiga datar besar yang diberikan sebagai koordinat JSON menjadi koordinat 11-bit yang dikompresi dengan segitiga yang diindeks dan mendapatkan ukuran file hingga 95 kB yang di-gzip.
Menggunakan mesh yang dikompresi tidak hanya menghemat bandwidth, tetapi mesh juga lebih cepat diuraikan. Mengubah 3 megigabyte angka string menjadi angka native memerlukan lebih banyak pekerjaan daripada mengurai seratus kilobyte data biner. Selain itu, pengurangan ukuran halaman sebesar 250 kB yang dihasilkan sangat praktis dan mendapatkan waktu pemuatan awal di bawah satu detik pada koneksi 2 Mbps. Lebih cepat dan lebih kecil, luar biasa!
Pada saat yang sama, saya mencoba memuat Shapefile Natural Earth asli yang menjadi asal mesh GlobeTweeter. Saya berhasil memuat Shapefile, tetapi merendernya sebagai daratan datar memerlukan triangulasi (dengan lubang untuk danau, tentu saja). Saya mendapatkan bentuk yang ditriangulasikan menggunakan utils THREE.js, tetapi tidak untuk lubang. Dan mesh yang dihasilkan memiliki tepi yang sangat panjang, yang mengharuskan pemisahan mesh menjadi tris yang lebih kecil. Singkatnya, saya tidak berhasil membuatnya berfungsi tepat waktu, tetapi yang menarik adalah format Shapefile yang dikompresi lebih lanjut akan memberi Anda model daratan 8 kB. Ah, mungkin lain kali.
Pekerjaan mendatang
Satu hal yang dapat menggunakan sedikit pekerjaan tambahan adalah membuat animasi penanda lebih bagus. Sekarang, saat mereka melewati cakrawala, efeknya sedikit norak. Selain itu, animasi keren untuk pembukaan penanda akan sangat bagus.
Dari segi performa, dua hal yang kurang adalah mengoptimalkan algoritma pemisahan mesh dan membuat penanda lebih cepat. Selain itu, semuanya baik-baik saja. Hore!
Ringkasan
Dalam artikel ini, saya menjelaskan cara kami membuat globe 3D untuk project Google World Wonders. Semoga Anda menikmati contoh-contoh ini dan akan mencoba membuat widget globe kustom Anda sendiri.