Table of Contents

Utiliser le Mega plugin pour implémenter occlusion

Occlusion est une technologie clé pour améliorer l'intégration immersive du virtual et real content en AR. Cet article vous guide pour implémenter des effets d'occlusion dans l'environment xr-frame via EasyAR cloud localization et annotations.

Avant de commencer

Comment occlusion est implémentée

  • Offline modeling : utilisez Unity editor pour créer dans le Block coordinate system une geometry correspondant 1:1 aux entités du monde réel, comme murs, colonnes et grands équipements ; ou obtenez un optimized model par cropping et face reduction du Block dense model.

  • Runtime alignment : au runtime xr-frame, alignez le Block coordinate system avec le real space via cloud localization et chargez la geometry correspondante.

  • Material replacement : attribuez des occlusion materials spéciaux à ces geometries.

  • Visual effect : lorsque le GPU rend d'autres virtual objects, les pixels des parties occluded sont automatiquement éliminés car ils échouent au depth test, ce qui fait suivre aux virtual objects la logique d'occlusion du physical space réel.

Comment organiser occlusion avec simple geometry

  1. Placez précisément les box annotations en comparant avec le dense model et le panorama. Après placement, l'annotation ressemble à un "wall" ou une "column".

    Annotation as occlusion

  2. Modifiez le nom de l'annotation, par exemple occlusion_wall, notez l'ID et upload l'annotation.

  3. Dans le xr-frame Mini Program, utilisez sa built-in geometry pour charger l'annotation utilisée comme occlusion.

    Dans le callback de chargement EMA, utilisez scene.createElement(xrFrameSystem.XRMesh,{}) pour créer une simple geometry et lui attribuer le material easyar-occlusion.

    Note

    Le loading, registration, deregistration et unloading du material easyar-occlusion sont contrôlés par 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>

> Avec occlusion, ce panda peut danser derrière le mur.

Comment organiser occlusion avec complex geometry

Cela convient aux scénarios nécessitant une occlusion de haute précision, comme les équipements de forme irrégulière ou les bâtiments irréguliers. Vous pouvez utiliser le fichier de masque issu des résultats de cartographie, ou exporter le modèle dense du Block via Mega Studio dans Unity, puis le découper et le réduire pour obtenir un modèle blanc d'occlusion.

Utiliser le fichier de masque issu des résultats de cartographie

Téléchargez le fichier de masque glb depuis le résultat de cartographie via Mask file download dans la base de données de localisation cloud et découpez-le. Le modèle découpé n'a pas besoin d'être tourné autour de l'axe Y lors du chargement dans xr-frame.

Note

Cette méthode ne s'applique qu'aux résultats de mapping version 9.6 ou ultérieure.

  1. Après avoir ajouté le Block à la base de données de localisation cloud, notez le Block ID correspondant et cliquez sur Mask file download dans la colonne Action.

    Entrée de téléchargement du fichier de masque

  2. Dans la fenêtre contextuelle, sélectionnez Low precision et cliquez sur Download pour télécharger le fichier glb.

    Télécharger le fichier de masque

  3. Ouvrez le modèle téléchargé dans un logiciel de création de contenu numérique, par exemple Blender.

    Fichier de masque avant découpe

    Découpez le modèle, conservez seulement la partie nécessaire à l'occlusion et enregistrez-le en glb. Gardez position, rotation, échelle et système de coordonnées d'origine.

    Si le masque d'un Block contient plusieurs fichiers GLB, vous pouvez les fusionner dans un logiciel de création de contenu numérique ou les découper et les exporter séparément. À l'exécution, attachez-les tous au même nœud Block.

    Fichier de masque après découpe

  4. Préparez le fichier glb recadré. Vous pouvez téléverser le fichier sur un hosting server HTTPS accessible par le Mini Program et obtenir l'URL, ou placer le fichier dans le répertoire miniprogram/assets/ du projet Mini Program et utiliser directement un chemin relatif. Lors de l'utilisation d'un hosting server, configurez le domaine de téléchargement légal correspondant dans le backend du Mini Program et ajoutez le domaine à la whitelist du Mini Program.

  5. Indiquez l'adresse du modèle dans sampleAssets.occlusionMesh.src, dans la configuration des ressources du composant. Utilisez un chemin relatif pour les modèles dans assets/. Une seule entrée suffit pour un GLB ; pour plusieurs parties, ajoutez une entrée par partie à charger, avec un assetId unique et le src correspondant. Par exemple :

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

    Utilisez le véritable nom du fichier téléchargé ou découpé. src peut aussi contenir une URL HTTPS du serveur, telle que https://your-domain.example/occlusion.glb.

    Le composant doit implémenter le chargement du modèle, le mounting, l'affectation du material d'occlusion et la libération des ressources. Montez le modèle sous le noeud Block localisé avec succès et conservez les position, rotation et scale d'origine du modèle.

  6. Chargez le GLTF d'occlusion dans le mini-programme xr-frame.

    Une fois les modèles chargés et la localisation réussie au moins une fois, créez un XRNode commun sous le premier nœud Block. Créez un XRGLTF pour chaque partie chargée et remplacez le matériau de son maillage par easyar-occlusion du plugin Mega. La méthode complète suivante associe les modèles et remplace le matériau ; elle peut remplacer showOcclusionMesh() dans l'exemple :

    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);
            // Attacher le nœud et régler Transform avant de remplacer le matériau d'occlusion.
            // Le masque issu de la cartographie conserve ses coordonnées d'origine ; aucune rotation autour de l'axe Y n'est nécessaire.
            occlusionMesh.getComponent(xrFrameSystem.GLTF).meshes.forEach((mesh: any) => {
                mesh.setData({ neverCull: true, material: occlusionMaterial });
            });
        }
        node.getComponent(xrFrameSystem.Transform).visible = this.data.occlusionMeshVisible;
        occlusionMeshNode = node;
    },
    

    Toutes les parties partagent le nœud parent occlusionMeshNode. Utilisez son Transform.visible pour les afficher ou les masquer ensemble sans définir de positions spatiales séparées.

    Note

    AR Session contrôle le chargement, l'enregistrement, la désinscription et le déchargement de easyar-occlusion. Récupérez et utilisez le matériau après le démarrage de la Session.

  7. Exécutez le mini-programme dans l'environnement réel du Block correspondant. Attendez le chargement des ressources et la réussite de la localisation, activez Fichier de masque, puis vérifiez que les parties conservées du modèle occultent bien les objets virtuels.

