自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

淺談 WebVR 全景

開發(fā) VR/AR
虛擬現(xiàn)實(英語:virtual reality,縮寫VR),是利用電腦模擬產(chǎn)生一個三維空間的虛擬世界,提供用戶關于視覺等感官的模擬,讓用戶感覺仿佛身歷其境,可以即時、沒有限制地觀察三維空間內(nèi)的事物。用戶進行位置移動時,電腦可以立即進行復雜的運算,將精確的三維世界影像傳回產(chǎn)生臨場感。

前言

近年來VR概念越來越火,相信大家在網(wǎng)上都有過VR的瀏覽體驗,比如VR全景看房[1]、VR全景看車[2]、VR全景旅游[3]等等,VR全景給了我們視覺上的沉浸式體驗。本文將會簡單探究Web VR全景的實現(xiàn)原理,同時也會用threejs實現(xiàn)兩個小的demo,希望對大家以后在業(yè)務上遇到類似的場景能有所幫助。

什么是VR

引用維基百科的定義:

虛擬現(xiàn)實(英語:virtual reality,縮寫VR),是利用電腦模擬產(chǎn)生一個三維空間的虛擬世界,提供用戶關于視覺等感官的模擬,讓用戶感覺仿佛身歷其境,可以即時、沒有限制地觀察三維空間內(nèi)的事物。用戶進行位置移動時,電腦可以立即進行復雜的運算,將精確的三維世界影像傳回產(chǎn)生臨場感。

與基于現(xiàn)實場景進行增強效果的 AR(Augmented Reality)的區(qū)別在于,VR 的場景需要完全重建,類似于進入另一個世界。

虛擬現(xiàn)實的實現(xiàn)原理

人眼對世界的感知,是通過將三維世界投射至視網(wǎng)膜上,以二維圖像建立的視覺體系。所以一張具備透視關系的圖像,在特定的角度,可以使人感受到三維的空間關系,這就是人眼的深度知覺(depth perception)。VR 技術則建立在這個基礎之上。

廣泛意義上來說,只要符合模擬三維空間這一行為,就可以稱為 VR,手機、電腦、大熒幕、VR 眼鏡甚至于空氣,都可以成為 VR 的載體。

如何在web上模擬三維空間

三維空間是由2D和3D圖形構成的,要模擬三維空間,必須繪制2D和3D圖形,我們寫js有ECMAScript規(guī)范,同理的繪制2D和3D圖形也有一套規(guī)范,這套規(guī)范就是OpenGL

?OpenGL是什么?

OpenGL(英語:Open Graphics Library,譯名:開放圖形庫或者“開放式圖形庫”)是用于渲染2D、3D矢量圖形的跨語言、跨平臺的應用程序編程接口(API)。

OpenGL規(guī)范描述了繪制2D和3D圖形的抽象API,常用于CAD、虛擬現(xiàn)實、科學可視化程序和電子游戲開發(fā)。

?在Web上能用OpenGL嗎?

可以的,在web上可以使用WebGL,它是Web瀏覽器中OpenGL的JavaScript綁定。

?什么是WebGL?

WebGL是一種JavaScript API,用于在不使用外掛程式的情況下在任何相容的網(wǎng)頁瀏覽器中呈現(xiàn)交互式2D和3D圖形。WebGL完全整合到瀏覽器的所有網(wǎng)頁標準中,可將影像處理和效果的GPU加速使用方式當做網(wǎng)頁Canvas的一部分。WebGL元素可以加入其他HTML元素之中并與網(wǎng)頁或網(wǎng)頁背景的其他部分混合。

有了WebGL API,我們就可以web網(wǎng)頁上構建三維空間啦。我們可以借助canvas標簽來獲取WebGL實例來進行WebGL API的調用,更多API可以戳WebGL: 2D and 3D graphics for the web - Web APIs | MDN[4]查看,下面是使用示例:

// 創(chuàng)建canvas標簽
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);

// 獲取webgl實例
const gl = canvas.getContext("webgl");
// 調用webgl api繪制圖形
// .....

目前業(yè)界有很多成熟的WebGL3D引擎,比如three.js[5]、babylon.js[6]等等,這些3D引擎已經(jīng)對WebGL API做了非常高效的封裝,可以幫助我們快速繪制2D和3D圖形。

three.js實現(xiàn)vr全景

