WeChat Mini Program Mega Plugin Sample Project Description
This article details the usage, implementation methods, and considerations of the various features demonstrated in the sample project.
Before you begin
- Be able to use the Unity editor to create and upload annotations and record the annotation names and their IDs.
- Be able to use the Unity editor to create 3D content aligned with the real world.
- Be able to fully run the sample project.
How to display a model at the annotation location
Precisely place and upload annotations in the Unity editor, recording annotation names and their IDs

Add GLTF model resources
Add model resources in
sampleAssetswithinminiprogram/components/sample-easyar-mega/index.ts.const sampleAssets = { your_model_name: { assetId: "your_model_asset_id", type: "gltf", src: "url/model.glb", options: {} } }Load the added model resources
Load the model in the
loadAsset()function withinminiprogram/components/sample-easyar-mega/index.ts.async loadAsset() { try { await scene.assets.loadAsset(sampleAssets.your_model_name); } catch (err) { console.error(`Failed to load assets: ${err.message}`); } }Configure annotations to be replaced
Configure target annotations in
miniprogram/components/sample-data/annotation-metadata.ts. Separate multiple entries with commas.export const AnnotationMetaData: Record<string, any> = { "aaaaaaaa-bbbb-cccc-dddd-123456789012": { assetId: "panda", scale: "0.5 0.5 0.5" }, "aaaaaaaa-bbbb-cccc-dddd-123456789013": { assetId: "your_model_asset_id", scale: "1 1 1" } };Replace annotations to load models
Use xr-frame’s factory method
scene.createElement(xrFrameSystem.XRGLTF, options)in EMA’s callback to create model nodes.Parameters:
xrFrameSystem.XRGLTF: Specifies the element type as GLTF model.options: Initialization properties corresponding to component attributes.
Key attributes:
"model": Required. References the loaded resource ID (asset-id)."anim-autoplay": Optional. Specifies auto-play animation after loading."scale": Optional. UsesassetInfo.scaleor "1 1 1".name: Required. Annotation name.
Caution
Ensure correct attribute key formatting (string vs non-string) as shown in the example.
Mount the model under the annotation node via
xrNode.addChild(child).Rotate loaded GLTF models 180 degrees around the Y-axis locally to ensure consistent rendering across platforms.
if (assetInfo && assetInfo.assetId && assetInfo.assetId.trim().length > 0) { model = scene.createElement( xrFrameSystem.XRGLTF, { /** assetId from previous step */ "model": assetInfo.assetId, /** Specify model animation here */ "anim-autoplay": assetInfo.animation ? assetInfo.animation : "", "scale": assetInfo.scale ? assetInfo.scale : "1 1 1", name: emaName } ); xrNode.addChild(model); /** * Rotate model 180° around Y-axis locally to ensure consistent orientation * between xr-frame and Unity due to differing GLTF loader behaviors */ 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); }Run on device
Compare real-device results with Unity editor positions from step 1:
Enable the transparent video button: A transparent-video-material cube appears at world origin
(0, 0, 0).Note
The origin position may be arbitrary. Place occlusion models via annotations as needed (see Create and upload annotations using Unity editor).
Enable the occlusion button: A panda model and stacked cubes appear at world origin
(0, 0, 0). The middle cube uses occlusion material, while a static panda model with occlusion material appears opposite.Note
The origin position may be arbitrary. Place occlusion models via annotations as needed (see Create and upload annotations using Unity editor).

How to play a transparent video at the annotation location
Load a video asset of type
video-texture.async loadAsset() { const videoTexture = { assetId: "fireball", type: "video-texture", // Video resource URL src: "url/video-resource.mp4", options: { autoPlay: true, loop: true, } }; try { // Load the video-texture type resource await scene.assets.loadAsset(videoTexture); } catch (err) { console.error(`Failed to load video texture: ${err.message}`); } }Modify the EMA loading callback
Use
scene.createElement(xrFrameSystem.XRMesh,options)in the EMA loading callback to create a simple geometric shape and assign theeasyar-video-tsbsmaterial, then modify theuniformtou_baseColorMap:video-{$assetId}.Parameters:
xrFrameSystem.XRMesh: Specifies the type of element to create as a basic geometric shape.options: Initial configuration items corresponding to the component's properties.
Key attributes in the code:
"geometry": "cube": Use the built-in cube geometric data of xr-frame."material": "easyar-video-tsbs": Specify a predefined material (inferred from the name, this is a special material that supports video textures)."uniforms": "u_baseColorMap:video-{$assetId}":
Caution
Pay attention to distinguishing between string and non-string attribute keys. Fill them in exactly as shown in the example.
This is the dynamic binding of material parameters.
It maps the video resource (texture) named
video-{$assetId}to the base color map of the material.Effect: This will create a cube with a surface playing a video.
model = scene.createElement(xrFrameSystem.XRMesh, { geometry: "cube", material: "easyar-video-tsbs", uniforms: "u_baseColorMap:video-fireball", }); xrNode.addChild(model);Note
When using
video-texture, if the console shows a warningwx.createVideoDecoder with type: 'wemedia' is deprecated, please ignore it.Confirmed with the WeChat official team, this warning does not affect usage.
Run on a real device
How to place an occlusion model aligned with the space
Precisely place the occlusion model and upload the annotation.

Load the occlusion GLTF in the xr-frame mini program.
Use
scene.assets.loadAsset()to load the model resource (requires manual unloading).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}`); } }Load the model and assign occlusion material during runtime in the EMA load callback
Use
scene.createElement(xrFrameSystem.XRGLTF,options)in the EMA load callback to create a model node.Parameters:
xrFrameSystem.XRGLTF: Specifies the element type to be created as a GLTF model.options: Initialization configuration items, corresponding to the component's properties.
Key properties in the code:
"model": Required, points to the loaded resource ID (asset-id)."scale": OptionalassetInfo.scaleor "1 1 1".name: Required, annotation name.
Caution
Note the distinction between string and non-string property keys. Fill them exactly as shown in the example.
Mount the model under the annotation node using
xrNode.addChild(child).To ensure the GLTF model appears the same across different platform loaders, rotate the loaded model 180 degrees around the Y-axis in place.
Finally, use
model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => {m.setData({ neverCull: true, material: occlusionMaterial });}to modify the GLTF model's material.Note
The loading, registration, deregistration, and unloading of the
easyar-occulusionmaterial are controlled by the AR Session.Use the model at the annotation position as occlusion:
if (...) { model = scene.createElement( xrFrameSystem.XRGLTF, { "model": assetInfo.assetId, "scale": assetInfo.scale ? assetInfo.scale : "1 1 1", name: emaName } ); /** * Due to differences in GLTF loader behavior, to ensure the model's orientation in xr-frame matches Unity's rendering results exactly, * sometimes it's necessary to rotate the loaded model 180 degrees around the Y-axis in 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); //Note: The material must be modified after adjusting the Transform. if (assetInfo.assetId == 'occlusion1') { //Get the occlusion material provided by the mega plugin. let occlusionMaterial = scene.assets.getAsset("material", "easyar-occlusion"); //Modify the occlusion material. model.getComponent(xrFrameSystem.GLTF).meshes.forEach((m: any) => { m.setData({ neverCull: true, material: occlusionMaterial }); }); } }Run on the device
Compare the results with the simulated run in the Unity editor.