Utiliser le modèle dense exporté depuis Unity

  1. Dans la Unity scene, cliquez sur le node Mega Block et notez le BlockID dans le panel Inspector.

    Record BlockID

  2. Dans Block de Mega Studio, sélectionnez export.

    Select export

  3. Modifiez les export options puis exportez.

    Export options

    Dans la figure, 1 correspond au LOD level. Plus le level est bas, plus le model est simple et moins il contient de faces. Si vous avez besoin de la precision la plus élevée, sélectionnez 2 ; si vous pouvez accepter une precision réduite pour diminuer le face count, sélectionnez 1 ou 0.

    Dans la figure, 2 correspond à la texture export option. Comme seul le white model est nécessaire pour occlusion, les textures ne sont pas nécessaires.

  4. Crop et reduce le model exporté dans digital content creation software, par exemple Blender, puis enregistrez-le en Glb.

    Astuce

    L'exemple utilise le Decimate Modifier de Blender.

    Before cropping

    Après cropping et reduction :

    After cropping

  5. Placez le fichier Glb utilisé pour l'occlusion dans le répertoire miniprogram/assets/ du projet Mini Program et utilisez un chemin relatif, ou montez-le sur un serveur de fichiers HTTPS accessible et utilisez son URL. Lors de l'utilisation d'une URL de serveur, configurez le domaine de téléchargement légal correspondant dans le backend du Mini Program et ajoutez le domaine à la whitelist du Mini Program.

  6. Chargez le GLTF utilisé comme occlusion dans le xr-frame Mini Program.

    Commencez par load le GLTF model utilisé pour occlusion, puis utilisez scene.createElement(xrFrameSystem.XRGLTF,options) pour créer le GLTF model.

    Utilisez assets.getAsset("material", "easyar-occlusion") pour obtenir le material object.

    Utilisez model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {m.setData({ neverCull: true, material: occlusionMaterial });} pour modifier le material du GLTF model.

    Note

    Le loading, registration, deregistration et unloading du material easyar-occlusion sont contrôlés par 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" //Renseigner ici le Block ID
    if (!blockHolder.getBlockById(blockParent.id)) {
        // Si aucun nœud Block n’existe, en créer un
        blockHolder.holdBlock({
            id: blockID
        })
    }
    // Obtenir le nœud Block dans la scène xr-frame
    let blockElement = blockHolder.getBlockById(blockParent.id).el;
    // Attacher le modèle d’occlusion découpé sous le nœud Block comme nœud enfant
    blockElement.addChild(model);
    /**
     * En raison des différences de comportement des chargeurs GLTF, pour que l’orientation du modèle dans xr-frame corresponde exactement au rendu de Unity
    * Il faut parfois faire pivoter le modèle chargé de 180 degrés autour de l’axe Y sur place
    */
    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);
    //Remarque : le matériau doit être modifié après Transform
    if (assetInfo.assetId == 'occlusion1') {
        //Obtenir le matériau d’occlusion fourni par le plugin Mega
        let occlusionMaterial = scene.assets.getAsset("material", "easyar-occlusion");
        //Modifier le matériau d’occlusion
        model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {
            m.setData({ neverCull: true, material: occlusionMaterial });
        });
    }
}
```
> [!NOTE]
> Ici, utiliser le Mega Block dense model après cropping comme occlusion ne nécessite pas d'annotation synchronization pour la spatial position. En effet, dans digital content creation software comme Blender, le model peut être réduit et découpé sans modifier la coordinate system definition.
>
> Si vous devez placer précisément votre propre GLTF model comme occlusion, consultez [Comment placer un occlusion model aligné avec l'espace](./sample.md#wechat-mega-sample-precise-occulusion-model).

L'effet final sur appareil réel est présenté dans la vidéo en haut de cet article.

Effet occlusion attendu

L'effet d'occlusion sur un xr-frame Mini Program est principalement affecté par les points suivants :

  • La précision de localization tracking itself
  • La précision du placement du model
  • La précision du model lui-même, s'il ne s'agit pas de simple geometry

Un décalage de plusieurs centimètres pendant localization drift est normal.

Un occlusion model avec trop de faces peut facilement affecter performance. Il est recommandé de l'utiliser uniquement dans les zones nécessaires et d'utiliser autant que possible simple geometry comme occlusion.

Étapes suivantes

Rubriques associées