Table of Contents

Использование Mega plugin для реализации occlusion

Occlusion - ключевая технология для усиления immersive-интеграции virtual и real content в AR. Эта статья объясняет, как реализовать эффекты occlusion в environment xr-frame с помощью EasyAR cloud localization и annotations.

Перед началом

Как реализуется occlusion

  • Offline modeling: используйте Unity editor, чтобы в Block coordinate system создать geometry, соответствующую 1:1 реальным объектам, таким как стены, колонны и крупное оборудование; либо получите optimized model путем cropping и face reduction Block dense model.

  • Runtime alignment: во время runtime xr-frame выровняйте Block coordinate system с real space через cloud localization и загрузите соответствующую geometry.

  • Material replacement: назначьте этим geometry специальные occlusion materials.

  • Visual effect: когда GPU рендерит другие virtual objects, pixels в закрытых частях автоматически отбрасываются из-за неуспешного depth test, и virtual objects следуют логике occlusion реального physical space.

Как разместить occlusion с простой geometry

  1. Точно разместите box annotations, сверяясь с dense model и panorama. После размещения annotation выглядит как "wall" или "column".

    Annotation as occlusion

  2. Измените имя annotation, например на occlusion_wall, запишите ID и upload annotation.

  3. В xr-frame Mini Program используйте встроенную geometry, чтобы загрузить annotation, используемую как occlusion.

    В callback загрузки EMA используйте scene.createElement(xrFrameSystem.XRMesh,{}), чтобы создать simple geometry и назначить материал easyar-occlusion.

    Примечание

    Загрузка, registration, deregistration и unloading материала easyar-occlusion управляются 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 эта панда может танцевать за стеной.

Как разместить occlusion со сложной geometry

Это подходит для сценариев с высокоточной occlusion, например для оборудования сложной формы и нерегулярных зданий. Можно использовать файл маски из результатов mapping или экспортировать dense model Block через Mega Studio в Unity, затем обрезать и упростить его для получения white model occlusion.

Использование файла маски из результатов mapping

Скачайте файл маски glb из результата mapping через Mask file download в cloud localization database и обрежьте его. Обрезанную модель не нужно поворачивать вокруг оси Y при загрузке в xr-frame.

Примечание

Способ применим только к результатам mapping версии 9.6 и выше.

  1. После добавления Block в cloud localization database запишите соответствующий Block ID и нажмите Mask file download в столбце Action.

    Пункт скачивания файла маски

  2. В popup выберите Low precision и нажмите Download, чтобы скачать файл маски glb.

    Скачать файл маски

  3. Откройте скачанную модель в ПО для цифрового контента, например Blender.

    Файл маски до обрезки

    Обрежьте модель, оставив часть для occlusion, и сохраните как glb. Не меняйте исходные position, rotation, scale и систему координат.

    Файл маски после обрезки

  4. Подготовьте обрезанный файл glb. Вы можете загрузить файл на HTTPS hosting server, доступный Mini Program, и получить URL, либо поместить файл в каталог miniprogram/assets/ проекта Mini Program и напрямую использовать относительный путь. При использовании hosting server настройте соответствующий разрешенный домен загрузки в backend Mini Program и добавьте домен в whitelist Mini Program.

  5. Заполните адрес модели в конфигурации ресурса компонента sampleAssets.occlusionMesh.src. Если модель помещена в каталог assets/, используйте относительный путь, например:

    src: "assets/occlusion_mesh_sd.glb",
    

    Также можно указать HTTPS URL с hosting server, например https://your-domain.example/occlusion.glb.

    Компонент должен реализовать загрузку модели, mounting, назначение occlusion material и освобождение ресурсов. Смонтируйте модель под успешно локализованным узлом Block и сохраните исходные position, rotation и scale модели.

    После загрузки модели и успешной localization создайте XRNode под первым узлом Block, затем смонтируйте модель XRGLTF:

    showOcclusionMesh() {
        if (!scene) { console.error("Empty scene"); return; }
        const blockHolder = session?.blockHolder;
        if (!blockHolder) { console.warn("Session not initialized"); return; }
        if (occlusionMeshNode) { return; }
        const root = blockHolder.blocks[0]?.el;
        if (!root) { console.warn("Localization must succeed before showing the occlusion mesh"); return; }
        if (!scene.assets.getAsset("gltf", sampleAssets.occlusionMesh.assetId)) {
            console.warn("Occlusion mesh asset is not loaded");
            return;
        }
        const node = scene.createElement(xrFrameSystem.XRNode);
        root.addChild(node);
        const model = scene.createElement(xrFrameSystem.XRGLTF, {
            model: sampleAssets.occlusionMesh.assetId
        });
        node.addChild(model);
        node.getComponent(xrFrameSystem.Transform).visible = this.data.occlusionMeshVisible;
        occlusionMeshNode = node;
    },
    

    После успешной localization смонтируйте модель под узлом Block. Ручная настройка spatial position не требуется.

    Этот метод напрямую отображает загруженную исходную white model и сохраняет default position, rotation, scale и visible material модели. О resource loading и node mounting см. Как загрузить 3D content в AR scene во время xr-frame runtime.

  6. Запустите Mini Program в реальной сцене этого Block, после localization покажите файл маски и проверьте совпадение с реальностью и occlusion виртуальных объектов.

