摘要:本文旨在介紹如何搭建工程以支持多場景開發。有了公用,我們希望這樣開發應用,即一個場景對應一個腳本,形如繼承父類,開發每一個場景啟動渲染之前,創建場景模型場景資源加載完畢,可執行音頻播放等。
本文旨在介紹如何搭建WebVR工程以支持多場景開發。
首先,作為一個基本的前端工程來說,我們需要讓代碼“工程化”,不僅要提供編譯構建、壓縮打包功能,還要讓每個頁面模塊化;
延伸到WebVR工程,我們也需要考慮就必須考慮“多頁面”模塊化,即提供多個場景模塊化開發,因為一個完整的WebVR App不僅僅只有一個場景。這里可以參考google的WebVR多場景示例:https://vr.chromeexperiments....
多場景開發,最簡單的方式就是,一個場景對應一份html、css、js,多個頁面需要多個html,每次頁面跳轉需要重新進行VR渲染進行初始化。
實際上我們在多場景中,場景初始化只需要執行一次(比如,創建一個場景->創建相機->創建渲染器),我們只需要一個index.html作為入口頁面,將VR場景初始化、創建、回收、切換封裝成公用組件。
在首次進入場景時進行初始化,在需要場景切換時進行場景回收和按需加載,這樣一來,用戶切換場景時,不用把時間浪費在等待html和初始化場景上。基于以上思路,本人總結的一套WebVR工程搭建方案,供各位參考。
項目地址:https://github.com/YorkChan94...
Demo:https://yorkchan94.github.io/...
相關技術棧:three.js、webpack2、es6/7
想詳細了解WebVR開發步驟,也歡迎參考我的文章《VR大潮來襲——前端開發能做些什么》
VR多場景模塊化開發
支持VR場景創建、回收、切換
項目自動化構建與壓縮打包
支持es7/6
WebVR相關庫three.js
vrcontrols.js
vreffect.js
webvr-manager.js
webvr-polyfill.js
three-onevent.js
主要目錄結構webpack |-- webpack.config.js # 公共配置 |-- webpack.dev.js # 開發配置 |-- webpack.prod.js # 生產配置 src # 項目源碼 |-- page # WebVR場景目錄 | |-- index.js # WebVR入口場景 | |-- page1.js | |-- page2.js |-- common # 公共目錄,包括webvr封裝類和polyfill | |-- VRCore.js | |-- VRPage.js | |-- vendor.js |-- lib # vr三方插件,包括相機控制器和分屏器 | |-- vrcontrol.js | |-- vreffect.js |-- assets # 素材目錄,包括3d模型、紋理、音頻等 | |-- audio | |-- model | |-- texture |-- index.html # WebVR公用頁面 package.json READNE.md
我們先來看看index.html,其實整個body就只有一個dom,用來append我們的canvas,畢竟所以場景都在canvas里運行。
webVR-INDEX
有了公用html,我們希望這樣開發WebVR應用,即一個場景對應一個js腳本,形如:
// 繼承VRPage父類,開發每一個場景 import VRPage from "common/js/VRPage"; class Page1 extends VRPage { start() { // 啟動渲染之前,創建場景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() { // 場景資源加載完畢,可執行音頻播放等。 } update(delta) { // 開啟渲染之后,執行模型動畫 this.box.rotation.y += 0.05; } } export default (() => { return new Page1(); })();
這里參照了類似Unity3d和React的開發模式,在start方法里創建3d模型,在update方法里處理3d動畫,這樣的好處在于:
每一個場景都可以進行獨立開發而互不影響;
一旦VR環境初始化之后,不需要在每次場景跳轉切換時重新初始化一遍。
VRCore.js作為公用模塊管理整個webvr應用的所有子場景,包括場景初始化、VR相機渲染、場景切換、場景回收等靜態函數。
VRPage.js作為每個場景的工廠類,支持不同3d頁面(場景)之間的代碼獨立。
每一個VR頁面的生命周期都是:創建物體->加載模型->啟動渲染的過程,因此,需要創建一個基類,來實現每一個VR場景實例的生命周期。
//common/VRPage.js import * as WebVR from "VRCore.js" //管理所有場景的公用模塊 // VR場景工廠 export default class VRPage { constructor(options={}) { // 創建場景,如果場景已初始化 WebVR.createScene(options); this.start(); this.loadPage(); } loadPage() { THREE.DefaultLoadingManager.onLoad = () => { // 模型加載完畢,即開啟渲染 WebVR.renderStart(this.update); this.loaded(); } } start() { // 實例的start方法將在啟動渲染之前,場景相機初始化后執行。 } loaded() { // 實例的loaded方法將在場景資源加載后執行。 } update(delta) { // 實例的update方法將在渲染器每一次渲染時執行。 } }
這里使用THREE.DefaultLoadingManager.onLoad方法監聽場景是否加載完畢,一旦加載完畢,便啟動渲染。
WebVR場景首次渲染主要包括四個步驟
新建場景
創建VR相機
加載場景腳本與資源
開啟動畫渲染
function createScene({domContainer=document.body,fov=70,far=4000}) { // 創建場景 Scene = new THREE.Scene(); // 創建相機 Camera = new THREE.PerspectiveCamera(fov,window.innerWidth/window.innerHeight,0.1,far); Camera.position.set( 0, 0, 0 ); Scene.add(Camera); // 創建渲染器 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開發三部曲,創建場景、相機、渲染器,接著調用initVR函數來完成VR場景分屏和陀螺儀控制,WebVR基本開發步驟可以參考。
function initVR() { // 初始化VR分屏器和控制器 Effect = new THREE.VREffect(Renderer); Controls = new THREE.VRControls(Camera); // 初始化VR管理器 Manager = new WebVRManager(Renderer, Effect); window.addEventListener( "resize", e => { // 調整渲染器和相機以適應窗口拉伸時寬高變動 Camera.aspect = window.innerWidth / window.innerHeight; Camera.updateProjectionMatrix(); Effect.setSize(window.innerWidth, window.innerHeight); }, false ); }
// VRCore.js function renderStart(callback) { // 設置loopID變量記錄每一幀ID loopID = 0; const loop = () => { if(loopID === -1) return; loopID = requestAnimationFrame(loop); callback(); Controls.update(); Manager.render(Scene, Camera); }; loop(); }
這里傳入參數動畫渲染做了三件事,使用loopID作為整個VR應用的全局變量,記錄每一幀動畫的更新;更新相機控制器和VR渲染器,
WebVR場景切換主要包括四個步驟
暫停渲染
清空當前場景物體
請求并加載目標場景腳本與資源
重啟渲染
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]); } }
切換到下一場景,我們需要請求對應的場景腳本,這里使用webpack2的import函數進行代碼分離,當然你也可以使用require.ensure(filename => {require(filename)})方法。
import(`page/${fileName}.js`);
最終將清空當前場景與請求加載目標場景功能封裝為forward跳轉方法,就可以在頁面里直接調用了。
// 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(); })();
我們在場景里創建一個立方體,當凝視到該物體時,執行forward方法跳轉至page2場景。
至此,我們的WebVR工程已經完成了一半,接下來,我們使用Webpack2來構建我們的工程。
開發環境和生產環境下webpack配置略有不同,這里主要給出webpack的基本配置,具體可參考項目地址。
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首個場景src/page/index.js作為項目打包入口,同時將page目錄下的文件也作為多帶帶chunk,配合按需加載來支持場景切換。
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,這樣就能在場景里直接import需要用到的素材,如下。
//page/page2.js import ASSET_TEXTURE_SKYBOX from "assets/texture/360_page2.jpg";
webpack相關的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作為公用模塊輸出,以省去在每個腳本import THREE from "three"的重復工作,同時將管理所有場景的核心模塊VRCore.js作為全局公用模塊輸出。
使用HtmlWebpackPlugin將公用的html打包到dist目錄下。
最后是polyfill配置,我們需要引入webvr-polyfill和babel-polyfill來分別支持webvr API和ES6 API,并作為一個頁面獨立腳本。
// common/vendor.js import "babel-polyfill"; import "webvr-polyfill";
以上WebVR工程已經基本搭建完畢,歡迎各位提出寶貴意見,后續我們將探索daydream和Oculus在webvr上的開發模式,敬請期待。
最后,獻上前幾天在google開發者網站上看到的:預測未來,不如創造未來。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83025.html
摘要:在文末,我會附上一個可加載的模型方便學習中文藝術字渲染用原生可以很容易地繪制文字,但是原生提供的文字效果美化功能十分有限。 showImg(https://segmentfault.com/img/bVWYnb?w=900&h=385); WebGL 可以說是 HTML5 技術生態鏈中最為令人振奮的標準之一,它把 Web 帶入了 3D 的時代。 初識 WebGL 先通過幾個使用 Web...
摘要:目錄如何用提高效率后端掘金經常有人說我應該學一門語言,比如之類,但是卻不知道如何入門。本文將通過我是如何開發公司年會抽獎系統的后端掘金需求出現年會將近,而年會抽獎環節必不可少,但是抽獎系統卻還沒有。 云盤一個個倒下怎么辦?無需編碼,手把手教你搭建至尊私享云盤 - 工具資源 - 掘金微盤掛了,360倒了,百度云盤也立了Flag。能讓我們在云端儲存分享文件的服務越來越少了。 買一堆移動硬盤...
閱讀 1260·2021-11-23 09:51
閱讀 1628·2021-11-16 11:45
閱讀 4014·2021-10-09 09:43
閱讀 2682·2021-07-22 16:47
閱讀 944·2019-08-27 10:55
閱讀 3449·2019-08-26 17:40
閱讀 3083·2019-08-26 11:39
閱讀 3228·2019-08-23 18:39