Table of Contents

Usar el Mega plugin para implementar occlusion

Occlusion es una tecnología clave para mejorar la integración inmersiva de virtual y real content en AR. Este artículo le guía para implementar efectos de occlusion en el environment xr-frame mediante EasyAR cloud localization y annotations.

Antes de empezar

Cómo se implementa occlusion

  • Offline modeling: use el Unity editor para crear geometry con correspondencia 1:1 en el Block coordinate system para entidades del mundo real como walls, columns y large equipment; o bien obtenga un optimized model mediante cropping y face reduction del Block dense model.

  • Runtime alignment: en runtime de xr-frame, alinee el Block coordinate system con el real space mediante cloud localization y cargue la geometry correspondiente.

  • Material replacement: asigne occlusion materials especiales a estas geometries.

  • Visual effect: cuando la GPU renderiza otros virtual objects, los pixels de las partes ocluidas se descartan automáticamente al fallar el depth test, haciendo que los virtual objects sigan la lógica de occlusion del physical space real.

Cómo disponer occlusion con simple geometry

  1. Coloque box annotations con precisión comparándolas con el dense model y el panorama. Tras la colocación, la annotation parece una "wall" o un "column".

    Annotation as occlusion

  2. Modifique el nombre de la annotation, por ejemplo occlusion_wall, registre el ID y suba la annotation.

  3. En el xr-frame Mini Program, use su built-in geometry para cargar la annotation usada como occlusion.

    En el callback de carga de EMA, use scene.createElement(xrFrameSystem.XRMesh,{}) para crear simple geometry y asignar el material easyar-occlusion.

    Nota

    La carga, registration, deregistration y unloading del material easyar-occlusion están controlados por 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>

> Con occlusion, este panda puede bailar detrás de la wall.

Cómo disponer occlusion con complex geometry

Esto se aplica a escenarios que requieren oclusión de alta precisión, como equipos de forma irregular o edificios irregulares. Puede usar el archivo de máscara de los resultados de mapeo, o exportar el modelo denso del Block mediante Mega Studio en Unity, recortarlo y reducirlo para obtener un modelo blanco de oclusión.

Usar el archivo de máscara de los resultados de mapeo

Descargue el archivo de máscara glb del resultado de mapeo mediante Mask file download en la base de datos de localización en la nube y recórtelo. El modelo recortado no necesita rotarse alrededor del eje Y al cargarse en xr-frame.

Nota

Este método solo se aplica a resultados de mapeo de la versión 9.6 o posterior.

  1. Después de agregar el Block a la base de datos, registre el Block ID correspondiente y haga clic en Mask file download en la columna Action.

    Entrada de descarga del archivo de máscara

  2. En la ventana emergente, seleccione Low precision y haga clic en Download para descargar el archivo glb.

    Descargar archivo de máscara

  3. Abra el modelo descargado en software de creación digital, como Blender.

    Archivo de máscara antes de recortar

    Recorte el modelo, conserve solo la parte necesaria para oclusión y guárdelo como glb. Mantenga posición, rotación, escala y sistema de coordenadas originales.

    Si la máscara de un Block contiene varios archivos GLB, puedes combinarlos en un programa de creación de contenido digital o recortarlos y exportarlos por separado. En tiempo de ejecución, agrégalos todos al mismo nodo Block.

    Archivo de máscara después de recortar

  4. Prepare el archivo glb recortado. Puede subir el archivo a un servidor de hosting HTTPS accesible por el Mini Program y obtener la URL, o poner el archivo en el directorio miniprogram/assets/ del proyecto Mini Program y usar directamente una ruta relativa. Al usar un servidor de hosting, configure el dominio de descarga legal correspondiente en el backend del Mini Program y añada el dominio a la whitelist del Mini Program.

  5. Indica la dirección del modelo en sampleAssets.occlusionMesh.src, dentro de la configuración de recursos del componente. Usa una ruta relativa para los modelos en assets/. Basta una entrada para un solo GLB; si hay varias partes, agrega una entrada por cada una, con un assetId único y su src. Por ejemplo:

    const occlusionMeshAssets = [
        // Un archivo GLB
        {
            assetId: "occlusionMesh00",
            type: "gltf",
            src: "assets/Low_Poly_occlusion.glb",
            options: {}
        },
        // Varios archivos 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: {}
        }
    ];
    

    Usa el nombre real del archivo descargado o recortado. src también puede contener una URL HTTPS del servidor, como https://your-domain.example/occlusion.glb.

    El componente debe implementar la carga del modelo, el mounting, la asignación del material de occlusion y la liberación de recursos. Monte el modelo bajo el nodo Block localizado correctamente y mantenga la position, rotation y scale originales del modelo.

  6. Carga el GLTF de oclusión en el Mini Program xr-frame.

    Cuando los modelos estén cargados y la localización haya tenido éxito al menos una vez, crea un XRNode compartido bajo el primer nodo Block. Crea un XRGLTF por cada parte cargada y sustituye el material de su malla por easyar-occlusion del complemento Mega. El método completo siguiente combina la asociación del modelo y la sustitución del material; puede sustituir showOcclusionMesh() en el ejemplo:

    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);
            // Asocia el nodo y configura Transform antes de sustituir el material de oclusión.
            // La máscara del resultado del mapeo conserva sus coordenadas originales; no requiere rotación sobre el eje 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;
    },
    

    Todas las partes comparten el nodo principal occlusionMeshNode. Usa su Transform.visible para mostrarlas u ocultarlas juntas sin establecer posiciones espaciales independientes.

    Nota

    AR Session controla la carga, el registro, la cancelación del registro y la descarga de easyar-occlusion. Obtén y usa el material después de iniciar Session.

  7. Ejecuta el Mini Program en el entorno real del Block correspondiente. Espera a que se carguen los recursos y se complete la localización, activa Archivo de máscara y comprueba que las partes conservadas del modelo oculten correctamente los objetos virtuales.

