Table of Contents

Usare il Mega plugin per implementare occlusion

Occlusion è una tecnologia chiave per migliorare l'integrazione immersiva di virtual e real content in AR. Questo articolo guida all'implementazione di effetti di occlusion nell'environment xr-frame tramite EasyAR cloud localization e annotations.

Prima di iniziare

Come viene implementata occlusion

  • Offline modeling: usare Unity editor per creare geometry con corrispondenza 1:1 nel Block coordinate system per entità del mondo reale come walls, columns e large equipment; oppure ottenere un optimized model tramite cropping e face reduction del Block dense model.

  • Runtime alignment: durante runtime di xr-frame, allineare il Block coordinate system con il real space tramite cloud localization e caricare la geometry corrispondente.

  • Material replacement: assegnare speciali occlusion materials a queste geometries.

  • Visual effect: quando la GPU renderizza altri virtual objects, i pixels delle parti occluse vengono automaticamente eliminati perché non superano il depth test, facendo sì che i virtual objects seguano la logica di occlusion del physical space reale.

Come disporre occlusion con simple geometry

  1. Posizionare con precisione box annotations confrontandole con dense model e panorama. Dopo il posizionamento, l'annotation appare come una "wall" o una "column".

    Annotation as occlusion

  2. Modificare il nome dell'annotation, ad esempio occlusion_wall, registrare l'ID e caricare l'annotation.

  3. Nel xr-frame Mini Program, usare la built-in geometry per caricare l'annotation usata come occlusion.

    Nel callback di caricamento EMA, usare scene.createElement(xrFrameSystem.XRMesh,{}) per creare simple geometry e assegnare il material easyar-occlusion.

    Nota

    Loading, registration, deregistration e unloading del material easyar-occlusion sono controllati da 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, questo panda può ballare dietro la wall.

Come disporre occlusion con complex geometry

È adatto a scenari che richiedono occlusion ad alta precisione, come dispositivi dalla forma irregolare o edifici non regolari. È possibile usare il file maschera dai risultati di mapping, oppure esportare il dense model del Block tramite Mega Studio in Unity e poi ritagliarlo e ridurlo per ottenere un modello bianco per occlusion.

Usare il file maschera dai risultati di mapping

Scaricare il file maschera glb dal risultato di mapping tramite Mask file download nel cloud localization database e ritagliarlo. Il modello ritagliato non deve essere ruotato attorno all'asse Y quando viene caricato in xr-frame.

Nota

Questo metodo vale solo per risultati di mapping versione 9.6 o successiva.

  1. Dopo aver aggiunto il Block al cloud localization database, registrare il Block ID corrispondente e fare clic su Mask file download nella colonna Action.

    Voce download file maschera

  2. Nel popup, selezionare Low precision e fare clic su Download per scaricare il file maschera glb.

    Scaricare file maschera

  3. Aprire il modello scaricato in un software di creazione contenuti digitali, ad esempio Blender.

    File maschera prima del ritaglio

    Ritagliare il modello, mantenere solo la parte necessaria per occlusion e salvarlo come glb. Mantenere invariati posizione, rotazione, scala e sistema di coordinate.

    Se la maschera di un Block contiene più file GLB, puoi unirli in un software di creazione di contenuti digitali oppure ritagliarli ed esportarli separatamente. Durante l'esecuzione, collegali tutti allo stesso nodo Block.

    File maschera dopo il ritaglio

  4. Preparare il file glb ritagliato. È possibile caricare il file su un hosting server HTTPS accessibile dal Mini Program e ottenere l'URL, oppure inserire il file nella directory miniprogram/assets/ del progetto Mini Program e usare direttamente un percorso relativo. Quando si usa un hosting server, configurare il dominio di download legale corrispondente nel backend del Mini Program e aggiungere il dominio alla whitelist del Mini Program.

  5. Inserisci l'indirizzo del modello in sampleAssets.occlusionMesh.src nella configurazione delle risorse del componente. Usa un percorso relativo per i modelli in assets/. Per un solo GLB basta una voce; se ci sono più parti, aggiungi una voce per ogni parte da caricare, con un assetId univoco e il relativo src. Ad esempio:

    const occlusionMeshAssets = [
        // Un file GLB
        {
            assetId: "occlusionMesh00",
            type: "gltf",
            src: "assets/Low_Poly_occlusion.glb",
            options: {}
        },
        // Più file 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 il nome effettivo del file scaricato o ritagliato. src può anche contenere un URL HTTPS del server, ad esempio https://your-domain.example/occlusion.glb.

    Il componente deve implementare il caricamento del modello, il mounting, l'assegnazione del material di occlusion e il rilascio delle risorse. Montare il modello sotto il nodo Block localizzato correttamente e mantenere position, rotation e scale originali del modello.

  6. Carica il GLTF usato per l'occlusione nel Mini Program xr-frame.

    Dopo il caricamento dei modelli e almeno una localizzazione riuscita, crea un XRNode condiviso sotto il primo nodo Block. Crea un XRGLTF per ogni parte caricata e sostituisci il materiale della mesh con easyar-occlusion del plugin Mega. Il metodo completo seguente unisce il collegamento dei modelli e la sostituzione del materiale; può sostituire showOcclusionMesh() nell'esempio:

    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);
            // Collega il nodo e imposta Transform prima di sostituire il materiale di occlusione.
            // La maschera del risultato della mappatura mantiene le coordinate originali; non serve ruotarla intorno all'asse 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;
    },
    

    Tutte le parti condividono il nodo padre occlusionMeshNode. Usa il suo Transform.visible per mostrarle o nasconderle insieme senza impostare posizioni spaziali separate.

    Nota

    AR Session controlla caricamento, registrazione, annullamento della registrazione e scaricamento di easyar-occlusion. Ottieni e usa il materiale dopo l'avvio della Session.

  7. Esegui il Mini Program nell'ambiente reale del Block corrispondente. Attendi il caricamento delle risorse e il completamento della localizzazione, attiva File maschera e verifica che le parti conservate del modello nascondano correttamente gli oggetti virtuali.

