摘要:本文旨在介紹如何搭建工程以支持多場(chǎng)景開(kāi)發(fā)。有了公用,我們希望這樣開(kāi)發(fā)應(yīng)用,即一個(gè)場(chǎng)景對(duì)應(yīng)一個(gè)腳本,形如繼承父類,開(kāi)發(fā)每一個(gè)場(chǎng)景啟動(dòng)渲染之前,創(chuàng)建場(chǎng)景模型場(chǎng)景資源加載完畢,可執(zhí)行音頻播放等。
本文旨在介紹如何搭建WebVR工程以支持多場(chǎng)景開(kāi)發(fā)。
首先,作為一個(gè)基本的前端工程來(lái)說(shuō),我們需要讓代碼“工程化”,不僅要提供編譯構(gòu)建、壓縮打包功能,還要讓每個(gè)頁(yè)面模塊化;
延伸到WebVR工程,我們也需要考慮就必須考慮“多頁(yè)面”模塊化,即提供多個(gè)場(chǎng)景模塊化開(kāi)發(fā),因?yàn)橐粋€(gè)完整的WebVR App不僅僅只有一個(gè)場(chǎng)景。這里可以參考google的WebVR多場(chǎng)景示例:https://vr.chromeexperiments....
多場(chǎng)景開(kāi)發(fā),最簡(jiǎn)單的方式就是,一個(gè)場(chǎng)景對(duì)應(yīng)一份html、css、js,多個(gè)頁(yè)面需要多個(gè)html,每次頁(yè)面跳轉(zhuǎn)需要重新進(jìn)行VR渲染進(jìn)行初始化。
實(shí)際上我們?cè)诙鄨?chǎng)景中,場(chǎng)景初始化只需要執(zhí)行一次(比如,創(chuàng)建一個(gè)場(chǎng)景->創(chuàng)建相機(jī)->創(chuàng)建渲染器),我們只需要一個(gè)index.html作為入口頁(yè)面,將VR場(chǎng)景初始化、創(chuàng)建、回收、切換封裝成公用組件。
在首次進(jìn)入場(chǎng)景時(shí)進(jìn)行初始化,在需要場(chǎng)景切換時(shí)進(jìn)行場(chǎng)景回收和按需加載,這樣一來(lái),用戶切換場(chǎng)景時(shí),不用把時(shí)間浪費(fèi)在等待html和初始化場(chǎng)景上。基于以上思路,本人總結(jié)的一套WebVR工程搭建方案,供各位參考。
項(xiàng)目地址:https://github.com/YorkChan94...
Demo:https://yorkchan94.github.io/...
相關(guān)技術(shù)棧:three.js、webpack2、es6/7
想詳細(xì)了解WebVR開(kāi)發(fā)步驟,也歡迎參考我的文章《VR大潮來(lái)襲——前端開(kāi)發(fā)能做些什么》
VR多場(chǎng)景模塊化開(kāi)發(fā)
支持VR場(chǎng)景創(chuàng)建、回收、切換
項(xiàng)目自動(dòng)化構(gòu)建與壓縮打包
支持es7/6
WebVR相關(guān)庫(kù)three.js
vrcontrols.js
vreffect.js
webvr-manager.js
webvr-polyfill.js
three-onevent.js
主要目錄結(jié)構(gòu)webpack |-- webpack.config.js # 公共配置 |-- webpack.dev.js # 開(kāi)發(fā)配置 |-- webpack.prod.js # 生產(chǎn)配置 src # 項(xiàng)目源碼 |-- page # WebVR場(chǎng)景目錄 | |-- index.js # WebVR入口場(chǎng)景 | |-- page1.js | |-- page2.js |-- common # 公共目錄,包括webvr封裝類和polyfill | |-- VRCore.js | |-- VRPage.js | |-- vendor.js |-- lib # vr三方插件,包括相機(jī)控制器和分屏器 | |-- vrcontrol.js | |-- vreffect.js |-- assets # 素材目錄,包括3d模型、紋理、音頻等 | |-- audio | |-- model | |-- texture |-- index.html # WebVR公用頁(yè)面 package.json READNE.md
我們先來(lái)看看index.html,其實(shí)整個(gè)body就只有一個(gè)dom,用來(lái)append我們的canvas,畢竟所以場(chǎng)景都在canvas里運(yùn)行。
webVR-INDEX
有了公用html,我們希望這樣開(kāi)發(fā)WebVR應(yīng)用,即一個(gè)場(chǎng)景對(duì)應(yīng)一個(gè)js腳本,形如:
// 繼承VRPage父類,開(kāi)發(fā)每一個(gè)場(chǎng)景 import VRPage from "common/js/VRPage"; class Page1 extends VRPage { start() { // 啟動(dòng)渲染之前,創(chuàng)建場(chǎng)景3d模型 let geometry = new THREE.CubeGeometry(5,5,5); let material = new THREE.MeshBasicMaterial( { color:0x00aadd} ); this.box = new THREE.Mesh(geometry,material); this.box.position.set(3,-2,-3); WebVR.Scene.add(this.box); } loaded() { // 場(chǎng)景資源加載完畢,可執(zhí)行音頻播放等。 } update(delta) { // 開(kāi)啟渲染之后,執(zhí)行模型動(dòng)畫(huà) this.box.rotation.y += 0.05; } } export default (() => { return new Page1(); })();
這里參照了類似Unity3d和React的開(kāi)發(fā)模式,在start方法里創(chuàng)建3d模型,在update方法里處理3d動(dòng)畫(huà),這樣的好處在于:
每一個(gè)場(chǎng)景都可以進(jìn)行獨(dú)立開(kāi)發(fā)而互不影響;
一旦VR環(huán)境初始化之后,不需要在每次場(chǎng)景跳轉(zhuǎn)切換時(shí)重新初始化一遍。
VRCore.js作為公用模塊管理整個(gè)webvr應(yīng)用的所有子場(chǎng)景,包括場(chǎng)景初始化、VR相機(jī)渲染、場(chǎng)景切換、場(chǎng)景回收等靜態(tài)函數(shù)。
VRPage.js作為每個(gè)場(chǎng)景的工廠類,支持不同3d頁(yè)面(場(chǎng)景)之間的代碼獨(dú)立。
每一個(gè)VR頁(yè)面的生命周期都是:創(chuàng)建物體->加載模型->啟動(dòng)渲染的過(guò)程,因此,需要?jiǎng)?chuàng)建一個(gè)基類,來(lái)實(shí)現(xiàn)每一個(gè)VR場(chǎng)景實(shí)例的生命周期。
//common/VRPage.js import * as WebVR from "VRCore.js" //管理所有場(chǎng)景的公用模塊 // VR場(chǎng)景工廠 export default class VRPage { constructor(options={}) { // 創(chuàng)建場(chǎng)景,如果場(chǎng)景已初始化 WebVR.createScene(options); this.start(); this.loadPage(); } loadPage() { THREE.DefaultLoadingManager.onLoad = () => { // 模型加載完畢,即開(kāi)啟渲染 WebVR.renderStart(this.update); this.loaded(); } } start() { // 實(shí)例的start方法將在啟動(dòng)渲染之前,場(chǎng)景相機(jī)初始化后執(zhí)行。 } loaded() { // 實(shí)例的loaded方法將在場(chǎng)景資源加載后執(zhí)行。 } update(delta) { // 實(shí)例的update方法將在渲染器每一次渲染時(shí)執(zhí)行。 } }
這里使用THREE.DefaultLoadingManager.onLoad方法監(jiān)聽(tīng)場(chǎng)景是否加載完畢,一旦加載完畢,便啟動(dòng)渲染。
WebVR場(chǎng)景首次渲染主要包括四個(gè)步驟
新建場(chǎng)景
創(chuàng)建VR相機(jī)
加載場(chǎng)景腳本與資源
開(kāi)啟動(dòng)畫(huà)渲染
function createScene({domContainer=document.body,fov=70,far=4000}) { // 創(chuàng)建場(chǎng)景 Scene = new THREE.Scene(); // 創(chuàng)建相機(jī) Camera = new THREE.PerspectiveCamera(fov,window.innerWidth/window.innerHeight,0.1,far); Camera.position.set( 0, 0, 0 ); Scene.add(Camera); // 創(chuàng)建渲染器 Renderer = new THREE.WebGLRenderer({ antialias: true } ); Renderer.setSize(window.innerWidth,window.innerHeight); Renderer.shadowMapEnabled = true; Renderer.setPixelRatio(window.devicePixelRatio); domContainer.appendChild(Renderer.domElement); initVR(); resize(); }
首先是three.js開(kāi)發(fā)三部曲,創(chuàng)建場(chǎng)景、相機(jī)、渲染器,接著調(diào)用initVR函數(shù)來(lái)完成VR場(chǎng)景分屏和陀螺儀控制,WebVR基本開(kāi)發(fā)步驟可以參考。
function initVR() { // 初始化VR分屏器和控制器 Effect = new THREE.VREffect(Renderer); Controls = new THREE.VRControls(Camera); // 初始化VR管理器 Manager = new WebVRManager(Renderer, Effect); window.addEventListener( "resize", e => { // 調(diào)整渲染器和相機(jī)以適應(yīng)窗口拉伸時(shí)寬高變動(dòng) Camera.aspect = window.innerWidth / window.innerHeight; Camera.updateProjectionMatrix(); Effect.setSize(window.innerWidth, window.innerHeight); }, false ); }
// VRCore.js function renderStart(callback) { // 設(shè)置loopID變量記錄每一幀ID loopID = 0; const loop = () => { if(loopID === -1) return; loopID = requestAnimationFrame(loop); callback(); Controls.update(); Manager.render(Scene, Camera); }; loop(); }
這里傳入?yún)?shù)動(dòng)畫(huà)渲染做了三件事,使用loopID作為整個(gè)VR應(yīng)用的全局變量,記錄每一幀動(dòng)畫(huà)的更新;更新相機(jī)控制器和VR渲染器,
WebVR場(chǎng)景切換主要包括四個(gè)步驟
暫停渲染
清空當(dāng)前場(chǎng)景物體
請(qǐng)求并加載目標(biāo)場(chǎng)景腳本與資源
重啟渲染
function renderStop() { if (loopID !== -1) { window.cancelAnimationFrame(loopID); loopID = -1; } }
function clearScene() { for(let i = Scene.children.length - 1; i >= 0; i-- ) { Scene.remove(Scene.children[i]); } }
切換到下一場(chǎng)景,我們需要請(qǐng)求對(duì)應(yīng)的場(chǎng)景腳本,這里使用webpack2的import函數(shù)進(jìn)行代碼分離,當(dāng)然你也可以使用require.ensure(filename => {require(filename)})方法。
import(`page/${fileName}.js`);
最終將清空當(dāng)前場(chǎng)景與請(qǐng)求加載目標(biāo)場(chǎng)景功能封裝為forward跳轉(zhuǎn)方法,就可以在頁(yè)面里直接調(diào)用了。
// common/VRCore.js function forward(fileName) { renderStop(); clearScene(); import(`page/${fileName}.js`); } // page/index.js ... class Index extends VRPage { start() { let geometry = new THREE.CubeGeometry(5,5,5); let material = new THREE.MeshBasicMaterial({ color: 0x00aadd }); this.box = new THREE.Mesh(geometry,material); this.box.position.set(3,-2,-3); // add gaze eventLisenter this.box.on("gaze",mesh => { // gazeIn trigger WebVR.forward("page2.js"); }); WebVR.Scene.add(box); } } ... // page2.js class page2 extends VRPage { start() { this.addPanorama(1000, ASSET_TEXTURE_SKYBOX); } addPanorama(radius,path) { // create panorama let geometry = new THREE.SphereGeometry(radius,50,50); let material = new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader().load(path),side:THREE.BackSide } ); let panorama = new THREE.Mesh(geometry,material); WebVR.Scene.add(panorama); return panorama; } } export default (() => { return new page2(); })();
我們?cè)趫?chǎng)景里創(chuàng)建一個(gè)立方體,當(dāng)凝視到該物體時(shí),執(zhí)行forward方法跳轉(zhuǎn)至page2場(chǎng)景。
至此,我們的WebVR工程已經(jīng)完成了一半,接下來(lái),我們使用Webpack2來(lái)構(gòu)建我們的工程。
開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境下webpack配置略有不同,這里主要給出webpack的基本配置,具體可參考項(xiàng)目地址。
const path = require("path"); const CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const ProvidePlugin = require("webpack/lib/ProvidePlugin"); module.exports = { entry: { "vendor": "./src/common/js/vendor.js", "app": "./src/page/index.js" }, output: { path: path.resolve(__dirname, "../dist/"), filename: "[name].js", sourceMapFilename: "[name].map", chunkFilename: "[id]-chunk.js", publicPath: "/" },
這里我們將webvr首個(gè)場(chǎng)景src/page/index.js作為項(xiàng)目打包入口,同時(shí)將page目錄下的文件也作為多帶帶chunk,配合按需加載來(lái)支持場(chǎng)景切換。
module: { rules: [ { test: /.js/, exclude: /node_modules/, use: [ { loader:"babel-loader",options: { presets: ["latest",["es2015", {"modules": false}]] } ] }, { test: /.css/, use: ["style-loader","css-loader"] }, { test: /.(jpg|png|mp4|wav|ogg|obj|mtl|dae)$/, loader: "file-loader" } ] },
這里引入file-loader,這樣就能在場(chǎng)景里直接import需要用到的素材,如下。
//page/page2.js import ASSET_TEXTURE_SKYBOX from "assets/texture/360_page2.jpg";
webpack相關(guān)的plugin配置如下
plugins: [ new CommonsChunkPlugin({ name: ["app", "vendor"], minChunks: Infinity }), new ProvidePlugin({ "THREE": "three", "WebVR": path.resolve(__dirname,"../src/common/js/VRCore.js") }), new HtmlWebpackPlugin({ inject: true, template: path.resolve(__dirname, "../src/index.html"), favicon: path.resolve(__dirname, "../src/favicon.ico") }) ] };
使用ProvidePlugin將three.js作為公用模塊輸出,以省去在每個(gè)腳本import THREE from "three"的重復(fù)工作,同時(shí)將管理所有場(chǎng)景的核心模塊VRCore.js作為全局公用模塊輸出。
使用HtmlWebpackPlugin將公用的html打包到dist目錄下。
最后是polyfill配置,我們需要引入webvr-polyfill和babel-polyfill來(lái)分別支持webvr API和ES6 API,并作為一個(gè)頁(yè)面獨(dú)立腳本。
// common/vendor.js import "babel-polyfill"; import "webvr-polyfill";
以上WebVR工程已經(jīng)基本搭建完畢,歡迎各位提出寶貴意見(jiàn),后續(xù)我們將探索daydream和Oculus在webvr上的開(kāi)發(fā)模式,敬請(qǐng)期待。
最后,獻(xiàn)上前幾天在google開(kāi)發(fā)者網(wǎng)站上看到的:預(yù)測(cè)未來(lái),不如創(chuàng)造未來(lái)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/83025.html
摘要:在文末,我會(huì)附上一個(gè)可加載的模型方便學(xué)習(xí)中文藝術(shù)字渲染用原生可以很容易地繪制文字,但是原生提供的文字效果美化功能十分有限。 showImg(https://segmentfault.com/img/bVWYnb?w=900&h=385); WebGL 可以說(shuō)是 HTML5 技術(shù)生態(tài)鏈中最為令人振奮的標(biāo)準(zhǔn)之一,它把 Web 帶入了 3D 的時(shí)代。 初識(shí) WebGL 先通過(guò)幾個(gè)使用 Web...
摘要:目錄如何用提高效率后端掘金經(jīng)常有人說(shuō)我應(yīng)該學(xué)一門(mén)語(yǔ)言,比如之類,但是卻不知道如何入門(mén)。本文將通過(guò)我是如何開(kāi)發(fā)公司年會(huì)抽獎(jiǎng)系統(tǒng)的后端掘金需求出現(xiàn)年會(huì)將近,而年會(huì)抽獎(jiǎng)環(huán)節(jié)必不可少,但是抽獎(jiǎng)系統(tǒng)卻還沒(méi)有。 云盤(pán)一個(gè)個(gè)倒下怎么辦?無(wú)需編碼,手把手教你搭建至尊私享云盤(pán) - 工具資源 - 掘金微盤(pán)掛了,360倒了,百度云盤(pán)也立了Flag。能讓我們?cè)谠贫藘?chǔ)存分享文件的服務(wù)越來(lái)越少了。 買(mǎi)一堆移動(dòng)硬盤(pán)...
摘要:它與智能手機(jī)相連接,將顯示變成顯示。廠商滑配式設(shè)備包括谷歌和三星。作為一款獨(dú)立的計(jì)算設(shè)備,整合式設(shè)備將配備一整套零部件,價(jià)格甚至高于普通。廠商微軟對(duì)于想初步體驗(yàn)或入門(mén)的用戶,推薦谷歌的或國(guó)內(nèi)的性價(jià)比高的滑配式設(shè)備。 本文轉(zhuǎn)自凹凸實(shí)驗(yàn)室:https://aotu.io/notes/2016/08... showImg(https://segmentfault.com/img/bVC2cm...
閱讀 1274·2021-11-23 09:51
閱讀 1635·2021-11-16 11:45
閱讀 4060·2021-10-09 09:43
閱讀 2694·2021-07-22 16:47
閱讀 953·2019-08-27 10:55
閱讀 3456·2019-08-26 17:40
閱讀 3098·2019-08-26 11:39
閱讀 3238·2019-08-23 18:39