【three.js】8thwall Lightship VPSをthree.jsで試してみる
はじめに
VPSとは「Visual Positioning System」の略であり、簡単に言うと現実のオブジェクトを利用したリッチなAR演出ができる技術です。
今年になってさまざまな会社が立て続けにVPSをリリースしていますが、2022年10月現在でブラウザで利用できるVPSは8thwallが提供するLightship VPSのみになります。
VPSについて、また8thwallが提供するVPSについての詳細は下記記事がわかりやすかったのでぜひご確認を!
また、8thwall Lightshipの一連の実装手順については、丁寧にまとめてくださっている記事が下記にあります。
ここまでほぼ他力本願ですが、次から本記事の本題です。
three.jsのサンプルが無い!
8thwallさんは結構親切で、コードサンプルも結構揃っているのですが、今日現在でA-FRAMEを利用したものしかなく、three.jsを利用するにはdocumentationを紐解かなければなりません。
そんな訳で、私がthree.jsで試してみましたのでコードを共有します。
ぜひ参考にしてください!
ソースコード
前提
- LiDARでスキャンされたモデルを、現実のオブジェクトに重ねる単純なVPSです。
- self hosting(8thwallのサーバーではなくローカル環境や独自サーバーで動かしたい時)です
- three.js v142
code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>8thwall VPS three.js example</title>
<script src="//unpkg.com/three@0.142.0/build/three.min.js"></script>
<script src="//unpkg.com/three@0.142.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="//unpkg.com/three@0.142.0/examples/js/loaders/DRACOLoader.js"></script>
<script src="//cdn.8thwall.com/web/xrextras/xrextras.js"></script>
<script src="//cdn.8thwall.com/web/coaching-overlay/coaching-overlay.js"></script>
<script src="//apps.8thwall.com/xrweb?appKey=xxxxxxxxx"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
let scanModel = null;
const rootGroup = new THREE.Group();
rootGroup.visible = false;
function modelLoad() {
//scanしたモデルをロードする
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath('//unpkg.com/browse/three@0.142.0/examples/js/libs/draco/');
dracoLoader.setDecoderConfig({ type: 'js' });
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load("./model/scan.glb", (model) => {
scanModel = model;
rootGroup.add(scanModel.scene);
});
}
modelLoad();
window.XR8 ? onLoad() : window.addEventListener('xrloaded', onLoad);
function onLoad() {
XRExtras.Loading.showLoading();//8thwallが用意しているローディング画面を表示d
XRLoaded();//と同時に8thwall起動の用意
}
function XRLoaded() {
const vpsPipeline = VPSPipelineModule();
XR8.XrController.configure({
enableVps: true//VPSを有効にする
});
XR8.addCameraPipelineModules([
// Add camera pipeline modules.
XR8.GlTextureRenderer.pipelineModule(), // Draws the camera feed.
XR8.Threejs.pipelineModule(), // Creates a ThreeJS AR Scene.
XR8.XrController.pipelineModule(), // Enables SLAM tracking.
XRExtras.AlmostThere.pipelineModule(), // Detects unsupported browsers and gives hints.
XRExtras.FullWindowCanvas.pipelineModule(), // Modifies the canvas to fill the window.
XRExtras.Loading.pipelineModule(), // Manages the loading screen on startup.
XRExtras.RuntimeError.pipelineModule(), // Shows an error image on runtime error.
VpsCoachingOverlay.pipelineModule(),// 体験の最初のチュートリアル的な表示
vpsPipeline,// Custom pipeline modules.
]);
XR8.run({canvas: document.getElementById("canvas")});
}
function VPSPipelineModule() {
return {
// Camera pipeline modules need a name. It can be whatever you want but must be
// unique within your app.
name: 'vps-example',
// onStart is called once when the camera feed begins. In this case, we need to wait for the
// XR8.Threejs scene to be ready before we can access it to add content. It was created in
// XR8.Threejs.pipelineModule()'s onStart method.
onStart: onXRCameraStart,
// onUpdate is called once per camera loop prior to render. Any 3js geometry scene would
// typically happen here.
onUpdate: onXRCameraRender,
// Listeners are called right after the processing stage that fired them. This guarantees that
// updates can be applied at an appropriate synchronized point in the rendering cycle.
listeners: [
{event: 'reality.projectwayspotfound', process: spotFound},//VPSスポットが検出された時
{event: 'reality.projectwayspotupdated', process: spotUpdate},//VPSスポットが検出中
{event: 'reality.projectwayspotlost', process: spotLost},//VPSスポットが検出できなくなった時
],
}
}
function onXRCameraStart({canvas}) {
//ARカメラスタート
const {scene, camera} = XR8.Threejs.xrScene();// Get the 3js scene from XR
initXrScene({scene, camera});//sceneとcamera渡して、three.js関連の記述をこの関数に
// prevent scroll/pinch gestures on canvas
canvas.addEventListener('touchmove', (event) => {
event.preventDefault()
})
// Sync the xr controller's 6DoF position and camera paremeters with our scene.
XR8.XrController.updateCameraProjectionMatrix(
{origin: camera.position, facing: camera.quaternion}
)
// Recenter content when the canvas is tapped.
canvas.addEventListener(
'touchstart', (e) => {
e.touches.length === 1 && XR8.XrController.recenter()
}, true
)
}
function onXRCameraRender() {
//ARカメラ動作中の処理
//今回のサンプルでは特に何もしない
const {scene, camera, renderer, cameraTexture} = XR8.Threejs.xrScene();
}
function initXrScene({scene, camera}) {
scene.add(rootGroup);
camera.position.set(0, 3, 0);
}
function spotFound({detail}) {
rootGroup.visible = true;
rootGroup.position.copy(detail.position);
rootGroup.quaternion.copy(detail.rotation);
}
function spotUpdate({detail}) {
rootGroup.position.copy(detail.position);
rootGroup.quaternion.copy(detail.rotation);
}
function spotLost() {
rootGroup.visible = false;
}
</script>
</body>
</html>
実装したものがこちら。
self hostingの場合は、localで開発するときもhttps環境が必要です。
また、8thwallの管理画面でドメイン登録を忘れないように!
(おまけ)試してみてわかった8thwall Lightship VPSの長所・短所
VPSの大きな特徴として、「位置情報+カメラからの画像情報」で対象のオブジェクトを検知しているところがあります。
これを理解することによって、8thwall Lightship VPSの長所と短所が見えてきます。
まず、8thwall Lightship VPSはスポットをLiDARでスキャンします。
例えばハチ公はこんな感じでスキャンされてますね。
逆に言うと、LiDARでスキャンできる範囲でしか登録できないということになります。
例えば、大きなビルをスポットにしたい、という演出には不向きでしょう。
公式も10m以上のカメラに収まらない範囲は避けた方がよいと言っています。
逆に、スケールの小さいスポットは8thwallに向いています。
しかも屋内であっても問題ありません。階数も問いません。
「とあるマンションのどっかの部屋の3Fにある置物」でも8thwallは威力を発揮すると思います。
役に、8thwallの短所がそのまま長所になっているのがGoogleのGeospatial APIです。
Googleの方はストリートビューの画像をリソースとしているので、大きい建物に強いです。
以下のように、渋谷の街並みをVPSしたい、という需要に向いています。
ただし、Geospatial APIはブラウザでは使えません。。
まとめると以下になります。できること、できないことを頭に入れつつ、いいコンテンツを作れるようになりたいですね!
-
8thwall Lightship VPSの長所
- 屋内でも使える
- 階数も問わない
- 小規模のオブジェクトに対して精度が高い
-
8thwall Lightship VPSの短所
- LiDARでスキャンできないくらいの大きい建物、街並みには向いてない
Discussion