在實戰(zhàn)前先了解下three.js的基本概念

?基本概念?

在three.js中,渲染一個三維空間的必要因素是場景(scene)、攝像機(camera)、渲染器(renderer)。渲染出一個三維空間后,可以往里面增加各種各樣的物體、光源等等。

場景(scene)

一個容器,容納著除渲染器以外的3d世界里的一切。場景的元素采用右手坐標系,x軸正方向向右,y軸正方向向上,z軸由屏幕從里向外。

圖片

// three.js創(chuàng)建場景
const scene = new THREE.Scene();

攝像機(camera)

就像人的眼睛,在一個空間里可以看向任意方向,可以通過參數(shù)調節(jié)可視角度和可視距離。

three.js中常用的camera有兩種,透視投影相機(PerspectiveCamera)與正交投影相機(OrthographicCamera)。這里的投影是指將三維空間中的物體坐標投影到二維平面上。

圖片

  • 透視 投影是將每個點都投影到三維空間中,近大遠小,看起來更符合真實世界看到的物體。
  • 正交 投影是只考慮所有點的XY坐標,每一個二維空間中的點都是與Z軸平行的直線在觀察平面上的投影。所看到的物體大小不會受到距離遠近的影響。

透視相機(PerspectiveCamera)

使用perspective projection(透視投影)來進行投影。

這一投影模式被用來模擬人眼所看到的景象,它是3D場景的渲染中使用得最普遍的投影模式。

PerspectiveCamera(fov, aspect, near, far) 有四個參數(shù):

  • fov - field of view,視野角,下圖中綠色英文標注的地方,是距離觀測點near長度處,最上端與最下端之間的角度,F(xiàn)ov越大,表示眼睛睜得越大,離得越遠,看得更多。
  • aspect - 畫面橫寬比
  • near - 相機最近范圍內(nèi)可以看到的物體的距離
  • far - 相機最遠范圍內(nèi)可以看到的物體的距離

圖片

正交相機(OrthographicCamera)

這一攝像機使用orthographic projection(正交投影)來進行投影。

在這種投影模式下,無論物體距離相機距離遠或者近,在最終渲染的圖片中物體的大小都保持不變。這對于渲染2D場景或者UI元素是非常有用的。

OrthographicCamera(left, right, top, bottom, near, far)有六個參數(shù)

left, right, top, bottom - 分別是紅色點距離左右上下邊框的距離,對應圖中XY軸的值;

near - 場景開始渲染并可以顯示的起點,對應圖中Z軸坐標的值,通常為負;

far - 場景結束渲染的終點,對應圖中Z軸坐標的值,通常為正;

圖片

渲染器(renderer)?

將camera在scene里看到的內(nèi)容渲染/繪制到畫布上

設置好 dpr、畫布寬高,Three.js 就會生成一個 canvas。

const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
const canvas = renderer.domElement;
document.body.appendChild(canvas);
// 將camera在scene里看到的內(nèi)容渲染/繪制到畫布上
renderer.render( scene, camera );

幾何體(geometry)

三維空間里的所有物體都是點組成面,面組成幾何體。

three.js核心中內(nèi)置了多種幾何體,如下圖的幾何體從左到右分別為:球緩沖幾何體(SphereGeometry)?、 圓柱緩沖幾何體(CylinderGeometry)、立方緩沖幾何體(BoxGeometry)、圓錐緩沖幾何體(ConeGeometry)?、 圓環(huán)(TorusGeometry)和圓環(huán)緩沖扭結幾何體(TorusKnotGeometry)等等

貼圖(texture)

想象一下你手里有一個幾何體,你用一張A4紙包裹上幾何體,并在上面畫畫。你畫的內(nèi)容就是貼圖。

下面給立方體貼上小黃鴨貼圖:

// 立方緩沖幾何體(BoxGeometry)
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
// 小黃鴨貼圖
const texture = new THREE.TextureLoader().load( "textures/duck.png" );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const cube = new THREE.Mesh( boxGeometry, material );
scene.add( cube );

圖片

也可以給立方體貼上視頻

const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
const video = document.getElementById( 'video' );
const texture = new THREE.VideoTexture( video );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const cube = new THREE.Mesh( boxGeometry, material );
scene.add( cube );

圖片

材質(material)

