๐ Stumbled here on accident? Start with the first part!
Welcome back to the 7th article in this WebXR/Babylon.js series. This part is about Anchors in WebXR.
โน๏ธ Remember - you can always run the code associated with this article and follow along using
npm start --part=7
What are Anchors?
Anchors are used to create a stable reference point in the physical space, which can be tracked by the device's sensors and cameras. When an anchor is set, the virtual object tied to it appears to be part of the real world, staying in the same place as if it were a physical object.
โ Anchoring is crucial for maintaining consistency in the virtual environment as it relates to the real world.
Adding anchors to the scene
To add an Anchor to the scene, we go back into our handleControllerSelection
, take the raycastHit
value and call addAnchorAtPositionAndRotationAsync
to add an anchor at the ray cast hit position.
Interacting with the controller
handleControllerSelection() {
...
if (raycastHit.pickedMesh === this._box) {
const mat = this._box!.material as StandardMaterial;
mat.diffuseColor = Color3.Random();
this._box!.material = mat;
} else {
this.addAnchorAtPosition(raycastHit);
}
this._box!.isVisible = true;
...
}
We first check if the mesh we picked is the box and if so, we just give it a randomly different color.
If we donโt pick the box but any other position in the 3D space we will call a new function addAnchorAtPosition
with the raycastHit
as a parameter.
Adding the Anchor
addAnchorAtPosition(raycastHit: PickingInfo) {
this._xrAnchors!.addAnchorAtPositionAndRotationAsync(raycastHit.pickedPoint!).then((anchor) => {
const boxTransformNode = new TransformNode('boxTransformNode');
boxTransformNode.position = raycastHit.pickedPoint!;
this._box!.parent = boxTransformNode;
this._box!.position = Vector3.Zero();
this._box!.isVisible = true;
anchor.attachedNode = boxTransformNode;
});
}
This is where we add a new anchor at our raycastHit.pickedPointposition
.
Since anchors have a fixed position in space and donโt support animations, we first have to help ourselves by creating a so called TransformNode
for the rotating box. A TransformNode is a versatile and flexible helper that, in our case, acts as a helper to the box by acting as parent to it. Therefore the box keeps itโs rotation animation.
The position of this transform node is set to the point where the ray cast hit, making it align with the XR anchor.
The box is then parented to boxTransformNode
. This means any transformation applied to boxTransformNode
will also affect the box. The box's position is set to Vector3.Zero()
, which means it's placed at the origin relative to its parent (i.e., the boxTransformNode
). Since the transform node is already positioned at the ray cast hit point, this effectively places the box there.
this._box!.isVisible = true;
makes the box visible in the scene.
Finally, the transform node is attached to the XR anchor. This ensures that the transform node (and therefore the box) will maintain its position relative to the real world, even as the user moves around in the XR space.
Cleaning up
observeAnchors() {
if (this._xrAnchors === null) {
return;
}
this._xrAnchors.onAnchorAddedObservable.add((addedAnchor) => {
this._xrAnchors!.anchors.forEach((anchor: IWebXRAnchor) => {
if (anchor !== addedAnchor) {
anchor.remove();
}
});
})
}
this._xrAnchors.onAnchorAddedObservable.add(() => { ... })
sets up an observer that will execute the provided callback function every time a new anchor is added to the XR environment.
Within the callback, there's a loop this._xrAnchors!.anchors.forEach((anchor: IWebXRAnchor) => { ... })
that iterates over all the anchors currently in this._xrAnchors
.
Inside the loop, anchor.remove()
is called on each anchor except the one we just added. This means that every time a new anchor is added to the system, all existing anchors are removed.
The primary intent of this function is to maintain a single active anchor in the XR environment. When a new anchor is added, it triggers the removal of all existing anchors. This is useful in scenarios where only the latest anchor is relevant, and previous anchors should not persist.
Adding observeAnchors
to the scene
async createScene(): Promise<Scene> {
...
this.observeAnchors();
return this._scene;
}
To make use of the function we have to add it to createScene
.
Conclusion
In conclusion, this seventh installment of our WebXR/Babylon.js series has taken us through the pivotal concept of Anchors in WebXR. Anchors are more than just a technical component in the realm of extended reality; they serve as the vital bridge connecting the virtual and real worlds. By creating these stable reference points in physical space, which remain consistent regardless of the viewer's movement or perspective, we enhance the immersion and believability of our virtual environments.
The practical demonstrations and code examples provided, from handling controller selections to dynamically adding and managing anchors, have illustrated how these concepts are not just theoretical but highly applicable in real-world scenarios. The use of TransformNode as a flexible tool to maintain object animations while anchoring, and the strategic management of anchors to keep the XR environment clean and relevant, show the depth and versatility of Babylon.js in creating compelling XR experiences.
In the eighth part we dive into loading models and assets.
Top comments (0)