您當前的位置:首頁 > 旅遊

Three.js開發基礎和3D全景(三)

作者:由 宋仔 發表于 旅遊時間:2017-07-02

具體效果&文章所涉及的程式碼可以在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和全景圖的介紹就基本結束了,這僅僅是基礎和入門的介紹,前方有更精彩的風景,歡迎大家互相交流。

標簽: 全景圖  3D  three  World  function