Table of Contents

Mega plugin を使用して occlusion を実装する

Occlusion は、AR における virtual と real content の没入的な融合を高める重要な技術です。この記事では、EasyAR cloud localization と annotations を通じて、xr-frame environment で occlusion 効果を実装する方法を案内します。

開始する前に

Occlusion の実装方式

  • Offline modeling: Unity editor を使用し、Block coordinate system 内で、壁、柱、大型設備など現実世界の実体に対して 1:1 で一致する geometry を作成します。または Block dense model を cropping および face reduction して optimized model を取得します。

  • Runtime alignment: xr-frame runtime で、cloud localization により Block coordinate system と real space を揃え、対応する geometry を読み込みます。

  • Material replacement: これらの geometry に special occlusion materials を割り当てます。

  • Visual effect: GPU が他の virtual objects を rendering するとき、occluded parts の pixels は depth test に通らないため自動的に除外され、virtual objects は現実の physical space の occlusion logic に従います。

Simple geometry の occlusion を配置する方法

  1. dense model と panorama を参照して box annotations を正確に配置します。配置後、annotation は "wall" または "column" のように見えます。

    Annotation as occlusion

  2. annotation の名前を occlusion_wall などに変更し、ID を記録して annotation を upload します。

  3. xr-frame Mini Program で、built-in geometry を使用して occlusion として使う annotation を読み込みます。

    EMA の loading callback で scene.createElement(xrFrameSystem.XRMesh,{}) を使用して simple geometry を作成し、easyar-occlusion material を割り当てます。

    注記

    easyar-occlusion material の loading、registration、deregistration、unloading は AR Session によって制御されます。

```ts
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>

> occlusion があると、このパンダは壁の後ろで踊ることができます。

Complex geometry の occlusion を配置する方法

異形設備や不規則な建築物など、高精度な occlusion が必要なシーンに適しています。マッピング結果のマスクファイルを使用するか、Unity の Mega Studio から Block dense model をエクスポートし、トリミングと面数削減を行って occlusion 用の白モデルを作成できます。

マッピング結果のマスクファイルを使用する

cloud localization database の Mask file download からマッピング結果の glb マスクファイルをダウンロードしてトリミングします。トリミング後のモデルは xr-frame で読み込むときに Y 軸回転を行う必要はありません。

注記

この方法は mapping version 9.6 以降のマッピング結果にのみ適用されます。

  1. Block を cloud localization database に追加した後、対応する Block ID を記録し、その Block 行の Action 列で Mask file download をクリックします。

    マスクファイルダウンロード入口

  2. ポップアップで Low precision を選択し、対応する Download ボタンをクリックして glb マスクファイルをダウンロードします。

    マスクファイルをダウンロード

  3. Blender などのデジタルコンテンツ作成ソフトでダウンロードしたモデルを開きます。

    マスクファイルのトリミング前

    モデルをトリミングし、occlusion に必要な部分だけを残して glb として保存します。元の position、rotation、scale、座標系は変更しません。

    マスクファイルのトリミング後

  4. トリミング後の glb ファイルを準備します。Mini Program からアクセスできる HTTPS サーバーにアップロードするか、miniprogram/assets/ に置いて相対パスを使用できます。

  5. コンポーネントの sampleAssets.occlusionMesh.src にモデルアドレスを入力します。

    src: "assets/occlusion_mesh_sd.glb",
    

    モデルの読み込みと localization が成功したら、Block ノードの下にモデルを配置します。空間位置を手動で設定する必要はありません。詳しくは xr-frame runtime で 3D コンテンツを読み込む方法 を参照してください。

  6. 対応する Block の実空間で Mini Program を実行し、localization 成功後にマスクファイルを表示して、実空間との一致と仮想オブジェクトの occlusion を確認します。

Unity からエクスポートした dense model を使用する

  1. Unity scene で Mega Block node をクリックし、Inspector panel で BlockID を記録します。

    Record BlockID

  2. Mega Studio の Block で export を選択します。

    Select export

  3. export options を変更して export します。

    Export options

    図中の 1 は LOD level です。level が低いほど model は単純になり、faces は少なくなります。最高の precision が必要な場合は 2 を選択し、precision 低下を許容して face count を減らせる場合は 1 または 0 を選択します。

    図中の 2 は texture export option です。occlusion には white model だけが必要で、textures は不要です。

  4. export した model を Blender などの digital content creation software で cropping と reduction し、Glb として保存します。

    ヒント

    例では Blender の Decimate Modifier を使用しています。

    Before cropping

    cropping と reduction 後:

    After cropping

  5. occlusion 用の Glb ファイルを file server に配置し、読み込み用の url を取得します。

  6. xr-frame Mini Program で occlusion として使う GLTF を読み込みます。

    まず occlusion 用の GLTF model を load し、その後 scene.createElement(xrFrameSystem.XRGLTF,options) を使用して GLTF model を作成します。

    assets.getAsset("material", "easyar-occlusion") を使用して material object を取得します。

    model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {m.setData({ neverCull: true, material: occlusionMaterial });} を使用して GLTF model の material を変更します。

    注記

    easyar-occlusion material の loading、registration、deregistration、unloading は AR Session によって制御されます。

```ts
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" //ここに Block ID を入力します
    if (!blockHolder.getBlockById(blockParent.id)) {
        // 既存の Block ノードがない場合は作成します
        blockHolder.holdBlock({
            id: blockID
        })
    }
    // xr-frame シーン内の Block ノードを取得します
    let blockElement = blockHolder.getBlockById(blockParent.id).el;
    // 切り抜いたオクルージョンモデルを Block ノードの子ノードとして追加します
    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]
> ここで Mega Block dense model を cropping して occlusion として使用する場合、spatial position を同期するための annotation は不要です。これは Blender などの digital content creation software で coordinate system definition を変えずに model を reduction および cropping できるためです。
>
> 自作の GLTF model occlusion を正確に配置する必要がある場合は、[空間に整合した occlusion model を配置する方法](./sample.md#wechat-mega-sample-precise-occulusion-model)を参照してください。

最終的な実機実行効果は、記事冒頭の動画を参照してください。

期待される occlusion 効果

xr-frame Mini Program 上の occlusion 効果は、主に次の点に影響されます。

  • localization tracking 自体の精度
  • model 配置の正確さ
  • simple geometry でない場合、model 自体の精度

localization drift 時に数センチの misalignment が発生するのは正常です。

occlusion 用 model の faces が多すぎると performance に影響しやすくなります。必要な領域でのみ使用し、可能な限り simple geometry を occlusion として使用することを推奨します。

次のステップ

関連トピック