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 и систему координат.

    Если файл маски одного Block содержит несколько файлов GLB, их можно объединить в программе для создания цифрового контента либо обрезать и экспортировать по отдельности. Во время выполнения прикрепите все к одному узлу Block.

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

  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/ используйте относительный путь. Для одного GLB достаточно одной записи; для нескольких частей добавьте запись для каждой загружаемой части с уникальным assetId и соответствующим src. Например:

    const occlusionMeshAssets = [
        // Один файл GLB
        {
            assetId: "occlusionMesh00",
            type: "gltf",
            src: "assets/Low_Poly_occlusion.glb",
            options: {}
        },
        // Несколько файлов GLB
        {
            assetId: "occlusionMesh01",
            type: "gltf",
            src: "assets/Low_Poly_occlusion_part1.glb",
            options: {}
        },
        {
            assetId: "occlusionMesh02",
            type: "gltf",
            src: "assets/Low_Poly_occlusion_part2.glb",
            options: {}
        }
    ];
    

    Используйте фактическое имя загруженного или обрезанного файла. В src также можно указать HTTPS URL сервера, например https://your-domain.example/occlusion.glb.

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

  6. Загрузите GLTF для окклюзии в мини-программе xr-frame.

    После загрузки моделей и хотя бы одной успешной локализации создайте общий XRNode под первым узлом Block. Для каждой загруженной части создайте XRGLTF и замените материал её сетки на easyar-occlusion из плагина Mega. Приведённый ниже полный метод объединяет прикрепление моделей и замену материала; им можно заменить showOcclusionMesh() в примере:

    showOcclusionMesh() {
        if (!scene) { console.error("Empty scene"); return; }
        const blockHolder = session?.blockHolder;
        if (!blockHolder) { console.warn("Empty block holder. Session not initialized."); return; }
        if (occlusionMeshNode) { return; }
        const root = blockHolder.blocks[0]?.el;
        if (!root) { console.warn("Empty block: localization must succeed at least once before showing the occlusion mesh"); return; }
        const loadedMeshes = occlusionMeshAssets.filter(meshAsset =>
            scene.assets.getAsset('gltf', meshAsset.assetId));
        if (loadedMeshes.length === 0) {
            console.warn("Occlusion mesh asset is not loaded");
            return;
        }
        const occlusionMaterial = scene.assets.getAsset("material", "easyar-occlusion");
        if (!occlusionMaterial) {
            console.warn("Occlusion material is not loaded");
            return;
        }
        const node = scene.createElement(xrFrameSystem.XRNode);
        root.addChild(node);
        for (const meshAsset of loadedMeshes) {
            const occlusionMesh = scene.createElement(xrFrameSystem.XRGLTF, {
                model: meshAsset.assetId
            });
            node.addChild(occlusionMesh);
            // Прикрепите узел и настройте Transform перед заменой материала окклюзии.
            // Файл маски из результата построения карты сохраняет исходные координаты; поворот вокруг оси Y не требуется.
            occlusionMesh.getComponent(xrFrameSystem.GLTF).meshes.forEach((mesh: any) => {
                mesh.setData({ neverCull: true, material: occlusionMaterial });
            });
        }
        node.getComponent(xrFrameSystem.Transform).visible = this.data.occlusionMeshVisible;
        occlusionMeshNode = node;
    },
    

    Все части используют общий родительский узел occlusionMeshNode. Его Transform.visible управляет их одновременным показом и скрытием без отдельной настройки положения.

    Примечание

    AR Session управляет загрузкой, регистрацией, отменой регистрации и выгрузкой easyar-occlusion. Получайте и используйте материал после запуска Session.

  7. Запустите мини-программу на реальной площадке соответствующего Block. Дождитесь загрузки ресурсов и успешной локализации, включите переключатель Файл маски и проверьте, скрываются ли виртуальные объекты сохранёнными частями модели.

Использование 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.

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

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