Использование dense model, экспортированной из Unity

  1. В Unity scene нажмите node Mega Block и запишите BlockID в панели Inspector.

    Record BlockID

  2. В Block Mega Studio выберите 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. Так как нам нужна только white model для occlusion, textures не нужны.

  4. Выполните crop и reduce экспортированной model в digital content creation software, например Blender, и сохраните как Glb.

    Совет

    В примере используется Blender Decimate Modifier.

    Before cropping

    После cropping и reduction:

    After cropping

  5. Поместите файл Glb, используемый для occlusion, в каталог miniprogram/assets/ проекта Mini Program и используйте относительный путь либо разместите его на доступном HTTPS-файловом сервере и используйте его URL. При использовании URL сервера настройте соответствующий разрешенный домен загрузки в backend Mini Program и добавьте домен в whitelist Mini Program.

  6. Загрузите GLTF, используемый как occlusion, в xr-frame Mini Program.

    Сначала load GLTF model для occlusion, затем используйте 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 });}, чтобы изменить material GLTF model.

    Примечание

    Загрузка, registration, deregistration и unloading материала easyar-occlusion управляются 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
        })
    }
    // Получить узел Block в сцене xr-frame
    let blockElement = blockHolder.getBlockById(blockParent.id).el;
    // Прикрепить обрезанную модель окклюзии к узлу Block как дочерний узел
    blockElement.addChild(model);
    /**
     * Из-за различий в поведении загрузчиков GLTF, чтобы ориентация модели в xr-frame полностью совпадала с результатом рендеринга Unity
    * Иногда после загрузки модель нужно повернуть на месте на 180 градусов вокруг оси Y
    */
    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 не требует annotation synchronization для spatial position. Это связано с тем, что в digital content creation software, например Blender, можно выполнять reduction и cropping модели без изменения coordinate system definition.
>
> Если нужно точно разместить собственную GLTF model occlusion, см. [Как разместить occlusion model, выровненную с пространством](./sample.md#wechat-mega-sample-precise-occulusion-model).

Финальный эффект работы на реальном устройстве показан в видео в начале статьи.

Ожидаемый эффект occlusion

Эффект occlusion в xr-frame Mini Program в основном зависит от следующего:

  • Точность localization tracking itself
  • Точность размещения model
  • Точность самой model, если это не simple geometry

Несовпадение на несколько сантиметров при localization drift является нормальным.

Слишком большое число faces у occlusion model может легко повлиять на performance. Рекомендуется использовать ее только в необходимых областях и по возможности использовать simple geometry как occlusion.

Следующие шаги

Связанные темы