时间:2023-06-22 08:48:02 | 来源:网站运营
时间:2023-06-22 08:48:02 来源:网站运营
WebVR开发教程——深度剖析:最近WebVR API 1.1已经发布,2.0草案也在拟定中,在我看来,WebVR走向大众浏览器是早晚的事情了,今天本人将对WebVR开发环境和开发流程进行深入介绍。模型矩阵 × 顶点坐标(相对模型) = 顶点世界坐标(绝对坐标)相比一般WebGL场景,WebVR App不同之处在于:
视图矩阵 × 世界坐标 = 顶点相机坐标(以相机为坐标原点)
投影矩阵 × 顶点相机坐标 = 2d屏幕坐标
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(); }});
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);}
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场景。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渲染的关键数据,下文将会介绍如何使用它。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)。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,并传入动画渲染函数;MvpMatrix = ProjectionMatrix × ViewMatrix × modelMatrix最后,在每一帧动画回调结束前,我们调用vrDisplay.getFrameData(frameData)来生成下一帧数据并覆盖frameData,并使用vrDisplay.submitFrame()将当前帧提交给当前画布渲染。
关键词:深度,剖析,教程