WebVR開發教程——深度剖析

最近WebVR API 1.1已經發布,2.0草案也在擬定中,在我看來,WebVR走向大衆瀏覽器是遲早的事情了,今天本人將對WebVR開發環境和開發流程進行深刻介紹。前端

WebVR與WebVR API

首先,WebVR指的是使用瀏覽器體驗VR的方式,現在已經成爲了一種開放標準。
它提供了JavaScript API,使開發者能夠獲取vr設備的輸入信息,來改變用戶在虛擬空間裏的位置、視覺、行爲等。
如下是目前主流VR及瀏覽器對WebVR的支持狀況。node

遺憾的是,WebVR的體驗方式目前只能運行在Android和Windows系統上。不過這並不影響咱們在mac和linux上開發與調試。linux

移動VR兩巨頭:Daydream 與 Gear VRgit

 

WebVR的開發環境配置

因爲WebVR App須要運行VR設備上,而目前購買一臺VR設備的成本不低,因此這裏我總結了一套開發環境下WebVR調試方案。
首先咱們須要給WebVR靜態頁面起一個web server,這裏我安裝 Web Server for Chrome,你也可使用node或者上傳至github託管。github

PC端調試

1. 安裝chrome擴展程序 WebVR API Emulation

使用WebVR API Emulation擴展程序能夠模擬VR設備用戶的視角、位置等。web

WebVR API Emulation模擬VR體驗chrome

移動端調試

適用於cardboard級別的WebVR App調試canvas

1. 安裝chrome beta

目前須要webvr還屬於早期實驗階段,須要下載chrome beta最新版,安裝完須要手動開啓webvr支持,在瀏覽器地址欄輸入chrome://flags#enable-webvr,點擊啓用並從新啓動chrome。api

2. 安裝Google VR 服務

這是google給cardboard、daydream用戶提供VR服務配置,能夠提供VR模式窗口,以下圖。
最後你能夠在chrome上打開WebVR示例頁面驗證是否配置成功promise

WebVR示例應用

3. chrome inspector調試

經過手機chrome訪問咱們開發的WebVR頁面,在PC端chrome輸入chrome://inspector進行調試,具體能夠參考 遠程調試 Android 設備使用入門

完成WebVR開發環境配置以後,咱們將正式進入WebVR開發之旅。

 

使用WebGL開發WebVR

WebVR App實現依賴於WebGL技術開發,WebGL是在瀏覽器上建立和運行3D圖像,它遵循OpenGL ES的規範,經過GLSL語言操做GPU進行頂點片元渲染。

3維模型矩陣變換

在WebGL場景中,3d物體都是經過矩陣變換最終造成屏幕上的2d圖像,[投影矩陣ProjectionMatrix] × [視圖矩陣ViewMatrix] × [模型矩陣ModelMatrix] × 頂點座標,其中投影矩陣和視圖矩陣能夠抽象爲3d場景中的相機屬性。

 

模型矩陣 × 頂點座標(相對模型) = 頂點世界座標(絕對座標)
視圖矩陣 × 世界座標 = 頂點相機座標(以相機爲座標原點)
投影矩陣 × 頂點相機座標 = 2d屏幕座標

相比通常WebGL場景,WebVR App不一樣之處在於:

  • WebVR須要進行雙屏渲染,經過分屏模擬人左右眼視野,所以在每一幀動畫渲染中,WebVR應用都要比普通WebGL應用多繪製一次;

左側視圖和右側視圖的相對變形,模擬左右眼的視覺差別,來產生3d效果

  • WebVR場景相機的方向、視野、位置(DOF)與用戶頭顯(HMD)緊密關聯。
    換句話說,當用戶的現實視角發生變化時,WebVR場景的相機也須要動態變化。

根據以上不一樣之處,我梳理了一個WebVR App的簡單開發流程,以下圖。

WebVR開發流程

開發流程總結爲: VR數據初始化 → WebGL初始化 → 動畫渲染。

1、VR數據初始化

使用navigator.getVRDisplay()方法獲取VR實例,該方法返回值是一個promise實例,經過.then(function(displays){})取得當前使用的VR實例列表。

let vrDisplay;
navigator.getVRDisplays().then(displays => {
  if (displays.length > 0) {
    vrDisplay = displays[0];
    console.log('Display found',vrDisplay);
    drawVRScene();
  } else {
    console.log('Display not found');
    // 非VR模式下渲染
    // drawScene();
  }
});

2、WebGL初始化

WebGL程序初始化通常分爲這幾個步驟:編譯頂點、片元着色器程序→建立頂點、紋理緩衝區→ 設置畫布被清空時顏色→啓動深度測試

function drawVRScene() {
  const canvas = document.getElementById('glcanvas');
  // 獲取WebGL上下文
  const gl = canvas.getContext('webgl');
  // WebGL初始化
  init(gl); 
  // WebGL渲染
  render(gl);
}
function init(gl) {
  // 預編譯着色器程序
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }
  // 建立頂點緩衝
  initVertexBuffers(gl);
  // 建立紋理緩衝
  initTextures(gl,'../assets/texture.jpg');
  gl.clearColor(0.4, 0.4, 0.4, 1.0);
  // 啓動深度測試
  gl.enable(gl.DEPTH_TEST);
  gl.depthFunc(gl.LEQUAL);
}

GLSL着色器程序

頂點着色器要作的工做是將Js輸入的頂點座標、模型-視圖-投影矩陣進行逐頂點運算。

const VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform mat4 u_MvpMatrix;
attribute vec2 a_TexCoord;
varying highp vec2 v_TexCoord;
void main() {
    gl_Position = u_MvpMatrix * a_Position;
    v_TexCoord = a_TexCoord;
}
`;

片元着色器主要處理片元顏色,在這裏只是將紋理座標和紋理對象傳給片元着色器。

const FSHADER_SOURCE = `
uniform sampler2D u_Sampler;
varying highp vec2 v_TexCoord;
void main() {
    gl_FragColor = texture2D(u_Sampler,v_TexCoord);
}
`;

WebVR前期初始化以後,咱們須要建立動畫來渲染VR場景。

3、動畫渲染

1. requestAnimationFrame建立動畫

經過使用vrDisplay實例的requestAnimationFrame(callback),遞歸執行callback函數。
該方法是window.requestAnimationFrame的一個特殊實現,它會優先使用VR設備原生的刷新率而不是瀏覽器的刷新率,以達到合適的渲染幀頻。

function render(gl,vrDisplay) {
    // 建立VR幀數據對象
    const frameData = new VRFrameData();
    const u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
    function animate() {
      // TODO
      draw(frameData,u_MvpMatrix);
      // 經過遞歸的方式,執行繪圖函數,產生動畫
      vrDisplay.requestAnimationFrame(animate);
    }
    animate();
}

咱們在啓動動畫遞歸以前使用new VRFrameData()方法,VRFrameData是WebVR提供的幀數據封裝對象,是WebVR渲染的關鍵數據,下文將會介紹如何使用它。

2. VR渲染

2.1 使用viewport設置雙視口

WebGL上下文提供了viewport函數,用來指定3d場景在canvas的繪製位置和尺寸。
默認的狀況下,WebGL渲染視口爲gl.viewport(0, 0, canvas.width, canvas.height)。
其中前兩個參數表明渲染的起點座標,後兩個參數表明渲染的尺寸,這裏經過依次設置左右眼渲染視口,來達到分屏效果。

function draw(frameData,u_MvpMatrix) {
  gl.viewport(0, 0, canvas.width * 0.5, canvas.height); // 設置左側視口
  // TODO
  gl.viewport(canvas.width * 0.5, 0, canvas.width * 0.5, canvas.height); // 設置右側視口
  // TODO
}

左、右側視口的渲染寬度爲canvas寬度的1/2,左視口起始點爲(0,0),右視口的起始點座標爲(canvas.width * 0.5, 0)。

2.2 使用VRFrameData動態渲染

前面介紹了WebVR渲染須要根據用戶行爲動態繪製每一幀場景,具體作法是:
1)經過WebVR API提供的VRFrameData實例獲取當前幀的視圖矩陣和投影矩陣;
2)將視圖-投影矩陣傳入着色器進行繪製;
3)生成下一幀數據並提交給當前canvas;
4)進入下一幀回調。
具體代碼以下

function draw(gl,frameData,u_MvpMatrix) {       
  const {
    leftProjectionMatrix,
    leftViewMatrix,
    rightProjectionMatrix,
    rightViewMatrix
  } = frameData; 
  // 初始化模型矩陣,模型-視圖-投影矩陣
  let modelMatrix = mat4.create(),
      vpMatrix = mat4.create(),
      mvpMatrix = mat4.create();

  // 將左眼視圖渲染到畫布的左側
  gl.viewport(0, 0, canvas.width * 0.5, canvas.height);
  // mvpMatrix = ProjectionMatrix × ViewMatrix × modelMatrix
  // 這裏使用gl-matrix.js的mat4對象對float32Array進行矩陣操做
  mat4.multiply(vpMatrix,leftProjectionMatrix,leftViewMatrix);
  mat4.multiply(mvpMatrix,vpMatrix,modelMatrix);
  // 將模型-視圖-投影矩陣mvpMatrix傳入着色器
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix);
  // 左側繪圖
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);

  // 將右眼視圖渲染到畫布的右側
  gl.viewport(canvas.width * 0.5, 0, canvas.width * 0.5, canvas.height);
  mat4.multiply(vpMatrix,rightProjectionMatrix,rightViewMatrix);
  mat4.multiply(mvpMatrix,vpMatrix,modelMatrix);
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix);
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);

  // 生成下一幀數據並覆蓋原來的frameData
  vrDisplay.getFrameData(frameData);
  vrDisplay.submitFrame();
}

首先,在動畫渲染前經過new VRFrameData()得到實例frameData,並傳入動畫渲染函數;
接着,在動畫函數裏獲取frameData的屬性:

固然VRFrameData還包括pose、orientation等屬性這裏就不一一列舉了。

根據公式分別計算出左右視口的模型-視圖-投影矩陣,傳給頂點着色器程序,與頂點緩衝區的頂點座標相乘繪製出最終頂點。

MvpMatrix = ProjectionMatrix × ViewMatrix × modelMatrix

最後,在每一幀動畫回調結束前,咱們調用vrDisplay.getFrameData(frameData)來生成下一幀數據並覆蓋frameData,並使用vrDisplay.submitFrame()將當前幀提交給當前畫布渲染。

至此,WebVR的開發流程已基本走完,具體代碼能夠參考下方demo

 

項目代碼:https://gitee.com/YorkChan/webvr-demo

結語:使用原生WebGL開發WebVR應用相比three.js或者aframe代碼要複雜不少,不過經過這種方式卻能更深刻的瞭解WebVR的工做原理。

想了解three.js開發webvr,可參考《VR大潮來襲---前端開發能作些什麼》
也歡迎各位關注個人專欄 WebVR技術莊園,不按期更新~

相關資料:
計算機圖形知識:矩陣變換
WebGL快速入門:WebGL 技術儲備指南
谷歌開發者 | WebVR:WebVR基本原理
MDN | WebVR API:使用WebVR API

相關文章
相關標籤/搜索