ことの発端
普段はThree.jsでWebVRコンテンツをつくっているので、Three.jsのVRButton.jsを使っています。
しかし、以前、gamepadにアクセスしようとした際、sessionが隠蔽されており、外部からスマートにアクセスする方法が見つからず、なかなか苦労した経験がありました。
そんな折、Immersive Webのサンプルをみてみると、ものすごく簡単に実装されているようで、VRモードに切り替えるボタンのみ、Immersive Webのwebxr-button.jsに切り替えようと思った次第です。
実装
webxr-button.jsをTypeScriptに対応させるために型を定義しまくるのが非常に難儀だったので、JavaScriptのままjsxで実装しました。
ブラウザが WebXR Device API に対応している場合のみ、ボタンを表示するようにしています。
WebXrButton.jsx
import styled from 'styled-components'; import { useEffect, useRef, useState } from 'react'; import { WebXRButton } from '~/scripts/immersive/webxr-button'; const Wrapper = styled.span` position: relative; &[data-renderer='false'] { .webvr-ui-title { display: none !important; } .webvr-ui-svg { display: none; } } &[data-renderer='true'] { .webvr-ui-svg-error { display: none; } } button { display: flex; flex-direction: row-reverse; align-items: center; justify-content: center; width: 176px; height: 56px; background: #FAFAFA; cursor: pointer; } .webvr-ui-title { margin-left: 8px; } `; export default function WebXrButton({ renderer }) { const wrapperRef = useRef(null); const [ xrButton, setXrButton ] = useState(null); const [ supportedXr, setSupportedXr ] = useState(false); useEffect(() => { if (!!renderer) { setXrButton(new WebXRButton({ color: '#212121', injectCSS: false, onRequestSession: onRequestSession, onEndSession: onEndSession })); if (navigator.xr) { (async () => { const supported = await navigator.xr.isSessionSupported('immersive-vr'); setSupportedXr(supported); })(); } } }, [renderer]); useEffect(() => { if (!xrButton) { return; } xrButton.enabled = supportedXr; if (xrButton.enabled && wrapperRef.current) { wrapperRef.current.innerHTML = ''; wrapperRef.current.appendChild(xrButton.domElement); } }, [xrButton, supportedXr, wrapperRef]); function onRequestSession() { if (!renderer) { return; } const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor', 'hand-tracking' ] }; navigator.xr.requestSession('immersive-vr', sessionInit).then(onSessionStarted); } function onSessionStarted(session) { renderer.xr.setSession(session); } function onEndSession(session) { session.end(); } return ( <Wrapper ref={ wrapperRef } data-renderer={ !!renderer } className="web-xr-button" ></Wrapper> ); }
実装のポイントとしては、
Three.jsのrendererを、
renderer.xr.setSession(session);
と、xr.setSessionにsessionを渡してあげるだけでOKです。
ただし、Three.js側で、
renderer.xr.enabled = true;
と。XRを有効にしておく必要があります。