Open6

WebXR(React-XR)に関するアレコレ

八葉ユーマ八葉ユーマ

React Three Fiberの関連ライブラリであるReact-XRを利用して制作物を作り始めたので、備忘録や知見、その他雑記等諸々書いていきます。

八葉ユーマ八葉ユーマ

そもそもなぜWebXR?

先日、AppleからVision ProというMRヘッドセットの発表がありました。
今後のVRMRコンテンツの発展を考える上でApple製MRデバイスは避けられない選択肢の一つと考えるべきです。
しかし、過去のAppleの動向から見るに、AppleがOpenXRのようなネイティブアプリケーション開発のための標準APIを実装しない可能性があり、そうなった場合には開発者は多かれ少なかれ、複数プラットフォームに対応するための作業にタスクを割かなくてはならなくなります。
それだけの価値のあるものならともかく、そこまでのタスクを割く労力が厳しいと感じたアプリケーションはAppleデバイスへの対応を諦めなくてはいけません。
まして、これまでのAppleデバイスのように開発にMacや高額なヘッドセットが必要になることを考えると、個人開発をやっているXR開発者には厳しいと感じることもあるでしょう…。

要は全部Appleのせいなのですが、そんなAppleにも実装を避けられないものがあります。
それがWebXRなのです。

WebXRはW3CというWebの標準規格を策定する団体が策定したものになりますが、W3Cの会員の中にはAppleも含まれており、実際にiOSのWebXRは機能こそしないものの、項目としては存在しており、いつになるかは不透明ですが、今後の対応の可能性があると思われます。
詳しくはこちらを参照ください。
https://zenn.dev/ikkou/articles/8ffc1c3ea92e9f#ios-16.1.1-における-webxr-device-api-の対応状況

つまり、WebXRならば、たとえそれが囲い込みの激しいAppleのデバイスであろうと、特別な対応なく使用できる可能性が高いということです。
なので、今後WebXRにも挑戦してみると、何かといいことがあるかもしれませんね。

八葉ユーマ八葉ユーマ

WebXR(React-XR)を触って感じた課題

これはReact-XRの問題なのかもしれないのですが、インタラクティブな要素が基本的にはコントローラーから伸びる光線での制御、もしくはハンドトラッキングによるオブジェクトに触れることでの制御がメインで、ネイティブアプリケーションによくあるGrabで物を掴むなどの動作を実装するのが大変な印象を受けました。
実際にはそういった問題も解決する方法があるのかもしれないのですが、この辺はReact-XRでの複雑なゲームなどのアプリケーションを作る上で障壁になりそうな感じはありました。

また、当たり前ですがアニメーションは基本的にアニメーションファイルを読み込むか、アニメーションライブラリ頼りなので、WebページライクなWebXRアプリケーションを作るのには苦労しないと思うのですが、Unityのような複雑なアニメーションを組み合わせて作るアプリケーションはかなりしんどいかもしれないですね。

八葉ユーマ八葉ユーマ

React-XRのXRビューはHTTPS接続でないと動かない

実際にアプリをGithub pagesやVerselにデプロイする上ではそこまで問題にならないかもしれないですが、開発を進める上でHTTPでのデバッグを行おうとすると「VR unsupported」と表示が出て動かない問題に直面するはずです。
理由はわかりませんが、XRビュー上ではHTTPSのみでしか動作しないことに注意してください。

八葉ユーマ八葉ユーマ

XRビューではrequestAnimationFrameが動作せず、多くのアニメーションライブラリでデフォルトの状態では動作しない

https://zenn.dev/habayuma/articles/1db1c56bf35d3c
こちらの記事にも記載させていただいたのですが、XRビューではrequestAnimationFrameが動かないことに注意してください。
useFrameなどを使用して手動更新をすることでこの問題は解決することが出来ます。

八葉ユーマ八葉ユーマ

React Three FiberとReact-XRのフックはJSXのCanvas、XR内で読み込ませないといけない

これはReact Three Fiberを使っている人なら当然の情報かもしれませんが、React Three FiberやReact-XRで使用するフックはJSX内のCanvas、XR内で読み込ませる必要があります。
その際に、新たに関数を作ってコンポーネントとして呼び出す必要があり、またコンポーネントなので返り値に何らかの要素がないといけません。

実装例はこちらです。

    function Frame(){//React Three FiberのフックであるuseFrameを呼び出すための関数
        const timeRef = useRef(0);
        useFrame((state, delta) => {
            timeRef.current += delta
            gsap.updateRoot(timeRef.current);
        })
        return(
            <></>{/*コンポーネントなので返り値が必要、空でもOK*/}
        )
    }
    function Interaction(){//React-XRのフックであるuseInteractionを呼び出すための関数
        useInteraction(box,"onSelect",(e:XRInteractionEvent)=>{
            console.log(e);
            select();
        })
        return(
            <></>
        )
    }
    return (
        <div>
            <XRButton mode="AR"/>
            <Canvas>
                <Frame/>{/*React Three Fiberで呼び出すフックを有したコンポーネントは<Canvas>下にないといけない*/}
                <XR>
                <Controllers />
                <Hands />
                <directionalLight/>
                <mesh
                ref={box}
                position={new THREE.Vector3(0,1,-2)}>
                    <boxGeometry 
                        args={[1,1,1]}/>
                    <meshBasicMaterial
                        color="blue" />
                </mesh>
                <mesh
                    ref={box2}
                    position={new THREE.Vector3(100,100,100)}>
                        <boxGeometry 
                            args={[1,1,1]}/>
                        <meshBasicMaterial
                            color="red" />
                </mesh>
                <Interaction/>{/*React-XRで呼び出すフックを有したコンポーネントは<XR>下にないといけない*/}
                </XR>
                ...