延續(xù)貼圖里的想象,你用白卡紙畫畫,還是用油紙畫畫,呈現(xiàn)出來的質感是不同的,這就是材質!

下面五個圓環(huán)扭結的顏色都是一樣的,而材質從左至右分別是:

  • MeshBasicMaterial(基礎材質,簡單的幾何材料,不考慮光照的影響)
  • MeshMatcapMaterial(網(wǎng)帽材質,在場景沒有光源的情況下,會模擬出物體被光照的效果)
  • MeshPhongMaterial(高光材質,適用于陶瓷,烤漆類質感)
  • MeshToonMaterial(卡通材質)
  • MeshLambertMaterial (非光澤表面的材質,沒有鏡面高光,可以很好地模擬一些表面,例如未經(jīng)處理的木材或石材,但不能模擬具有鏡面高光的光澤表面,例如涂漆木材。)

環(huán)境光的情況下:

圖片

有從上往下的平行光:

圖片

燈光(light)

在沒有手動創(chuàng)建光的情況下會默認有個環(huán)境光,不然你什么都看不到。

常見的燈光有以下幾種類型:

AmbientLight:環(huán)境光,沒有方向,會均勻的照亮場景中的所有物體,不會產(chǎn)生陰影

DirectionalLight:平行光,常常用平行光來模擬太陽光 的效果; 太陽足夠遠,因此我們可以認為太陽的位置是無限遠,所以我們認為從太陽發(fā)出的光線也都是平行的。

圖片

PointLight:點光源,從一個點向各個方向發(fā)射的光源??梢阅M一個燈泡發(fā)出的光。

圖片

SpotLight?:聚光燈,光線從一個點沿一個方向射出,隨著光線照射的變遠,光線圓錐體的尺寸也逐漸增大。

圖片

?VR全景看房的實現(xiàn)?

原理

我們可以通過模擬人走在房子里的行為來實現(xiàn):

  • 用攝像機模擬人,鏡頭相當于人的眼睛
  • 用立方幾何體六個面貼上貼圖模擬房子,貼圖要求真實還原房子六個面的情況
  • 將攝像機放置在幾何體里面,移動攝像機相當于人在房子里走動

素材準備

房子六面貼圖:

圖片

代碼實現(xiàn)

1.創(chuàng)建場景和攝像機,并且設置攝像機位置和添加加軌道控制器

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 100 );
// 設置攝像機位置
camera.position.set(1, 1, 4);
const controls = new THREE.OrbitControls( camera, renderer.domElement );

2.創(chuàng)建立方幾何體,并且給幾何體貼上準備好的貼圖

// 獲取加載6個面的貼圖
const textures = getTexturesFromAtlasFile( 'textures/house.jpeg', 6 );
const materials = [];
for ( let i = 0; i < 6; i ++ ) {
// 將貼圖貼在立方幾何體上
materials.push( new THREE.MeshBasicMaterial( { map: textures[ i ] } ) );
}
// 創(chuàng)建立方幾何體
const skyBox = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), materials );
// 把幾何體加到場景里
scene.add( skyBox );

function getTexturesFromAtlasFile( atlasImgUrl, tilesNum ) {
const textures = [];
for ( let i = 0; i < tilesNum; i ++ ) {
textures[ i ] = new THREE.Texture();
}
new THREE.ImageLoader()
.load( atlasImgUrl, ( image ) => {
let canvas, context;
const tileWidth = image.height;
for ( let i = 0; i < textures.length; i ++ ) {
canvas = document.createElement( 'canvas' );
context = canvas.getContext( '2d' );
canvas.height = tileWidth;
canvas.width = tileWidth;
context.drawImage( image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth );
textures[ i ].image = canvas;
textures[ i ].needsUpdate = true;
}
} );
return textures;
}

頁面效果:

圖片

3.將攝像頭的位置移動到幾何體內(nèi)部

camera.position.set(0, 0, -0.01);

將攝像機移動到幾何體內(nèi)部后發(fā)現(xiàn)視野一片黑暗,這是因為貼圖是在幾何體外表面,所以需要將貼圖翻轉至內(nèi)表面。

4.將幾何體貼圖翻轉至內(nèi)表面,就能看到全景啦

skyBox.geometry.scale( 1, 1, - 1 );

5.監(jiān)聽鍵盤事件移動攝像機

