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など | ? |
戻る