DEV Community

Cover image for An Introduction to Augmented Reality with Viro
Peter Klingelhofer
Peter Klingelhofer

Posted on • Edited on

An Introduction to Augmented Reality with Viro

Introduction

Augmented Reality (AR) is a tech used to superimpose computer-generated images on a user's view of the real world, providing a composite view. This can be viewable on a simple phone screen (think Pokemon Go) or with smart phone based Virtual Reality headsets.

Since Expo's AR component is deprecated, Viro Media have taken charge as one of the fastest, easiest ways to get augmented reality up and running on your iOS or Android device. Their documentation is impressively detailed and helpful.

Setup

If you're in OSX:

brew install node
brew install watchman
Enter fullscreen mode Exit fullscreen mode

If you're in Linux:

apt-get install node
apt-get install watchman
Enter fullscreen mode Exit fullscreen mode

For all distributions:

npm install -g react-native-cli
npm install -g react-viro-cli
react-viro init ViroSample --verbose
cd ViroSample
npm start
Enter fullscreen mode Exit fullscreen mode

Now within the /js/HelloWorldSceneAR.js file, change line 38 so that "Hello World!" is a message of your choosing:

  _onInitialized(state, reason) {
    if (state == ViroConstants.TRACKING_NORMAL) {
      this.setState({
        text : "Happy Birthday" // was "Hello World"
      });
    } else if (state == ViroConstants.TRACKING_NONE) {
      // Handle loss of tracking
    }
  }
Enter fullscreen mode Exit fullscreen mode

Then get the iOS Viro Media App or the Android Viro Media App
Open the Viro Media App, and press the icon in the top left, and then press "Enter Testbed". Scroll up in your Terminal to find your ngrok url, enter that into the text field, and press Go. You should see your message displayed on the scene!

Happy Birthday text in front of a plant

Happy Birthday text in front of a plant, side view

3D Objects

Now that you've seen the message displayed on your phone, download this file, unzip it, and place it in your /ViroSample/js/ folder.

Now, replace the contents of your HelloWorldSceneAR.js with this code from the docs:

'use strict';

import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import {
  ViroARScene,
  ViroText,
  ViroMaterials,
  ViroBox,
  Viro3DObject,
  ViroAmbientLight,
  ViroSpotLight,
  ViroARPlaneSelector,
  ViroNode,
  ViroAnimations,
} from 'react-viro';
const createReactClass = require('create-react-class');
const HelloWorldSceneAR = createReactClass({
  getInitialState() {
    return {
      text : "Initializing AR..."
    };
  },

  render: function() {
    return (
      <ViroARScene onTrackingInitialized={()=>{this.setState({text : "Happy Birthday"})}}> // Again, change this text to your desired message
        <ViroText text={this.state.text} scale={[.1, .1, .1]} height={1} width={4} position={[0, .5, -1]} style={styles.helloWorldTextStyle} />

        <ViroAmbientLight color={"#aaaaaa"} />
        <ViroSpotLight innerAngle={5} outerAngle={90} direction={[0,-1,-.2]} position={[0, 3, 1]} color="#ffffff" castsShadow={true} />

          <Viro3DObject
            source={require('./res/emoji_smile/emoji_smile.vrx')}
            position={[0, 0, -1]}
            scale={[.2, .2, .2]}
            type="VRX"
            dragType="FixedDistance" onDrag={()=>{}} // allows user to drag 3D object around with finger
          />

      </ViroARScene>
    );
  },
});

const styles = StyleSheet.create({
  helloWorldTextStyle: {
    fontFamily: 'Arial',
    fontSize: 50,
    color: '#ffffff',
    textAlignVertical: 'center',
    textAlign: 'center',
  },
});

module.exports = HelloWorldSceneAR;
Enter fullscreen mode Exit fullscreen mode

You should see a 3D smiley face beneath your message, like so:

Hovering smiley face

Hovering smiley face beneath Happy Birthday text from side

Plane Detection

Now, on line 38, add

<ViroARPlaneSelector />

and reload. Save and reload test bed. You should see multiple planes detected as you move your device over different flat surfaces:
Plane detected on floor

Now, replace

jsx <ViroARPlaneSelector />

with

<ViroARPlaneSelector>
  <Viro3DObject
    source={require('./res/emoji_smile/emoji_smile.vrx')}
    position={[0, .1, 0]}
    scale={[.2, .2, .2]}
    type="VRX"
    dragType="FixedDistance" onDrag={()=>{}} />
</ViroARPlaneSelector>
Enter fullscreen mode Exit fullscreen mode

Save, and load up another test bed. Now, when you tap a plane with your finger, all planes should disappear and a 3D object should be generated on the plane that was tapped.

