# Build an Advanced Portal Experience

{% content-ref url="<https://app.gitbook.com/o/QMsd5hT6sxA9FBIB03yh/s/3UKy7VHRJWrNarYaMp13/>" %}
[Blippar Documentation Centre](https://app.gitbook.com/o/QMsd5hT6sxA9FBIB03yh/s/3UKy7VHRJWrNarYaMp13/)
{% endcontent-ref %}

## Introduction

This document provides a brief overview and walkthrough into the construction of a portal experience, built using **‘Blippar's WebAR SDK, A-Frame, Blender(optional).’** It displays an output of a portal model fixed to an identified surface. This portal provides a gateway through a door, to a **whole new virtual world!** ([see here](#h_01fhdhbxr43xnrtq5nc20t9he0)).

## Concept

The main concept on which the portal experience is built is described in this document.

## Portal Experience Logic

The portal experience logic has the following needs:

* Room 3D model, a Sphere with 360° image/video texture
* Door 3D model to enter the Room.
* Hider 3D model, a Sphere with invisible material to make the external surface of the Room invisible on the AR scene.

## WebAR SDK API Utilised

It mainly uses the following two WebAR SDK APIs:

1. *<mark style="color:blue;">SetPortalEntryCallback() API</mark>*\
   \
   When the user takes the mobile phone closer to the Door 3D model (which is at the origin of the detected surface), *SetPortalEntryCallback()* API triggers the passed callback.\\
2. *<mark style="color:blue;">DisableTracking(true) API</mark>*

* This API disables the Surface Tracking in the callback.
* This API can also be used to enable the gyro camera experience.
* Pass the argument as *true* as in *<mark style="color:blue;">DisableTracking(true)</mark>* call to enable it.
* The user can use the gyro camera to experience an entry into a virtual world and look around the inner side of the Room Sphere having 360° image/video material

## Procedure

Follow the below steps to create your AR portal experience web app:

{% hint style="info" %}

* Creating the Portal Models in Blender is [(STEP 1-OPTIONAL)](#h_01fhdh8wzw3qp9sy2e4dm7aygf) You can use the portal and hider 3D models that we have already created for your testing purposes from the [Github repo here](https://github.com/blippar/webar-sdk-example/tree/main/aframe/portal-webar) and proceed to [STEP 2](#h_01fhdh9jqfz8t39s9mywr15n5t)
* If you are making your own portal models, make sure the door is at the origin of the detected surface i.e. <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark> as shown in Fig 1.
  {% endhint %}

## **Step 1: Create Portal Models in Blender (Optional)** <a href="#h_01fhdh8wzw3qp9sy2e4dm7aygf" id="h_01fhdh8wzw3qp9sy2e4dm7aygf"></a>

The AR portal experience basically needs two models.

**portal.glb** containing:

* **Portal Door** - A *Grid* mesh converted into a rectangular door and located at the origin.
* **Portal Room** - A *Sphere* mesh with a 360° image texture

**portal-hider.glb** containing:

* **Portal Hider** - A *Sphere* mesh to cover/hide the Portal Room mesh. This Portal Hider model will be made transparent so that the Portal Room will look invisible from the outside.

To Create a ***portal.blend*** file follow the steps:

* Open the Blender application
* *Create* a *New* ***General** Blender* file.
* Select all the default objects (Cube, Camera and Light) and *Delete* them.
* Save the empty Blender scene as ***portal.blend*** file.

To Create and export the GLB model files in Blender follow the steps:

**A. Create a Portal Door Mesh**

* Add a ***Grid*** mesh
* Rotate it about the ***X axis*** by ***90°***
* Switch to ***Edit Mode,*** select the **inner \_Faces**\_ of the ***Grid*** mesh and ***Delete*** them
* Switch to ***Object Mode*** and edit the ***Scale*** properties as **X = 0.200**, **Y = 0.250**
* Switch to ***Edit Mode***, select **all the Faces** and **Extrude** by **0.1**
* Switch to ***Object Mode*** and edit the Location properties as **Y = 0.1 m**, so that the entrance is exactly at the origin.

**B. Create a Portal Room Mesh**

* Add a ***UV Sphere*** mesh and change its ***Add UV Sphere*** attributes to ***Segments = 32*** and ***Rings = 32***
* Change its ***Location*** properties to ***Y = 1.025,** so that the **Grid**(Door) mesh intersects on the surface of the sphere*
* Switch to **Edit Mode**, select the sphere's ***Faces*** appearing within the door boundary and **Subdivide** the Edges by setting ***Number of Cuts = 2***
* ***Deselect*** all and ***Select*** the sphere's ***Faces*** appearing within the door boundary and ***Delete*** them\_.\_

**C. Add a 360° Texture Image to the Portal Room Mesh**

* Select the ***Sphere*** mesh
* Add a new **Material** and change its ***Base Color*** to ***Image Texture***
* Click ***Open*** and select a **360° image file** of your choice
* ***Right click*** on the sphere and select ***Shade*** ***Smooth***
* Click ***Viewport Shading***, Click ***Display in Material preview mode*** to verify the 360° texture material
* Select the ***Grid*** mesh
* Add a new **Material** and change its ***Base Color*** to *any color* of your choice

**D. Create the Portal Hider Mesh**

* Select the ***Sphere*** mesh and ***Duplicate*** it in the same location
* ***Rename*** the new ***Sphere.001*** mesh as ***Hider***
* Scale the object to **1.01** across **all the axis**
* Go to its ***Material Properties*** and ***Remove*** its ***Material Slot***

**E. Export the Portal GLB**

* Select both the ***Grid*** and ***Sphere*** meshes
* Go to ***File*** -> ***Export*** -> ***glTF 2.0 (.glb/.gltf)***
* On the right side panel of ***Blender File View*** dialog, go to ***Include*** tab and check ***Selected Objects*** (if not checked)
* Enter the name as **portal.glb**
* Click ***Export glTF 2.0*** button

**F. Export the Portal Hider GLB**

* Select the ***Hider*** mesh
* Go to ***File*** -> ***Export*** -> ***glTF 2.0 (.glb/.gltf)***
* On the right side panel of ***Blender File View*** dialog, go to ***Include*** tab and check ***Selected Objects*** (if not checked)
* Enter the name as **portal-hider.glb**
* Click ***Export glTF 2.0*** button

## **Step 2: Setup Base Code Using the WebAR SDK Example** <a href="#h_01fhdh9jqfz8t39s9mywr15n5t" id="h_01fhdh9jqfz8t39s9mywr15n5t"></a>

* Purchase the Blippar License for your domain and download the WebAR SDK zip file. Extract the files and two folders named ***blippar*** and ***example*** will appear.
* Rename the example folder to portal-webar (or any name of your choice).
* Copy the ***portal.glb*** and ***portal-hider.glb*** files created in the previous step (if you skipped the previous step copy them from [this link](https://github.com/blippar/webar-sdk-example/tree/main/aframe/portal-webar)) and paste it in the models folder.

<figure><img src="https://content.gitbook.com/content/MNoA4M7dz4DXv12sDZUk/blobs/4XHCRCRFQm7futtB1kLG/Portal_Base_Code%20(1).gif" alt=""><figcaption><p>Setup Base Code</p></figcaption></figure>

## **Step 3: Import Portal models** <a href="#h_01fhdh9sjhq6a265zc4s5neejx" id="h_01fhdh9sjhq6a265zc4s5neejx"></a>

* Open the WebAR SDK folder in **Visual Studio Code (VSC)**
* Click open ***portal-webar / index.html*** file
* Under <mark style="color:orange;">\<a-assets></mark> tag, do the following:
* \- Remove the following line

```html
<a-asset-item id="astronaut" src="models/astronaut.glb"></a-asset-item>
```

* +Add the following lines to import portal glb files as asset items

```html
<a-asset-item id="portalhider" src="models/portal-hider.glb"></a-asset-item>
<a-asset-item id="portal" src="models/portal.glb"></a-asset-item>
```

* Under <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark>
* \- Remove the following line

```html
<a-entity gltf-model="#astronaut" id="astronaut_1" rotation="0 0 0" position="0 0 0" scale="0.25 0.25 0.25" webar-loadmonitor="elType: glb"></a-entity>
```

* \+ Add the following lines to add the portal models as AFRAME entities, scaled to **3.0** on all the axis

```html
<a-entity gltf-model="#portalhider" id="portal_1" rotation="0 0 0" position="0 0 0" scale="3.0 3.0 3.0" webar-loadmonitor="elType: glb"></a-entity>
<a-entity gltf-model="#portal" id="portal_2" rotation="0 0 0" position="0 0 0" scale="3.0 3.0 3.0" webar-loadmonitor="elType: glb"></a-entity>
```

<figure><img src="https://content.gitbook.com/content/MNoA4M7dz4DXv12sDZUk/blobs/DK4DWEakRizTkgWAPlmq/Portal_ImportModels.gif" alt=""><figcaption><p>Import Portal Models</p></figcaption></figure>

## **Step 4: Add Surface Landing Animation** <a href="#h_01fhdha14dhvcgh458j59avape" id="h_01fhdha14dhvcgh458j59avape"></a>

* \+ Include the [aframe-animation-timeline-component js](https://github.com/supermedium/superframe/tree/master/components/animation-timeline/) script tag after the WebAR SDK's script

```html
<script src="https://unpkg.com/aframe-animation-timeline-component@2.0.0/dist/aframe-animation-timeline-component.min.js"></script>
```

* \- Remove the following lines from the example code

```html
<!-- Refer API:Functions documentation for more details -->
<script>
  // Give a callback when the WebAR Stage <a-entity webar-stage> is ready to display the 3d object
  WEBARSDK.SetStageReadyCallback(() => {
    console.info('Stage is ready now!!!');
  });
</script>
```

* \+ Add the following lines of code under <mark style="color:orange;">\<body></mark> and before <mark style="color:orange;">\<a-scene</mark> <mark style="color:blue;">webar-scene</mark><mark style="color:orange;">></mark> element to:
* Create a custom *landingcursor* A-Frame component.
* The landing cursor A-Frame ring object/entity created in the following steps uses this *landingcursor* component.
* When initialised call WebAR SDK's <mark style="color:orange;">SetStageReady()</mark> callback to emit ***landit*** event to start ***landingTimeline*** animation sequence.
* Listen to ***cursorend*** mixin animation's ***animationcomplete*** event (*<mark style="color:red;">animationcomplete\_\_cursorend</mark>*), to make portal objects visible and then cursor object invisible.

```html
<script>
  var portalObject = undefined;
  var portalHiderObject = undefined;

  AFRAME.registerComponent("landingcursor", {
    init: function() {
      WEBARSDK.SetStageReadyCallback(() => {
        console.log('Stage is ready now!!!');
        portalHiderObject = document.getElementById('portal_1');
        portalObject = document.getElementById('portal_2');

        // First, show the surface landing animation
        this.el.emit('landit');
      });

      this.el.addEventListener("animationcomplete__cursorend", e => {
        // Second, show the 3D object
        portalHiderObject.setAttribute('visible', true);
        portalObject.setAttribute('visible', true);

        // Hide surface landing cursor
        this.el.setAttribute('visible', false);
      });
    }
  });
</script>
```

* \+ Add the following lines of code under <mark style="color:orange;">\<a-assets></mark> to:
* Add a timeline asset <mark style="color:orange;">\<a-timeline</mark> <mark style="color:red;">id</mark>=<mark style="color:blue;">"landingTimeline"</mark>> in A-Frame's <mark style="color:orange;">\<a-asset></mark>. It applies two timeline animations, ***cursorbegin*** and ***cursorend*** to the selected cursor entity (#landing).

```html
<a-timeline id="landingTimeline">
  <a-timeline-animation select="#landing" name="cursorbegin"></a-timeline-animation>
  <a-timeline-animation select="#landing" name="cursorend"></a-timeline-animation>
</a-timeline>
```

\+ Add the following lines of code under <mark style="color:orange;">\<a-assets></mark> to:

* add the animation ***mixin*** element <mark style="color:orange;">\<a-mixin</mark> <mark style="color:red;">id</mark>=<mark style="color:blue;">"landeffect" ...</mark><mark style="color:orange;">></mark> which will be later used by the cursor ring object and it defines the following two animations:\\
  * ***cursorbegin*** to make the cursor visible
  * ***cursorbegin*** to scale up the cursor object

```html
<a-mixin id="landeffect"
 animation__cursorbegin="property: visible; from: false; to: true; dur: 1; autoplay: false"
 animation__cursorend="property: scale; to: 0.75 0.75 0.75; dur: 2000; easing: easeInOutElastic; autoplay: false"
 scale="0.0001 0.0001 0.0001"
 position="0 0 0"
 visible="false"></a-mixin>
```

* \+ Add the following attribute to:
* Add an animation-timeline attribute to A-Frame's <mark style="color:orange;">\<a-scene</mark> <mark style="color:red;">webar-scene</mark><mark style="color:orange;">></mark> to link the landingTimeline element defined above

```html
animation-timeline__1="timeline: #landingTimeline; loop: false; startEvents: landit;"
```

* \+ Add the following line of code under <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark> to:
* Define the cursor ring object <mark style="color:orange;">\<a-ring</mark> *l<mark style="color:red;">andingcursor</mark>* <mark style="color:red;">id</mark>=<mark style="color:blue;">"landing"</mark> <mark style="color:red;">mixin</mark>=<mark style="color:blue;">"landeffect" ...</mark>> which will be animated to scale up when a surface is detected

```html
<a-ring landingcursor id="landing" mixin="landeffect" color="#FBB42C" material="opacity: 0.4; transparent: true" radius-inner="0.8" radius-outer="1" rotation="-90 0 0" segments-phi=30 segments-theta=96></a-ring>
```

## **Step 5: Add Portal Entry Logic** <a href="#h_01fhdhbg990yjm6aefr64dq44r" id="h_01fhdhbg990yjm6aefr64dq44r"></a>

* \+ Add the following lines of code under <mark style="color:orange;">\<script></mark> tag which is before the <mark style="color:orange;">\<a-scene</mark> <mark style="color:red;">webar-scene</mark><mark style="color:orange;">></mark> element:
* To get a callback using *<mark style="color:orange;">WEBARSDK.SetPortalEntryCallback()</mark>*, when the user goes closer to the Door which is at the origin of the detected surface i.e <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">webar-stage</mark><mark style="color:orange;">></mark>
* Once the *Portal Entry* *callbackFunction* is invoked, the *<mark style="color:red;">WEBARSDK.DisableTracking</mark>(<mark style="color:blue;">true</mark>)* API is called to disable surface tracking and enable the gyro camera which is when the user's mobile phone camera is virtually inside the Room sphere model.

```javascript
let closenessRatio = 0.25;

WEBARSDK.SetPortalEntryCallback(() => {
  console.log("Entered the portal door");

 let enableGyroCam = true;
  WEBARSDK.DisableTracking(enableGyroCam);

  // Position to the center of 360 view
  // Its a one way portal. No going back to the real world
  portalObject.object3D.position.set(0, 0, 3.0);
  portalHiderObject.object3D.position.set(0, 0, 3.0);
  console.log("Enabled gyro cam");
},
closenessRatio);
```

## **Step 6: Hide Portal Backside** <a href="#h_01fhdhaw5cqrdzhvdcybebh7ns" id="h_01fhdhaw5cqrdzhvdcybebh7ns"></a>

* \+ Add *<mark style="color:red;">hider-material</mark>* attribute to <mark style="color:orange;">\<a-entity</mark> <mark style="color:red;">gltf-model</mark>=<mark style="color:blue;">"#portalhider" ...</mark><mark style="color:orange;">></mark> as in the below code:

```html
<a-entity gltf-model="#portalhider" id="portal_1" rotation="0 0 0" position="0 0 0" scale="3.0 3.0 3.0" webar-loadmonitor="elType: glb"
hider-material></a-entity>
```

* \+ Add the following lines of code under \<script> tag which is before the \<a-scene webar-scene> element to:
* Define a custom A-Frame component which makes the Hider model and the object that is appearing behind this Hider model invisible.
* Enable this by setting the material's *colorWrite* to *false*.

```javascript
AFRAME.registerComponent("hider-material", {
  init: function () {
    this.el.addEventListener('model-loaded', this.update.bind(this));
  },
  update: function () {
    this.el.object3D.traverse(function (child) {
      if (child.isMesh) {
        child.material.colorWrite = false;
      }
    });
  }
});
```

<figure><img src="https://content.gitbook.com/content/MNoA4M7dz4DXv12sDZUk/blobs/6TdoBm4JZJ22eHLGJYNf/Portal_Hide_Backside.gif" alt=""><figcaption><p>Hide Portal Backside</p></figcaption></figure>

## **Step 7: Start the HTTPS Server** <a href="#h_01fhdhbqbcfa6cpy58as6dbzbd" id="h_01fhdhbqbcfa6cpy58as6dbzbd"></a>

* Configure the Live Server VSC Extension settings.json to setup a self-signed HTTPS server.
* For more details, refer the documentation on [Develop Locally.](https://docs.blippar.com/webar-sdk/v.1.7.1/publish-your-creation/develop-locally)
* Click Go Live to start the HTTPS Server
* Load the HTTPS server url in your mobile browser to start the WebAR portal experience

## **Step 8: Full Source Code** <a href="#h_01fhsmdcf5ttqa3n58ynf47rp6" id="h_01fhsmdcf5ttqa3n58ynf47rp6"></a>

The full source code of this Portal WebAR example is available in the [Github repo here.](https://github.com/blippar/webar-sdk-example/tree/main/aframe/portal-webar)

## **Resulting Portal WebAR Experience** <a href="#h_01fhdhbxr43xnrtq5nc20t9he0" id="h_01fhdhbxr43xnrtq5nc20t9he0"></a>

<figure><img src="https://content.gitbook.com/content/MNoA4M7dz4DXv12sDZUk/blobs/Px5p1TCmHMCg3VQs3bsG/final_616dbf2d3575b4009b53a5aa_65801%20(1).gif" alt=""><figcaption><p>Resulting Portal WebAR Experience</p></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.blippar.com/webar-sdk/v.1.7.1/integrations/a-frame-integration/build-an-advanced-portal-experience.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