Usare il dense model esportato da Unity

  1. Nella Unity scene, fare clic sul node Mega Block e registrare il BlockID nel panel Inspector.

    Record BlockID

  2. In Block di Mega Studio selezionare export.

    Select export

  3. Modificare le export options ed esportare.

    Export options

    Nella figura, 1 è il LOD level. Più basso è il level, più semplice è il model e minore è il numero di faces. Se serve la precision massima selezionare 2; se è accettabile ridurre la precision per diminuire il face count, selezionare 1 o 0.

    Nella figura, 2 è la texture export option. Poiché serve solo il white model come occlusion, le textures non sono necessarie.

  4. Ritagliare e ridurre il model esportato in digital content creation software, come Blender, e salvarlo come Glb.

    Consiglio

    Nell'esempio viene usato Blender Decimate Modifier.

    Before cropping

    Dopo cropping e reduction:

    After cropping

  5. Inserire il file Glb usato per occlusion nella directory miniprogram/assets/ del progetto Mini Program e usare un percorso relativo, oppure montarlo su un file server HTTPS accessibile e usarne l'URL. Quando si usa un URL server, configurare il dominio di download legale corrispondente nel backend del Mini Program e aggiungere il dominio alla whitelist del Mini Program.

  6. Caricare il GLTF usato come occlusion nel xr-frame Mini Program.

    Prima caricare il GLTF model usato per occlusion, poi usare scene.createElement(xrFrameSystem.XRGLTF,options) per creare il GLTF model.

    Usare assets.getAsset("material", "easyar-occlusion") per ottenere il material object.

    Usare model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {m.setData({ neverCull: true, material: occlusionMaterial });} per modificare il material del GLTF model.

    Nota

    Loading, registration, deregistration e unloading del material easyar-occlusion sono controllati da 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" //Inserire qui il Block ID
    if (!blockHolder.getBlockById(blockParent.id)) {
        // Se non esiste un nodo Block, crearne uno
        blockHolder.holdBlock({
            id: blockID
        })
    }
    // Ottiene il nodo Block nella scena xr-frame
    let blockElement = blockHolder.getBlockById(blockParent.id).el;
    // Monta il modello di occlusione ritagliato sotto il nodo Block come nodo figlio
    blockElement.addChild(model);
    /**
     * Poiché i loader GLTF si comportano in modo diverso, per mantenere l’orientamento del modello in xr-frame esattamente coerente con il risultato renderizzato in Unity
    * A volte il modello caricato deve essere ruotato sul posto di 180 gradi attorno all’asse 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);
    //Nota: il materiale deve essere modificato dopo aver modificato Transform
    if (assetInfo.assetId == 'occlusion1') {
        //Ottiene il materiale di occlusione fornito dal plugin Mega
        let occlusionMaterial = scene.assets.getAsset("material", "easyar-occlusion");
        //Modifica il materiale di occlusione
        model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {
            m.setData({ neverCull: true, material: occlusionMaterial });
        });
    }
}
```
> [!NOTE]
> Qui, usare il Mega Block dense model dopo cropping come occlusion non richiede annotation synchronization della spatial position. Questo perché in digital content creation software, come Blender, è possibile ridurre e ritagliare il model senza cambiare la coordinate system definition.
>
> Se è necessario posizionare con precisione il proprio GLTF model come occlusion, vedere [Come posizionare un occlusion model allineato con lo spazio](./sample.md#wechat-mega-sample-precise-occulusion-model).

L'effetto finale su dispositivo reale è mostrato nel video all'inizio dell'articolo.

Effetto atteso di occlusion

L'effetto di occlusion su un xr-frame Mini Program è influenzato principalmente da:

  • Precisione del localization tracking itself
  • Precisione del posizionamento del model
  • Precisione del model stesso, se non è simple geometry

Durante localization drift, un disallineamento di alcuni centimetri è normale.

Troppe faces nel occlusion model possono facilmente influire sulla performance. Si consiglia di usarlo solo nelle aree necessarie e di usare il più possibile simple geometry come occlusion.

Passaggi successivi

Argomenti correlati