Plane detected on floor

Tapped plane produced floating 3D smiley face

Now replace

<ViroARPlaneSelector>
  <Viro3DObject
    source={require('./res/emoji_smile/emoji_smile.vrx')}
    position={[0, .1, 0]}
    scale={[.2, .2, .2]}
    type="VRX"
    dragType="FixedDistance" onDrag={()=>{}} />
</ViroARPlaneSelector>
Enter fullscreen mode Exit fullscreen mode

with

        <ViroNode
          position={[0, 0, -1]}
          dragType="FixedToWorld"
          onDrag={() => {}}
        >
          <Viro3DObject
            source={require('./res/emoji_smile/emoji_smile.vrx')}
            position={[0, 0.1, 0]}
            scale={[0.2, 0.2, 0.2]}
            type="VRX"
          />
        </ViroNode>
Enter fullscreen mode Exit fullscreen mode

Save, and load up another test bed. Note how dragging a 3D object around moves along detected places/surfaces.
Smiley on floor

Dragged/Moved smiley on floor

Particles

Now, let's add particles!

First, download the particles res.zip file, unzip it, and add the unzipped contents to your /ViroSample/js/res/.

Now, replace the code in HelloWorldSceneAR.js with the following:

'use strict';

import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import PropTypes from 'prop-types';

import {
  ViroSceneNavigator,
  ViroARScene,
  ViroNode,
  ViroAmbientLight,
  ViroDirectionalLight,
  ViroText,
  ViroAnimations,
  ViroParticleEmitter,
  Viro3DObject,
} from 'react-viro';

'use strict';
const createReactClass = require('create-react-class');
const MainScene = createReactClass({

  getInitialState() {
    return {
    };
  },

  render: function() {
    return (
     <ViroARScene>
      <ViroAmbientLight color={"#aaaaaa"} />
      <ViroDirectionalLight color="#ffffff" direction={[0,-1,-.2]}/>

      <ViroNode position={[0,-.5,-1]} scale={[.5,.5,.5]} dragType="FixedToWorld" onDrag={()=>{}}>
         <Viro3DObject
           source={require('./res/object_bday_cake/object_bday_cake.vrx')}
           type="VRX"
         />

         <ViroNode position={[0.18,.67,0.004]} scale={[.4,.4,.4]}>
             <ViroParticleEmitter
               duration={1200}
               visible={true}
               run={true}
               loop={true}
               fixedToEmitter={false}

               image={{
                 source:require("./res/particle_fire.png"),
                 height:0.3,
                 width:0.3,
                 bloomThreshold:0.0
               }}

               spawnBehavior={{
                 particleLifetime:[500,500],
                 emissionRatePerSecond:[30, 40],
                 maxParticles:800
               }}

               particleAppearance={{
                 opacity:{
                   initialRange:[0.2, 0.2],
                   factor:"time",
                   interpolation:[
                     {endValue:0.2, interval:[0,200]},
                     {endValue:0.0, interval:[200,500]},
                   ]
                 },
                 scale:{
                   initialRange:[[1,1,1], [1,1,1]],
                   factor:"time",
                   interpolation:[
                     {endValue:[0,0,0], interval:[150,500]},
                   ]
                 },

               }}

               particlePhysics={{
                 velocity:{initialRange:[[0,.3,0], [0,.5,0]]}
               }}
             />
          </ViroNode>
        </ViroNode>
     </ViroARScene>
    );
  },
});

module.exports = MainScene;
Enter fullscreen mode Exit fullscreen mode

Save, run npm start oncemore, re-open your new test bed, and voila! You will see a birthday cake with an animated flame at the top of the candle.

Floating birthday cake with flame

Floating birthday cake with flame zoomed in on flame

Now, let's add animated smoke to an object. Once more, replace the code in HelloWorldSceneAR.js with the following:

'use strict';

import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import PropTypes from 'prop-types';

import {
  ViroSceneNavigator,
  ViroARScene,
  ViroNode,
  ViroAmbientLight,
  ViroDirectionalLight,
  ViroText,
  ViroAnimations,
  ViroParticleEmitter,
  Viro3DObject,
} from 'react-viro';

