Table of Contents

Using mega plugin to achieve occlusion

オクルージョン(Occlusion)は、ARの実世界と仮想世界の融合における没入感を向上させる重要な技術です。本稿では、xr-frame環境下でEasyARクラウドローカライゼーションとアノテーションを活用してオクルージョンを実現する方法を解説します。

開始前に

オクルージョンの実現方法

  • オフラインモデリング:UnityエディタでBlock座標系を用い、実世界の物体(壁・柱・大型設備など)に正確に一致するジオメトリを作成。またはBlock高密度モデルをトリミング・ポリゴン削減して最適化したモデルを生成。
  • ランタイム位置合わせ:xr-frame実行時にクラウドローカライゼーションでBlock座標系と実空間を位置合わせし、対応するジオメトリをロード。
  • マテリアル割り当て:これらのジオメトリに特殊なオクルージョン用マテリアルを適用。
  • 視覚効果:GPUが他の仮想物体をレンダリングする際、深度テスト未通過部分のピクセルが自動的に除外され、仮想物体が実世界物理空間の遮蔽ルールに従う。

単純なジオメトリによるオクルージョンの配置

  1. 高密度モデルとパノラマ画像を参照し、立方体アノテーションを壁や柱のように正確に配置。

    アノテーションをオクルージョンとして使用

  2. アノテーション名を変更(例: occlusion_wall)し、IDを記録後アップロード。

  3. xr-frameアプリで組み込みジオメトリとしてオクルージョン用アノテーションをロード。

    EMAロードコールバック内で scene.createElement(xrFrameSystem.XRMesh,{}) を使用し、easyar-occulusion マテリアルを割り当てた単純ジオメトリを作成。

    注記

    easyar-occulusion マテリアルのロード、登録、登録解除、アンロードはAR Sessionが制御。

handleEmaResult(ema: easyar.ema.v0_5.Ema) {
    let blockHolder: easyar.BlockHolder = session.blockHolder;
    ema.blocks.forEach(emaBlock => {
        const blockInfo: easyar.BlockInfo = {
            id: emaBlock.id
        };
        // Blockノードが存在しない場合、Blockノードを作成する
        blockHolder.holdBlock(blockInfo, easyarPlugin.toXRFrame(emaBlock.transform));
    });
    ema.annotations.forEach(annotation => {
        if (annotation.type != mega.EmaV05AnnotationType.Node) {
            return;
        }
        const nodeAnnotation = annotation as easyar.ema.v0_5.Node;
        const xrNode: xrfs.XRNode = easyarPlugin.createXRNodeFromNodeAnnotation(nodeAnnotation, blockHolder);
        const emaName: string = nodeAnnotation.name;
        const geometryStr: string = nodeAnnotation.geometry === "cube" ? "cube" : "sphere";
        const assetInfo = AnnotationMetaData[nodeAnnotation.id as keyof typeof AnnotationMetaData];
        let model: xrfs.Element;

        if (assetInfo) {
            // GLTF部分
        } else {
            model = scene.createElement(
                xrFrameSystem.XRMesh,
                {
                    // プラグインで登録されたオクルージョン材質を使用する
                    material: "easyar-occlusion",
                    // xr-frame組み込みのジオメトリを使用し、ここでは直接"cube"を使用することもできる
                    geometry: geometryStr,
                    name: emaName,
                    "receive-shadow": "false",
                    "cast-shadow": "false"
                    // Scaleを変更しないように注意する
                }
            );
            xrNode.addChild(model);
        }
    })
}
<video src="https://doc-asset.easyar.com/develop/wechat/mega/media/occlusion03.mp4" style="width:480px; max-width:100%; height:auto;" muted playsinline controls></video>

> オクルージョンにより、パンダが壁の後ろで踊る様子を表現可能。

複雑なジオメトリによるオクルージョンの配置

異形設備や不規則な建造物など、高精度オクルージョンが必要なシーンに適用。

