var vrDisplay; var frameData=new VRFrameData(); function animate(timestamp) { vrDisplay.requestAnimationFrame(animate); if (vrDisplay.isPresenting) { vrDisplay.getFrameData(frameData); render(); } vrDisplay.submitFrame(); } navigator.getVRDisplays().then(function(displays) { for (var i=0;i<=displays.length-1;i++) { vrDisplay=displays[i]; init(); vrDisplay.requestAnimationFrame(animate); break; } }); document.getElementById("vr").addEventListener("click",function(event) { vrDisplay.requestPresent([{ source: canvas }]); },false);
var canvas=document.getElementById("canvas"); var gl=canvas.getContext("webgl2"); function init() { canvas.width =Math.max(vrDisplay.getEyeParameters("left").renderWidth ,vrDisplay.getEyeParameters("right").renderWidth )*2; canvas.height=Math.max(vrDisplay.getEyeParameters("left").renderHeight,vrDisplay.getEyeParameters("right").renderHeight); } function render() { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.viewport(0,0,canvas.width*0.5,canvas.height); // frameData.leftViewMatrix,frameData.leftProjectionMatrixで左目用を描画 gl.viewport(canvas.width*0.5,0,canvas.width*0.5,canvas.height); // frameData.rightViewMatrix,frameData.rightProjectionMatrixで右目用を描画 gl.flush(); }
var session; function animate(time,frame) { session.requestAnimationFrame(onAnimationFrame); render(time,frame); } document.getElementById("vr").addEventListener("click",function(event) { navigator.xr.requestSession("immersive-vr").then(function(xrSession) { session=xrSession; session.requestAnimationFrame(animate); }); },false);
var canvas=document.getElementById("canvas"); var gl=canvas.getContext("webgl2",{ xrCompatible: true }); var referenceSpace; function render(time,frame) { var webGLLayer=session.renderState.baseLayer; var pose=frame.getViewerPose(referenceSpace); gl.bindFramebuffer(gl.FRAMEBUFFER,webGLLayer.framebuffer); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); for (var i=0;i<=pose.views.length-1;i++) { var viewport=webGLLayer.getViewport(pose.views[i]); gl.viewport(viewport.x,viewport.y,viewport.width,viewport.height); // pose.views[i].transform.inverse.matrix (=viewMatrix), pose.views[i].projectionMatrix で片目用を描画 } gl.flush(); } document.getElementById("vr").addEventListener("click",function(event) { navigator.xr.requestSession("immersive-vr").then(function(xrSession) { session=xrSession; session.updateRenderState({ baseLayer: new XRWebGLLayer(session,gl) }); session.requestReferenceSpace("local").then((xrReferenceSpace) => { referenceSpace=xrReferenceSpace; session.requestAnimationFrame(animate); }); }); },false);
var gamepads=navigator.getGamepads(); for (var i=0;i<=gamepads.length-1;i++) { if (gamepads[i].id.match(/^Oculus Touch \([^\)]+\)$/)) { if (gamepads[i].hand=="right") { // gamepads[i].buttons[] などで右手の判定 } if (gamepads[i].hand=="left") { // gamepads[i].buttons[] などで左手の判定 } } }
for (var i=0;i<=session.inputSources.length-1;i++) { if (session.inputSources[i].profiles.indexOf("oculus-touch")!=-1) { if (session.inputSources[i].handedness=="right") { // session.inputSources[i].gamepad.buttons[] などで右手の判定 } if (session.inputSources[i].handedness=="left") { // session.inputSources[i].gamepad.buttons[] などで左手の判定 } } }
属性名 | データ内容 | |||
---|---|---|---|---|
Oculus Quest Oculus Touchコントローラー (右手側) |
Oculus Quest Oculus Touchコントローラー (左手側) |
Oculus Go コントローラー |
Gear VR | |
id | "Oculus Touch (Right)" | "Oculus Touch (Left)" | "Oculus Go Controller" | "Gear VR Touchpad" ※外部コントローラーの時は"Gear VR Controller" |
hand | "right" | "left" | "right" | "right" ? |
axes[0] | 右アナログスティックの倒し量 (左が-1.0、右が1.0) |
左アナログスティックの倒し量 (左が-1.0、右が1.0) |
Touchサーフェス (左スワイプが-1.0、右スワイプが1.0) |
Touchサーフェス (左スワイプが-1.0、右スワイプが1.0) |
axes[1] | 右アナログスティックの倒し量 (上が-1.0、下が1.0) |
左アナログスティックの倒し量 (上が-1.0、下が1.0) |
Touchサーフェス (上スワイプが-1.0、下スワイプが1.0) |
Touchサーフェス (上スワイプが-1.0、下スワイプが1.0) |
buttons[0] | 右アナログスティック押し込み | 左アナログスティック押し込み | Touchサーフェスのタッチ | タッチ |
buttons[1] | 右手側人差し指トリガー | 左手側人差し指トリガー | トリガーボタン | |
buttons[2] | 右手側中指トリガー | 左手側中指トリガー | ||
buttons[3] | Aボタン | Xボタン | ||
buttons[4] | Bボタン | Yボタン | ||
buttons[5] | Oculusボタン ? | メニューボタン ? | ||
pose.position | 右手の位置 | 左手の位置 | 手の位置 | 手の位置 |
pose.orientation | 右手の向き | 左手の向き | 手の向き | 手の向き |
pose.linearVelocity | 右手の速度 | 左手の速度 | 手の速度 | 手の速度 |
pose.angularVelocity | 右手の角速度 | 左手の角速度 | 手の角速度 | 手の角速度 |
pose.linearAcceleration | 右手の加速度 | 左手の加速度 | 手の加速度 | 手の加速度 |
pose.angularAcceleration | 右手の角加速度 | 左手の角加速度 | 手の角加速度 | 手の角加速度 |
hapticActuators[] (関数) | 右手側振動制御 | 左手側振動制御 |
var head=poseToMatrix(frameData.pose); // VRPoseを4×4行列に変換するposeToMatrix()はこちらを参照 var sittingToStanding=vrDisplay.stageParameters.sittingToStandingTransform; // sittingToStanding×headが頭の位置を表すモデル行列
var gamepads=navigator.getGamepads(); for (var i=0;i<=gamepads.length-1;i++) { if (gamepads[i]!=null && gamepads[i].id.match(/^Oculus Touch \([^\)]+\)$/)) { var hand=poseToMatrix(gamepads[i].pose); var sittingToStanding=vrDisplay.stageParameters.sittingToStandingTransform; if (gamepads[i].hand=="right") { // sittingToStanding×handが右手の位置を表すモデル行列 } if (gamepads[i].hand=="left") { // sittingToStanding×handが左手の位置を表すモデル行列 } } }
var sittingToStanding=vrDisplay.stageParameters.sittingToStandingTransform; var view=frameData.leftViewMatrix; // view×sittingToStanding-1を左目描画時のビュー行列に var view=frameData.rightViewMatrix; // view×sittingToStanding-1を右目描画時のビュー行列に
document.getElementById("vr").addEventListener("click",function(event) { navigator.xr.requestSession("immersive-vr",{ requiredFeatures: ["local-floor"] }).then(function(xrSession) { session=xrSession; session.updateRenderState({ baseLayer: new XRWebGLLayer(session,gl) }); session.requestReferenceSpace("local-floor").then((xrReferenceSpace) => { referenceSpace=xrReferenceSpace; session.requestAnimationFrame(animate); }); }); },false);
var head=frame.getViewerPose(referenceSpace).transform.matrix; // headが頭の位置を表すモデル行列
for (var i=0;i<=session.inputSources.length-1;i++) { if (session.inputSources[i].profiles.indexOf("oculus-touch")!=-1) { var hand=frame.getPose(session.inputSources[i].gripSpace,referenceSpace).transform.matrix; if (session.inputSources[i].handedness=="right") { // handが右手の位置を表すモデル行列 } if (session.inputSources[i].handedness=="left") { // handが左手の位置を表すモデル行列 } } }
vrDisplay.requestPresent([{ source: canvas, attributes: { highRefreshRate: true } }]);
vrDisplay.requestPresent([{ source: canvas, attributes: { foveationLevel: 3 } }]);
gl.getExtension("OCULUS_multiview"); // Multiview機能を有効化 var samples=2; // 2以上でアンチエイリアシング (最大値:gl.getParameter(gl.MAX_SAMPLES)) var defaultFramebuffer=gl.getParameter(gl.FRAMEBUFFER_BINDING); var multiviewFramebuffer=gl.createFramebuffer(); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER,multiviewFramebuffer); var multiviewColorTexture=gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D_ARRAY,multiviewColorTexture); gl.texStorage3D(gl.TEXTURE_2D_ARRAY,1,gl.RGBA8,canvas.width/2,canvas.height,2); if (samples>1) { multiview.framebufferTextureMultisampleMultiviewOVR(gl.DRAW_FRAMEBUFFER,gl.COLOR_ATTACHMENT0,multiviewColorTexture,0,samples,0,2); } else { multiview.framebufferTextureMultiviewOVR(gl.DRAW_FRAMEBUFFER,gl.COLOR_ATTACHMENT0,multiviewColorTexture,0,0,2); } var depthStencilTexture=gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D_ARRAY,depthStencilTexture); gl.texStorage3D(gl.TEXTURE_2D_ARRAY,1,gl.DEPTH32F_STENCIL8,canvas.width/2,canvas.height,2); if (samples>1) { multiview.framebufferTextureMultisampleMultiviewOVR(gl.DRAW_FRAMEBUFFER,gl.DEPTH_STENCIL_ATTACHMENT,depthStencilTexture,0,samples,0,2); } else { multiview.framebufferTextureMultiviewOVR(gl.DRAW_FRAMEBUFFER,gl.DEPTH_STENCIL_ATTACHMENT,depthStencilTexture,0,0,2); } function render() { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER,multiviewFramebuffer); gl.viewport(0,0,canvas.width/2,canvas.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram,"vpMatrixLeft"),false,vpMatrixLeft); // frameData.leftProjectionMatrix×frameData.leftViewMatrix gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram,"vpMatrixRight"),false,vpMatrixRight); // frameData.rightProjectionMatrix×frameData.rightViewMatrix // ここで描画 gl.invalidateFramebuffer(gl.DRAW_FRAMEBUFFER,[ gl.DEPTH_STENCIL_ATTACHMENT ]); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER,defaultFramebuffer); // multiviewColorTextureを画面全体に書き写し (こちらのblit()を参照) gl.flush(); }
<script type="x-shader/x-vertex"> #version 300 es #extension GL_OVR_multiview : require // Multiview機能用頂点シェーダー layout(num_views=2) in; // Multiview用指定 (2は同時に描画する数) in vec3 position; in vec4 color; in vec3 normal; uniform mat4 vpMatrixLeft; uniform mat4 vpMatrixRight; uniform mat4 mMatrix; out vec4 vColor; out vec3 vNormal; void main() { mat4 vpMatrix = gl_ViewID_OVR == 0u ? vpMatrixLeft : vpMatrixRight; // 左右のどちらの描画かによって振り分ける gl_Position=vpMatrix*mMatrix*vec4(position,1.0); vColor=color; vNormal=normalize(normal); } </script>
gl.getExtension("OVR_multiview"); // Multiview機能を有効化 vrDisplay.requestPresent([{ source: canvas, attributes: { multiview: true, depth: true } }]); // Multiview機能を要求 function render() { var views=vrDisplay.getViews(); // getViewsはWebVR 2.0の機能で、大半のブラウザでは未定義(バージョン6以降含む)なので注意 gl.enable(gl.SCISSOR_TEST); for (var i=0;i<=views.length-1;i++) { var viewport=views[i].getViewport(); gl.bindFramebuffer(gl.FRAMEBUFFER,views[i].framebuffer); gl.viewport(viewport.x,viewport.y,viewport.width,viewport.height); gl.scissor(viewport.x,viewport.y,viewport.width,viewport.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Multiview機能が動いているか views[i].getAttributes().multiview で要確認 gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram,"vpMatrixLeft"),false,vpMatrixLeft); // frameData.leftProjectionMatrix×frameData.leftViewMatrix gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram,"vpMatrixRight"),false,vpMatrixRight); // frameData.rightProjectionMatrix×frameData.rightViewMatrix // ここで描画 } gl.disable(gl.SCISSOR_TEST); gl.flush(); }
adb shell am start 開きたいURL
adb shell input text 打ち込みたい文字列
document.getElementById("vr").addEventListener("click",function(event) { navigator.xr.requestSession("inline").then(function(xrSession) { session=xrSession; session.updateRenderState({ baseLayer: new XRWebGLLayer(session,gl) }); session.requestReferenceSpace("viewer").then((xrReferenceSpace) => { referenceSpace=xrReferenceSpace; session.requestAnimationFrame(animate); }); }); },false);
機種名 | UserAgentに含まれる文字列 | VRDisplay.displayName |
---|---|---|
Oculus Quest | Quest Build | Oculus Quest |
Oculus Go | Pacific Build | Oculus Go |
Gear VR | SAMSUNG SM-G920F for a Samsung Galaxy S6など | ? |
戻る