Usar el modelo denso exportado desde Unity

  1. En la Unity scene, haga clic en el nodo Mega Block y registre el BlockID en el panel Inspector.

    Record BlockID

  2. En Block de Mega Studio, seleccione export.

    Select export

  3. Modifique las export options y exporte.

    Export options

    En la figura, 1 es el LOD level. Cuanto menor sea el level, más simple será el model y menor el número de faces. Si necesita la precision más alta, seleccione 2; si puede aceptar menor precision para reducir el face count, seleccione 1 o 0.

    En la figura, 2 es la texture export option. Como solo necesitamos el white model como occlusion, no se necesitan textures.

  4. Recorte y reduzca el model exportado en digital content creation software, como Blender, y guárdelo como Glb.

    Consejo

    El ejemplo usa el Decimate Modifier de Blender.

    Before cropping

    Después de cropping y reduction:

    After cropping

  5. Coloque el archivo Glb usado para occlusion en el directorio miniprogram/assets/ del proyecto Mini Program y use una ruta relativa, o móntelo en un servidor de archivos HTTPS accesible y use su URL. Al usar una URL de servidor, configure el dominio de descarga legal correspondiente en el backend del Mini Program y añada el dominio a la whitelist del Mini Program.

  6. Cargue el GLTF usado como occlusion en el xr-frame Mini Program.

    Primero cargue el GLTF model usado para occlusion y luego use scene.createElement(xrFrameSystem.XRGLTF,options) para crear el GLTF model.

    Use assets.getAsset("material", "easyar-occlusion") para obtener el material object.

    Use model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {m.setData({ neverCull: true, material: occlusionMaterial });} para modificar el material del GLTF model.

    Nota

    La carga, registration, deregistration y unloading del material easyar-occlusion están controlados por 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" //Aquí debe rellenarse el Block ID
    if (!blockHolder.getBlockById(blockParent.id)) {
        // Si no existe un nodo Block, cree uno
        blockHolder.holdBlock({
            id: blockID
        })
    }
    // Obtener el nodo Block en la escena xr-frame
    let blockElement = blockHolder.getBlockById(blockParent.id).el;
    // Montar el modelo de oclusión recortado bajo el nodo Block como nodo hijo
    blockElement.addChild(model);
    /**
     * Debido a diferencias en el comportamiento de los cargadores GLTF, para que la orientación del modelo en xr-frame coincida exactamente con el resultado renderizado en Unity
    * A veces hay que rotar el modelo cargado 180 grados alrededor del eje Y en el lugar
    */
    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);
    //Nota: el material debe modificarse después de modificar Transform
    if (assetInfo.assetId == 'occlusion1') {
        //Obtener el material de oclusión proporcionado por el plugin Mega
        let occlusionMaterial = scene.assets.getAsset("material", "easyar-occlusion");
        //Modificar el material de oclusión
        model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {
            m.setData({ neverCull: true, material: occlusionMaterial });
        });
    }
}
```
> [!NOTE]
> Aquí, usar el Mega Block dense model tras cropping como occlusion no requiere annotation synchronization para spatial position. Esto se debe a que en digital content creation software, como Blender, el model puede reducirse y recortarse sin cambiar la coordinate system definition.
>
> Si necesita colocar con precisión su propio GLTF model como occlusion, consulte [Cómo colocar un occlusion model alineado con el espacio](./sample.md#wechat-mega-sample-precise-occulusion-model).

El efecto final en dispositivo real se muestra en el vídeo al principio del artículo.

Efecto esperado de occlusion

El efecto de occlusion en un xr-frame Mini Program se ve afectado principalmente por lo siguiente:

  • La precisión de localization tracking itself
  • La precisión de la colocación del model
  • La precisión del propio model, si no es simple geometry

Es normal que aparezcan varios centímetros de desalineación durante localization drift.

Demasiadas faces en el occlusion model pueden afectar fácilmente al performance. Se recomienda usarlo solo en áreas necesarias y usar simple geometry como occlusion siempre que sea posible.

Siguientes pasos

Temas relacionados