'use strict';
const createReactClass = require('create-react-class');
const MainScene = createReactClass({

  getInitialState() {
    return {
    };
  },

  render: function() {
    return (
     <ViroARScene>
      <ViroAmbientLight color={"#aaaaaa"} />
      <ViroDirectionalLight color="#ffffff" direction={[0,-1,-.2]}/>

      <ViroNode position={[0, 0, -2]} scale={[.5, .5, .5]} dragType="FixedToWorld" onDrag={()=>{}}>
        <Viro3DObject
          source={require('./res/emoji_angry_anim/emoji_angry_anim.vrx')}
          resources={[require('./res/emoji_angry_anim/emoji_angry_diffuse.png'),
                      require('./res/emoji_angry_anim/emoji_angry_normal.png'),
                      require('./res/emoji_angry_anim/emoji_angry_specular.png')]}
          type="VRX"
          animation={{name:"02", run:true, loop:true,}}
          />
          <ViroParticleEmitter
              position={[-.6, 0, .2]}
              scale={[.4, .2, .2]}
              duration={1100}
              delay={1100}
              visible={true}
              run={true}
              loop={true}
              fixedToEmitter={true}

              image={{
                source:require("./res/particle_smoke.png"),
                height:1,
                width:1,
              }}

              spawnBehavior={{
                particleLifetime:[500,500],
                emissionRatePerSecond:[200,200],
                maxParticles:200,
                spawnVolume:{
                  shape:"box",
                  params:[.7, .1, .1],
                  spawnOnSurface:false
                },
              }}

              particleAppearance={{
                opacity:{
                  initialRange:[0.0, 0.0],
                  interpolation:[
                    {endValue:0.4, interval:[0,200]},
                    {endValue:0.0, interval:[900,1500]}
                  ]
                },
              }}

              particlePhysics={{
                velocity:{initialRange:[[-2,2,0], [-2,-2,0]]},
                acceleration:{initialRange:[[0,0,0], [0,0,0]]}
              }}
          />

          <ViroParticleEmitter
              position={[.6, 0, .2]}
              scale={[.4, .2, .2]}
              duration={1100}
              delay={1100}
              visible={true}
              run={true}
              loop={true}
              fixedToEmitter={true}

              image={{
                source:require("./res/particle_smoke.png"),
                height:1,
                width:1,
              }}

              spawnBehavior={{
                particleLifetime:[500,500],
                emissionRatePerSecond:[200,200],
                maxParticles:200,
                spawnVolume:{
                  shape:"box",
                  params:[.7, .1, .1],
                  spawnOnSurface:false
                },
              }}

              particleAppearance={{
                opacity:{
                  initialRange:[0.0, 0.0],
                  interpolation:[
                    {endValue:0.4, interval:[0,200]},
                    {endValue:0.0, interval:[900,1500]}
                  ]
                },
              }}
              particlePhysics={{
                velocity:{initialRange:[[2,2,0], [2,-2,0]]},
                acceleration:{initialRange:[[0,0,0], [0,0,0]]}
              }}
          />
      </ViroNode>
     </ViroARScene>
    );
  },
});

module.exports = MainScene;
Enter fullscreen mode Exit fullscreen mode

Angry smiley face with smoke coming out of both ears

Tweaking Parameters

Save, run npm start, and you should see an angry smiley face with smoke coming out of its ears intermittently.

Now, let's mess with a few settings. On line 88, if we change the scale parameter from its initial value of [.4, .2, .2] to [.8, .4, .4], we can achieve more smoke coming out of the right side than the left:

Smiley more smoke on right side

If we change the duration parameter on line 89, using a value of 2200 instead of 1100, the smoke on the right side will be emitted for double the duration of the left side, so now they both are emitted at the same time, the right lasts twice as long as the left, and the left starts again just as the right stops. Then, when the left stops, the right starts. Thus, they alternate between being emitted concurrently and starting another emission as the other side ceases.

Smiley smoke just on right side

Smiley smoke both sides

Smiley smoke just on left side

Now, on line 104, change emissionRatePerSecond:[200,200], to emissionRatePerSecond:[800,800], and on line 105, change maxParticles:200, to maxParticles:800,. Now, the smoke coming out of the right side is significantly thicker and more filled in than that of the left: four times as many particles!

Smiley more smoke on right side than left

Now, on line 97, let's change source:require("./res/particle_smoke.png"),
to source:require("./res/particles_flame.png"),
. You will now see particles more reminiscent of fire coming out of the right side, whereas the left side remains smokey.

Smiley with smoke coming out of left side and fire coming out of the right

If you want to change the left side, just change the parameters within the <ViroParticleEmitter></ViroParticleEmitter> tags above, between lines 43-84.

Conclusion

ViroReact is a fast an easy way to set up AR on your smart phone. Today we went through initial setup, displaying text, displaying 3D objects, displaying particles, and changing the way the particles behave. For further customization, I recommend playing around with the image files in the /res folder with a photo editor such as Adobe Photoshop. For added creative inspiration, try adding portals to your AR project.

Top comments (0)