February 19, 2019

Getting Started with VR in React Native with ViroReact

by Wern Ancheta

Getting Started with VR in React Native with ViroReact

VR and AR are very promising technologies that, when implemented well, can improve how we experience things both in the real and virtual world. From providing immersive experiences to aid in entertainment and gaming, to performing healthcare simulations in a virtual environment.

Thankfully for us React Native developers, this technology was made more accessible by platforms such as ViroReact. This platform allows us to easily implement VR and AR in a React Native app.

In this tutorial, we’ll take a look at how you can use it to create a simple VR app. You can find the code for this tutorial on this GitHub repo.

Prerequisites

Basic knowledge of React Native is required. Your machine should also be set up for plain React Native development (not Expo).

If you want to use ViroReact for iOS, make sure you have the latest version of Xcode installed and that your manifest is updated with the latest package versions (by running pod repo update).

If you want to use it for Android, be sure that you have the following installed for the SDK platforms:

SDK platforms

And the following for SDK tools:

SDK tools

The rest is a standard requirement for React Native, so be sure to check it out.

Since we’ll be working with VR, I recommend you to get your own Google Cardboard. You also need to install the Google Cardboard app. It will help you optimize your phone’s display for the specific viewer you’re using.

Lastly, you’ll need to sign up for a Viro Media API key. Just fill up the form and they will send the API key that you can use on the app we’ll be creating later.

Basic concepts

ViroReact uses ARCore and ARKit behind the scenes. ARCore is the AR development platform for Android, while ARKit is the iOS equivalent. Both use the device’s accelerometer and gyroscope to perform the environment mapping as the device is moved. This allows for a truly immersive experience as if the user is navigating the real world.

Do note that ARCore and ARKit have limited support for devices:

But since we’re working with VR, the requirements are a bit forgiving. If you have a pretty recent device (released within the past 3-5 years), then you should be good.

ViroReact has two main components:

  • React components for VR and AR development - ViroReact comes with a number of custom React components that allow you to render different scenes, objects, and controls in a 3D environment.
  • High-performance native 3D rendering engine - ViroReact renders all objects with native device hardware acceleration. This means that the performance is actually good even though we’re using React Native.

App overview

The app that we’ll be creating is a VR app which displays a park as a background, an image, and a text. We’ll also be animating the image so it becomes larger. Here’s what it looks like:

VR app

Setting up the app

The first thing that you need to do is install the Viro CLI tool. This will allow us to easily generate a React Native project which has all the dependencies required for running VR/AR apps:

npm install -g react-viro-cli

Next, create a new project:

react-viro init RNViro --verbose

Once the project is created, navigate inside the RNViro directory and run ./setup-ide.sh:

cd RNViro
./setup-ide.sh --android

The option can either be --ios, --android, or --all.

Once that’s done, you can actually run the app. The Viro CLI tool already includes a sample app when you bootstrap a new project. You can run the app via the Viro Media app (iOS, Android):

npm start

Or via standard means:

react-native run-android --variant=gvrDebug

Note that we need to supply the --variant option of gvrDebug. This builds the debug version of the app which uses settings that are optimal for VR.

Building the app

Now we’re ready to build the app. As you can see, there’s already a “Hello World” sample app pre-created for you. All we have to do is edit it to accomplish what we want.

The first thing that we want to do is supply our API key because the app won’t run if we don’t supply it:

// App.js
var sharedProps = {
  apiKey:"YOUR VIRO MEDIA API KEY",
}

At this point, you can run the app on your device and look around. Be sure to enable live reload so the changes get reflected immediately every time you save.

Edit the code once you have an idea of what’s going on, and how the sample app works. We know that it first lets you choose the experience you want (whether VR or AR). But we want to get straight to VR, so we update the render method to immediately return the VRNavigator:

render() {
  return this._getVRNavigator(); // replace everything inside render() with this
}

This renders the scene navigator for the VR experience. The scene navigator is the entry point of all Viro apps. There are only two types, ViroVRSceneNavigator (for VR) and ViroARSceneNavigator (for AR):

// App.js
import {
  ViroVRSceneNavigator
} from 'react-viro';

Going back to the _getVRNavigator() function, it simply renders the ViroVRSceneNavigator component. This requires the API key and the initialScene to be passed to it:

// App.js
_getVRNavigator() {
  <ViroVRSceneNavigator 
{...this.state.sharedProps}
initialScene={{scene: InitialVRScene}}/>
}

The initialScene is what’s going to be initially displayed by this scene navigator. Think of scenes as pages of the app. If you’ve previously used React Navigation for navigating between the pages of your app, the idea is the same. The scene that we’re using is in a different file which we have imported right below the declaration of the API key. This is where the main “meat” of the app we’ll be creating is at:

var InitialVRScene = require('./js/HelloWorldScene');

Open js/HelloWorldScene.js. First, import all the Viro components that we will be using. You’ll learn what each one does as we move along:

import {
  ViroScene,
  ViroText,
  ViroImage,
  Viro360Image,
  ViroAnimations
} from "react-viro";

Here’s the render method:

