利用javascript和WebGL繪制地球 【翻譯】
原翻譯:利用javascript和WebGL繪制地球 【翻譯】
在我們所有已知的HTML5API中,WebGL可能是最有意思的一個,利用這個API我們能夠在瀏覽器中創造出炫酷3D場景的能力。本文將完整的向你展示一些炫酷是如何實現的。
需要特別指出的是,這篇教程我們將會構建一個地球行星模型,這個模型可以像一個興奮的人一樣環繞的旋轉,另外,它可能使我們可以獲得一些其他程序員的稱贊,好吧,就這么多了。
准備
這篇教程我們將會用到一個令人着迷的WebGL插件:three.js. 這個插件跟JQuery有點像,不過它是針對WebGL的,它將很多復雜的原生API訪問接口進行了抽象,從而讓我們可以更輕易的利用WebGL的特性。
在HTML中,我們的可以通過正常的script標簽引入這個插件,如下:
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r63/three.min.js"></script>
此處我們引用的是CDN版本,如果你有必要,可以使用本地的方法引入。然后我們需要確保WebGL有東西可以在上面進行渲染。這里我們有個靈活的做法:我們可以直接將一般的Div或者Canvas寫到HTML中,或者我們可以另外用JS創建並且追加canvas元素到Dom里后再去渲染。這里我們采用第一種容易點的方法,如下:
<body> <div id="container"></div> <script src="earth.js"></script> </body>
添加完script標簽連接到Dom中后,我們的HTML部分差不多就完成了。
下一步
Tree.js本身是趨向把東西做的非常接近真實的3D桌面程序的。我們擁有一個場景,有一些東西現場直播,然后通過攝像機去瀏覽,然后有些燈光,特效,渲染在整場景上面,當然他們自身也全都是3D對象。這個場景的元素列表似乎有點嚇人,在我們的earth.js文件里,所有的這些元素都可以當成形狀變量,Javascript文件如下:
var scene, camera, light, renderer, earthObject; var WIDTH = window.innerWidth - 30, HEIGHT = window.innerHeight - 30; var angle = 45, aspect = WIDTH / HEIGHT, near = 0.1, far = 3000;
有些額外的變量也定義在這里了,WIDTH,HEIGHT 變量用來獲取我們畫布的寬與高,下面的其他變量之后將會用來設置我們相機的位置。對於幾乎所有的3D對象來說,所有這些元素都是共通的,無論是平台還是環境,所以在這里我們習慣性的將這些家伙寫到一起。然而利用Three.js我們可以輕松的實現,我們將看看所有這些元素是如何在同一時刻融合到項目中的。
環境
首先,我們需要啟用新變量並初始化他們,從而使我們的地球模型可以展示的更炫。我們可以先設置每個處理環境因素的變量:
var container = document.getElementById('container'); camera = new THREE.PerspectiveCamera(angle, aspect, near, far); camera.position.set(0, 0, 0); scene = new THREE.Scene(); light = new THREE.SpotLight(0xFFFFFF, 1, 0, Math.PI / 2, 1); light.position.set(4000, 4000, 1500); light.target.position.set (1000, 3800, 1000);
下面是針對上面代碼執行情況的描述:
- 在我們的HTMl中抓取container對象
- 我們用之前聲明的變量設置camera對象(更多關於cameras是如何在3D中工作的信息,可以點擊這里)
- 通過
position.set
方法設置camera的位置, 這個方法需要攜帶一個維度(x, y, z)參數對象, 可能你已經想到了, 我們將會使用這個camera去定位我們的3D對象,本教程中的3D對象就是我們的地球模型。 - 接下來設置我們的light對象。如果沒有light對象做渲染的話,那么整個模型出來的效果將會是一片漆黑,所以我們必須細心的注意這一步驟。Three.js的SpotLight object對象擁有與我們的camera對象大致相同的參數,只不過這個對象的第一個參數colour必須為十六進制的值,然后剩余的其他參數與camera基本相同。
- 最后,我們需要設置我們的畫布對象renderer.另外一個需要確保的點則是:我們需要提前將畫布對象完整的渲染到了屏幕上,再次強調,如果沒有完成這一步,那么整個畫布將會什么也看不見,一片漆黑。我們給畫布添加了去鋸齒效果,並且將這個效果作為Dom元素添加到我們的原始容器中。
現在我們需要通過將整個地球粘貼在網上一樣的方式來構建自身整個模型。代碼如下:
var earthGeo = new THREE.SphereGeometry (30, 40, 400), earthMat = new THREE.MeshPhongMaterial(); var earthMesh = new THREE.Mesh(earthGeo, earthMat); earthMesh.position.set(-100, 0, 0); earthMesh.rotation.y=5; scene.add(earthMesh);
在這里我們創建了一個網狀(Mesh)對象,這個網狀是一種可以被用來裝扮並看起來像地球形狀的對象,然后給這個對象添加一些幾何結構,外觀包裝,或者一些有質感的材料來包裹這個網狀體。我們同樣會將這個對象設置適當的在位置,與其他參數對象一樣,我們會並且將Mesh對象添加到我們的場景(scene)中。
如下有個樣例。這里面有些額外的渲染效果,稍后我們將會講解。這個樣例看起來離我們想要的越來越近了。
藍色星球
接下來有趣的部分是給這個家伙制作皮膚。首先我們將會使用一張漫反射貼圖,它會讓這個家伙看起來更像個地圖。你可以像下面的方式一樣添加:
// diffuse map earthMat.map = THREE.ImageUtils.loadTexture('images/earthmap1k.jpg');
如果你想要質感更好些的話,你可以嘗試用這個圖片,或者你可以去google搜索一張你想要的圖片都行。高分辨率的圖都可以。
現在這個模型看起來沒那么糟糕了,但是我們仍然可以通過引用一點地形描繪的方式,使整個模型看起來更真實些。這個地球有一些高山,為了確保區分太陽系的其他星球,我們需要使用凹凸地圖(bump map), 在3D模型中, 凹凸地圖是黑白圖,使用鮮明的白色去凸顯圖像凹凸不平的部分(例如我們示例中的:山脈)。
// bump map earthMat.bumpMap = THREE.ImageUtils.loadTexture('images/elev_bump_16ka.jpg'); earthMat.bumpScale = 8;
使用上面的圖片我們差不多達到了效果,再次強調,使用過Google搜索“Earth bump map”會獲得大量的選擇,但是如果你感覺都不好的話,你可以點擊這個連接。 運行了以上的代碼,我們將會看到如下效果:
讓它轉起來!
接下來剩余的事情就是我們給這個地球模型添加一些動畫效果,為此,我們需要兩個新的方法,我們命名為render()
和animate()
function animate() { requestAnimationFrame(animate); render(); }
我們的animate()
方法並不是很復雜,通過自身遞歸連續的調用requestAnimationFrame()
方法,anmiate()
會請求我們的render()
方法,讓我們看看render()
方法的代碼:
function render() { var clock = new THREE.Clock(), delta = clock.getDelta(); earthMesh.rotation.y += rotationSpeed * delta; renderer.render(scene, camera); }
我們看看上面的代碼做了些什么工作。每次render()
方法被請求,它便會讓地球模型在y軸上緩緩的轉動起來(此處你可以選擇設置任意的轉動次數,我們在這里利用getDelta()
方法構建一個時鍾對象來控制轉動次數,當然你可以不使用這種方法)。然后render()
方法會執行清理畫布操作,這是防止畫布亂掉很重要的步驟,最后它會渲染我們的場景(以及場景對象中的其他所有對象)和我們的camera對象。
最后
當然,擁有拖拽操作會讓我們的地球模型的體驗更好,OrbitControls.js是一個可以為我們地球模型提供鼠標驅動旋轉效果能力的腳本,並且它為我們的平流層里添加一些星星或者雲作為地球模型的背景同樣也並不困難,如果你並不嫌麻煩的話,你甚至可以利用WebGL的着色器(shaders)為你的星球添加一個平流層。
運行代碼,你可以看到一個樣例,在CodePen中最終的Demo如下:
通過按住鼠標拖動和滾動鼠標滑輪來查看效果(或者點擊此處demo
結尾
WebGL和Three.js變得越發的具有挑戰性,因為他們偶爾會要求我們要像3D藝術家一樣,利用場景,畫布,camera去完成我們的工作,最終的結果就是做出了一些加令人印象深刻的東西。如果你專注於在這個技術上的話,你可以通過在瀏覽器中使用3D特性創造出一些有趣的可能性。如果堅持它,相信不久你就很可能獲取一些非凡的成績。