Block高密度モデルをトリミング・ポリゴン削減し、オクルージョン用の簡素モデル(白モデル)を生成可能。

  1. Unityシーンで Mega Block ノードを選択し、Inspector パネルでBlockIDを記録。

    BlockIDの記録

  2. Mega Studioの Block からエクスポートを選択。

    エクスポート選択

  3. エクスポートオプションを変更後エクスポート。

    エクスポートオプション

    図中1はLODレベル。レベルが低いほどモデルは簡素化されポリゴン数が減少。最高精度が必要な場合は2、精度低下許容でポリゴン削減する場合は1または0を選択。

    図中2はテクスチャエクスポートオプション。オクルージョン用白モデルには不要。

  4. エクスポートしたモデルをBlender等のDCCツールでトリミング・ポリゴン削減後、Glb形式で保存。

    ヒント

    例ではBlenderのDecimate Modifierを使用。

    トリミング前

    トリミング・ポリゴン削減後:

    トリミング後

  5. オクルージョン用Glbファイルをファイルサーバーに配置し、ロード用URLを取得。

  6. xr-frameアプリでオクルージョン用GLTFをロード。

    まずオクルージョン用GLTFモデルをロード。次に scene.createElement(xrFrameSystem.XRGLTF,options) でGLTFモデルを作成。

    assets.getAsset("material", "easyar-occlusion") でマテリアルオブジェクトを取得。

    model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {m.setData({ neverCull: true, material: occlusionMaterial });} でGLTFモデルのマテリアルを変更。

    注記

    easyar-occulusion マテリアルのロード、登録、登録解除、アンロードはAR Sessionが制御。

const sampleAssets = {
    occlusion1: {
        assetId: "occlusion1",
        type: "gltf",
        src: "url/occlusion1.glb",
        options: {}
    }
}
async loadAsset() {
    if (!scene) {console.error("Empty scene"); return;}
    try {
        await scene.assets.loadAsset(sampleAssets.occlusion1);
    } catch (err) {
        console.error(`Failed to load assets: ${err.message}`);
    }
},
addOcclusion() {
    model = scene.createElement(
        xrFrameSystem.XRGLTF,
        {
            "model": assetInfo.assetId,
            "anim-autoplay": assetInfo.animation ? assetInfo.animation : "",
            "scale": assetInfo.scale ? assetInfo.scale : "1 1 1",
            name: "tree"
        }
    );
    const blockID = "aaaa1234-bbbb-cccc-dddd-eeeeee123456" //ここにブロックIDを記入する必要がある
    if (!blockHolder.getBlockById(blockParent.id)) {
        // 存在するブロックノードがない場合、新規作成する
        blockHolder.holdBlock({
            id: blockID
        })
    }
    // xr-frameシーン内のブロックノードを取得
    let blockElement = blockHolder.getBlockById(blockParent.id).el;
    // クリッピングされたオクルージョンモデルをブロックノードの子として追加
    blockElement.addChild(model);
    /**
     * GLTFローダーの挙動の違いにより、モデルをxr-frame上でUnityのレンダリング結果と完全に一致させるためには、
    * ロード後のモデルをY軸周りに180度回転させる必要がある場合がある
    */
    let modelTransform = model.getComponent(xrFrameSystem.Transform);
    let currentRotation = modelTransform.quaternion.clone();
    let targetRotation = currentRotation.multiply(new xrFrameSystem.Quaternion().setValue(0, 1, 0, 0));
    modelTransform.quaternion.set(targetRotation);
    //Transformの変更後にマテリアルを変更する必要があることに注意
    if (assetInfo.assetId == 'occlusion1') {
        //megaプラグインが提供するオクルージョンマテリアルを取得
        let occlusionMaterial = scene.assets.getAsset("material", "easyar-occlusion");
        //オクルージョンマテリアルを適用
        model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {
            m.setData({ neverCull: true, material: occlusionMaterial });
        });
    }
}
> [!NOTE]
> Block高密度モデルをトリミングしたオクルージョンでは、アノテーションによる空間位置同期が不要。Blender等のDCCツールでは座標系定義を変更せずにモデルのポリゴン削減・トリミングが可能。
>
> 自作GLTFモデルを正確に配置する場合は[空間位置合わせされたオクルージョンモデルの配置方法](./sample.md#wechat-mega-sample-precise-occulusion-model)を参照。

実機実行結果は冒頭動画を参照。

オクルージョンの効果予測

xr-frameアプリにおけるオクルージョン効果は以下に影響される:

  • ローカライゼーション・トラッキング自体の精度
  • モデル配置の正確さ
  • モデル自体の精度(単純ジオメトリでない場合)

ローカライゼーションのドリフト時に数センチの位置ずれが発生するのは正常。

オクルージョンモデルのポリゴン数過多はパフォーマンス低下を招くため、必要な領域のみに適用し、可能な限り単純なジオメトリを使用推奨。

次のステップ

関連トピック