window.addEventListener('keydown', onKeydown);
function onKeydown(e) {
switch(e.keyCode) {
//
case 37:

if(camera.position.x > -0.5) {
camera.position.x -= 0.01;
}
break;
//
case 38:
if(camera.position.z > -0.5) {
camera.position.z -= 0.01;
}
break;
//
case 39:
if(camera.position.x < 0.5) {
camera.position.x += 0.01;
}
break;
//
case 40:
if(camera.position.z < 0.5) {
camera.position.z += 0.01;
}
break;
}
}

最終效果

圖片

?VR全景看車的實現(xiàn)?

原理

VR全景看車的難點是汽車模型的設計,需要設計一個3d汽車模型,然后渲染到頁面上。汽車換裝則是通過對關鍵節(jié)點的操作來完成。

另外我們一般說的3d模型就是一個或多個幾何體,只是有的3d模型文件里除了包含幾何體還可以包含一些額外的信息,比如貼圖,材質等等需要在讀取模型文件時解析出來

3d模型的文件格式有很多,但threejs里常用的基本是:

  • OBJ格式:老牌通用3d模型文件,不包含貼圖,材質,動畫等信息。
  • GLTF格式(圖形語言傳輸格式):由OpenGL官方維護團隊推出的現(xiàn)代3d模型通用格式,可以包含幾何體、材質、動畫及場景、攝影機等信息,并且文件量還小。有3D模型界的JPEG之稱。GLTF格式有.gltf(JSON/ASCII)和.glb(二進制)兩種拓展名。

素材準備

官網(wǎng)上找了一個汽車模型,可以用PlayCanvas glTF Viewer[7]查看汽車模型的節(jié)點信息。

圖片

代碼實現(xiàn)

1.創(chuàng)建場景和攝像機,并且設置攝像機位置和添加加軌道控制器

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
const camera.position.set( 4.25, 1.4, - 4.5 );
const controls = new THREE.OrbitControls( camera, container );

2.加載汽車模型

通過GLTFLoader,我們可以加載一個gltf?格式的3d模型文件。需要注意的是,這些Loader都以插件的形式存在,需要引入相應的XXXLoader.js才能使用

const bodyMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xff0000, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5
} );

const detailsMaterial = new THREE.MeshStandardMaterial( {
color: 0xffffff, metalness: 1.0, roughness: 0.5
} );

const glassMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0
} );

const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath( 'draco/' );
// GLTF文件loader
const loader = new THREE.GLTFLoader();
loader.setDRACOLoader( dracoLoader );
// 存儲車輪,后面讓車輪旋轉
const wheels = [];
loader.load( 'glb/car.glb', function ( gltf ) {
// 獲取模型節(jié)點
const carModel = gltf.scene.children[ 0 ];
// 重定義模型節(jié)點的材質
carModel.getObjectByName( 'body' ).material = bodyMaterial;
carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;
carModel.getObjectByName( 'trim' ).material = detailsMaterial;
carModel.getObjectByName( 'glass' ).material = glassMaterial;
// 存儲車輪
wheels.push(
carModel.getObjectByName( 'wheel_fl' ),
carModel.getObjectByName( 'wheel_fr' ),
carModel.getObjectByName( 'wheel_rl' ),
carModel.getObjectByName( 'wheel_rr' )
);

// 車底陰影
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),
new THREE.MeshBasicMaterial( {
map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true
} )
);
mesh.rotation.x = - Math.PI / 2;
mesh.renderOrder = 2;
carModel.add( mesh );

scene.add( carModel );
});

3.車輪滾動

function render() {
controls.update();
const time = - performance.now() / 1000;
// 讓車輪旋轉,模擬行駛效果
for ( let i = 0; i < wheels.length; i ++ ) {
wheels[ i ].rotation.x = time * Math.PI * 2;
}
grid.position.z = - ( time ) % 1;
renderer.render( scene, camera );
stats.update();
}

4.提供拾色器,改變車身(body)、細節(jié)(detail)和玻璃(glass)的顏色

