Three.js開發基礎和3D全景(三)
具體效果&文章所涉及的程式碼可以在codePen獲得。
前言
透過該系列的第一篇文章和第二篇文章,相比大家肯定對WebGL和three。js有基本的瞭解了,但是文章題目中不是還有個3D全景麼,所謂“光說不練假把式”,這一篇文章就跟大家介紹一下3D全景圖是怎麼實現的。
3D全景圖是個什麼東東
大家可以先感受一下效果:
上海外灘全景;
KrPano主頁,可以找到各種全景示例;
全景圖,又稱Panorama,根據Wiki Pedia的定義,是一種寬角度的物理場景檢視或展現形式,包括繪畫、攝影、電影、震動映像或3D模式等,原文如下:
A panorama, is any wide-angle view or representation of a physical space, whether in painting, drawing, photography, film, seismic images or a three-dimensional model。
Wiki Pedia
Web 3D全景圖即是在Web頁面內,以3D的展現形式,將透過某種介質記錄(圖片&影片)的寬角度(一般是360°)現實場景進行展示和播放。簡單說,Web3D全景的核心就是做一個全景圖播放器。
在不遠的過去,webGL還沒有被提出和支援的時候,要想實現3D全景還是要依賴Flash的。隨著瀏覽器,尤其是H5,對webGL的支援,使用JS實現3D全景圖具備了可行性。
如何實現一個簡單的全景圖
首先,要引入three。js和一張如下圖所示的全景圖片:
然後,初始化three。js世界,具體原理請參見系列文章第一篇:
// init 3D world (虛擬碼)
var world = {};
function initWord(fov, width, height) {
initScene(); // 場景
initRenderer(); // 渲染器
initCamera(); // 攝像機
}
然後,向3D世界內新增全景景象:
一般情況下全景圖的實現有兩種策略:
將全景圖片切圖為6張子圖貼到六面體的六個面上
將全景圖片作為紋理整體貼合到一個球體上
從下面的兩張圖片中可以看出兩者的區別
使用球體的具體實現如下:
// 新增全景圖
function addPano(url) {
THREE。ImageUtils。crossOrigin = ‘’;
// 使用全景圖片生成紋理
THREE。ImageUtils。loadTexture(url, undefined, function(texture) {
// 設定屬性
texture。maxFilter = THREE。NearestFilter;
texture。minFilter = THREE。NearestFilter;
texture。format = THREE。RGBFormat;
// 從紋理建立材質
var material = new THREE。MeshLambertMaterial({map: texture});
// 建立球體,並加入到場景中
var mesh = new THREE。Mesh( new THREE。SphereGeometry( 955, 50, 50 ), material );
mesh。scale。x = -1;
world。scene。add( mesh );
})
}
到此為止,一張全景圖已經被繪製到了web頁面上,但是你會發現你只能看到全景圖的一部分,關於如何讓全景圖動起來的基本原理和方法可以閱讀上一篇文章。
在球體模擬的3D全景場景下,可以使用經緯度座標和半徑來絕對定位空間中的一個點,同時滑鼠控制下的攝像機鏡頭旋轉用經緯度變化表示比較方便。
但是最終我們需要將經緯度轉換為真實的空間座標,轉換關係如下:
基於此,滑鼠操控可以如下實現(虛擬碼,具體程式碼可在codePen獲取):
// 重新計算攝像機視覺中心
function refreshCamera(){
var camera = world。camera, lat = world。lat, lon = world。lon;
// 控制維度處於-85和85之間
lat = Math。max( - 85, Math。min( 85, lat ) );
// 計算豎直夾角弧度 & 計算水平旋轉度(弧度)
var phi = THREE。Math。degToRad( 90 - lat ), theta = THREE。Math。degToRad( lon );
// 設定相機視角中心點
camera。target。x = 500 * Math。sin( phi ) * Math。cos( theta );
camera。target。y = 500 * Math。cos( phi );
camera。target。z = 500 * Math。sin( phi ) * Math。sin( theta );
camera。lookAt( camera。target );
}
// 開啟使用者控制
function startUserControl(){
var elem = document。getElementById(“container”);
elem。addEventListener(“mousedown”, function(e){
// 滑鼠按下,開始控制,記錄初始位置等資訊
});
elem。addEventListener(“mousemove”, function(e){
// 滑鼠拖動,根據滑鼠位置和初始位置計算檢視位置
});
elem。addEventListener(“mouseup”, function(e){
// 滑鼠抬起,停止控制
})
}
// 持續渲染重新整理
function anim(){
requestAnimationFrame(anim);
refreshCamera();
world。renderer。render(world。scene, world。camera);
}
如此,一個簡單的3D全景圖就已經實現了,具體的效果請見codePen。
如果你不僅僅想做一個玩具
一個簡單的3D全景圖只完成了一個特別糙的架子,仍然屬於玩具,如果想要繼續深入,可以考慮以下問題:
放縮
多種控制方式,例如手勢控制
圖片載入動畫
多場景的管理和切換
場景內增加各種物體 & 互動
等等
基於此,需要設計一個多層模組化的結果體系來實現全景圖,具體結構因為一些原因不便給出,有需要可以交流。
小結
到此為止,關於three。js和全景圖的介紹就基本結束了,這僅僅是基礎和入門的介紹,前方有更精彩的風景,歡迎大家互相交流。