render() {
    return (
      <ViroScene>
        <Viro360Image source={require("./res/360_park.jpg")} />
        <ViroImage
          height={1}
          width={1}
          position={[0, 0, -4]}
          source={require("./res/logo-jscrambler.jpeg")}
          animation={{ name: "animateImage", run: this.state.runAnimateImage }}
        />
        <ViroText
          text="Protect the client side!"
          width={6}
          height={5}
          position={[0, 0, -2]}
          style={styles.textStyle}
          outerStroke={{ type: "Outline", width: 8, color: "#333" }}
        />
      </ViroScene>
    );
  }

Now let’s break down the code above. In ViroReact, every scene needs to have a top-most component which wraps everything inside of it. ViroScene is one such component. It allows for the construction of a 3D environment so we can use components like the Viro360Image.

As the name suggests, the Viro360Image component is used for displaying a 360-degree photosphere. This uses the device’s gyroscope to display a specific part of the image based on the current orientation of the device:

    <Viro360Image source={require("./res/360_park.jpg")} />

The image we’re using above was downloaded from the ViroReact website. Simply choose from the available Photospheres and download it inside the js/res folder of the project. We’re specifically using the park image. You can also Google for “360 degree image”, and you’ll find lots of images that you can use with this component.

Next is the ViroImage component. This allows you to display plain old image files. Here we’re using it to display the Jscrambler logo. But, unlike the Viro360Image, we have to supply a few props in order for it to display properly:

<ViroImage
height={1}
width={1}
position={[0, 0, -4]}
source={require("./res/logo-jscrambler.jpeg")}
animation={{ name: "animateImage", run: this.state.runAnimateImage }}
/>

Here’s what each prop does:

  • height and width - the height and width of the image in 3D space. The default value for this is 1 which basically means it will render the full size of the image. 2 means it will double, and so on. It’s best to resize the image in the size you wish to display it beforehand, and then preview it to make sure it’s not pixelated.
  • position - the cartesian position of the image in 3D space (x, y, and z positions). We’re used to the x and y positions, which represent the first and second values. But, in a 3D environment, there’s also a z dimension. This represents how near or far the image within the viewing range. 0 means it’s just at the center of the viewing range, so -4 means it’s way back. This makes the user perceive the image as being far. Supplying a number that’s greater than 0 will make the image appear as if it was right in front of the user’s face. One thing that you need to remember about positioning is that 0 means center.
  • source - this is the same as the default React Native Image component. So you can either specify a local asset or a URL. In this case, it’s a local asset.
  • animation - the animation you want to perform to the image. This accepts an object containing the name of the animation, and boolean value run for specifying whether to run the animation or not. In this case, we’re using a value from the state to control when exactly it will animate. Later on, you’ll learn how we actually declare the name of the animation.

Next is the ViroText component. Just like the Text component in React Native, this is used to display text. Here, we’re supplying the same dimensions and positioning props that we used on the ViroImage component. The only thing that might not be intuitive is the outerStroke. This allows you to put a background fill to the text so that it’s still pretty legible even though you have a very busy background (the 360-degree image):

<ViroText
text="Protect the client side!"
width={6}
height={5}
position={[0, 0, -2]}
style={styles.textStyle}
outerStroke={{ type: "Outline", width: 8, color: "#333" }}
/>

Now that we’re done walking through the different components, let’s add the one thing that we’re still missing: animation. Animations can be added to any component that’s displayed on the screen. Examples include the ViroImage and ViroText component.

Using animations in ViroReact is a two-step process. First, you have to register the animation; that is, give it a name and specify the animation itself. The animation to perform is specified under the properties property.

Here, we’re doing a scale animation. This scales the x (horizontal) and y (vertical) dimensions of the image. As you can imagine, the values we specified will elongate the image horizontally until it becomes twice its original width. While doing that, we also make the image smaller in height so it becomes 80% its original height.

easing is the progression of the animation. EaseIn means that the animation will begin slowly and then speed up as it progresses. Lastly, the duration is how long the animation will take in milliseconds. In this case, we’re animating it over the course of 3 seconds:

ViroAnimations.registerAnimations({
    animateImage: {
      properties: { scaleX: 2, scaleY: 0.8 },
      easing: "EaseIn",
      duration: 3000
    }
  });

Next, initialize the boolean value for controlling the start of the animation:

constructor() {
    super();
    this.state = {
      runAnimateImage: false
    }; 
}

To actually run the animation, we update the state to true:

componentDidMount() {
    setTimeout(() => {
      this.setState({ runAnimateImage: true });
    }, 5000);
}

Lastly, we add the styles:

var styles = StyleSheet.create({
    textStyle: {
        fontFamily: "Arial",
        fontSize: 20,
        color: "#ffffff",
        textAlignVertical: "center",
        textAlign: "center"
    }
});

Here’s what it will look like once you run the app:

VR app

Conclusion

That’s it! In this tutorial, you learned the basics of using ViroReact. Specifically, you learned how to create a basic VR application which displays a scene, an image, and a text.

ViroReact is a very promising solution for creating VR and AR apps in React Native. It allows you to develop VR/AR apps faster because of React Native’s fast live reload feature. Aside from that, you don’t have to care about the differences in implementation between ARCore and ARKit because ViroReact already provides you with components you can use to create fast and truly immersive VR and AR apps.

Lastly, if you're building React Native apps with sensitive logic, be sure to protect them against code theft and reverse-engineering by following our guide.