<div id="info">
<span class="colorPicker"><input id="body-color" type="color" value="#ff0000"></input><br/>Body</span>
<span class="colorPicker"><input id="details-color" type="color" value="#ffffff"></input><br/>Details</span>
<span class="colorPicker"><input id="glass-color" type="color" value="#ffffff"></input><br/>Glass</span>
</div>
// 車身顏色
const bodyColorInput = document.getElementById( 'body-color' );
bodyColorInput.addEventListener( 'input', function () {
bodyMaterial.color.set( this.value );
} );
// 車細節(jié)顏色
const detailsColorInput = document.getElementById( 'details-color' );
detailsColorInput.addEventListener( 'input', function () {
detailsMaterial.color.set( this.value );
} );
// 車玻璃顏色
const glassColorInput = document.getElementById( 'glass-color' );
glassColorInput.addEventListener( 'input', function () {
glassMaterial.color.set( this.value );
} );

最終效果

圖片

總結一下

以上只是本人對web VR全景的一個粗淺的探究,希望能夠引起你們對web VR的興趣。threejs(或其他3d引擎)能做的東西遠不止于此,還有非常多炫酷、好玩的3d場景,感興趣的可以私下探索哦!

參考資料

[1]VR全景看房: https://bj.ke.com/

[2]VR全景看車: https://car.autohome.com.cn/vr/list-0-0-0-1.html

[3]VR全景旅游: http://www.quanjingke.com/

[4]WebGL: 2D and 3D graphics for the web - Web APIs | MDN: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API

[5]three.js: https://github.com/mrdoob/three.js/

[6]babylon.js: https://github.com/BabylonJS/Babylon.js

[7]PlayCanvas glTF Viewer: https://playcanvas.com/viewer

[8]three.js docs: https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene

[9]three.js中兩種常用的攝像機模式: https://segmentfault.com/a/1190000013145246

[10]Three 之 three.js (webgl)涉及的各種材質簡單說明(常用材質配有效果圖)_仙魁XAN的博客-CSDN博客_threejs材質和紋理: https://blog.csdn.net/u014361280/article/details/124416631

[11]Threejs 實現(xiàn)簡單的全景圖: https://fangsueluo.github.io/2021/01/12/vr-threejs-learning/

[12]三種前端實現(xiàn)VR全景看房的方案!說不定哪天就用得上! - 掘金: https://juejin.cn/post/6973865268426571784

[13]一步步帶你實現(xiàn)web全景看房--three.js - 掘金: https://juejin.cn/post/6844903918409875469

[14]硬核看房利器——Web 全景的實現(xiàn): https://segmentfault.com/a/1190000024476102

[15]2天賺了4個W,手把手教你用Threejs搭建一個Web3D汽車展廳! - 掘金: https://juejin.cn/post/6981249521258856456

[16]WebGL坐標系介紹_桑榆未晚_的博客-CSDN博客_webgl坐標系: https://blog.csdn.net/TW934440653/article/details/102529017?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-102529017-blog-70878160.pc_relevant_multi_platform_whitelistv4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-102529017-blog-70878160.pc_relevant_multi_platform_whitelistv4&utm_relevant_index=2

- EN?

責任編輯:武曉燕 來源: Lab團隊
相關推薦

2016-04-13 09:52:31

虛擬現(xiàn)實webvr搭建

2023-09-20 00:21:51

Redis數(shù)據(jù)庫

2016-09-12 14:23:37

Edge瀏覽器WebVR

2017-08-22 21:42:51

WebVRFirefox瀏覽器

2020-12-31 05:33:34

軟件性能優(yōu)化

2009-10-15 10:58:05

敏捷開發(fā)全景圖

2021-12-08 11:30:41

Python全景拼接代碼

2009-06-08 15:14:54

CMDB運維管理摩卡

2011-06-01 14:18:41

JVM

2009-12-01 10:29:42

BSM

2019-05-22 10:43:16

Cgroups容器監(jiān)控

2010-12-01 09:50:21

全景視圖Windows Pho

2016-07-14 16:11:30

華為

2021-06-08 13:28:57

JavaScript開發(fā)代碼

2010-07-23 14:34:38

2020-02-15 17:03:25

網(wǎng)絡威脅網(wǎng)絡安全網(wǎng)絡釣魚

2022-07-22 13:13:05

機器視覺人工智能

2013-07-04 10:05:50

軟件定義網(wǎng)絡SDN

2021-04-30 14:57:01

區(qū)塊鏈銀行數(shù)據(jù)

2016-03-08 15:18:47

GMGC
點贊
收藏

51CTO技術棧公眾號