Creating a spherical flow field with Perlin noise

In this recipe we will learn how to use Perlin noise with a spherical flow field and animate objects in an organic way around a sphere.

We will animate our objects using spherical coordinates and then transform them into Cartesian coordinates in order to draw them.

Getting ready

Add the necessary files to use Perlin noise and draw with OpenGL:

#include "cinder/gl/gl.h"
#include "cinder/Perlin.h"
#include "cinder/Rand.h"

Add the following useful using statements:

using namespace ci;
using namespace ci::app;
using namespace std;

How to do it…

We will create the Follower objects that move organically in a spherical flow field. Perform the following steps to do so:

  1. We will start by creating a Follower class representing an object that will follow the spherical flow field.

    Add the following code snippet before the application's class declaration:

    class Follower{
    public:
    Follower(){
     theta = 0.0f;
     phi = 0.0f;
    }
    void moveTo( const Vec3f& target ){
     prevPos = pos;
     pos += ( target - pos ) * 0.1f;
    }
    void draw(){
     gl::drawSphere( pos, 10.0f, 20 );
     Vec3f vel = pos - prevPos;
     gl::drawLine( pos, pos + ( vel * 5.0f ) );
    }
    Vec3f pos, prevPos;
    float phi, theta;
    };
  2. We will be using spherical to Cartesian coordinates, so declare the following method in the application's class:
    Vec3f sphericalToCartesians(sphericalToCartesians( float radius, float theta, float phi );
  3. The implementation of this method is as follows:
    float x = radius * sinf( theta ) * cosf( phi );
    float y = radius * sinf( theta ) * sinf( phi );
    float z = radius * cosf( theta );
    return Vec3f( x, y, z );
  4. Declare the following members in the application's class:
    vector<shared_ptr< Follower > > mFollowers;
    float mRadius;
    float mCounter;
    Perlin mPerlin;
  5. In the setup method we will begin by initializing mRadius and mCounter:
    mRadius = 200.0f;
    mCounter = 0.0f;
  6. Now let's create 100 followers and add them to mFollowers. We will also attribute random values to the phi and theta variables of the Follower objects and set their initial positions:
    int numFollowers = 100;
    for( int i=0; i<numFollowers; i++ ){
     shared_ptr<Follower> follower( new Follower() );
     follower->theta = randFloat( M_PI * 2.0f );
     follower->phi = randFloat( M_PI * 2.0f );
     follower->pos = sphericalToCartesian( mRadius, 
      follower->theta, follower->phi );
     mFollowers.push_back( follower );
    }
  7. In the update method we will animate our objects. Let's start by incrementing mCounter.
    mCounter += 0.01f;
  8. Now we will iterate over all the objects in mFollowers and use Perlin noise, based on the follower's position, to calculate how much it should move on spherical coordinates. We will then calculate the correspondent Cartesian coordinates and move the object.

    Add the following code snippet inside the update method:

    float resolution = 0.01f;
    for( int i=0; i<mFollowers.size(); i++ ){
     shared_ptr<Follower> follower = mFollowers[i];
     Vec3f pos = follower->pos;
     float thetaAdd = mPerlin.noise( pos.x * resolution, 
      pos.y * resolution, mCounter ) * 0.1f;
     float phiAdd = mPerlin.noise( pos.y * resolution, 
      pos.z * resolution, mCounter ) * 0.1f;
     follower->theta += thetaAdd;
     follower->phi += phiAdd;
     Vec3f targetPos = sphericalToCartesian( mRadius, 
      follower->theta, follower->phi );
     follower->moveTo( targetPos );
    }
  9. Let's move to the draw method and begin by clearing the background, setting the windows matrices, and enabling reading and writing in the depth buffer.
    gl::clear( Color( 0, 0, 0 ) ); 
    gl::setMatricesWindowPersp( getWindowWidth(), getWindowHeight() );
    gl::enableDepthRead();
    gl::enableDepthWrite();
  10. Since the followers are moving around the origin, we will draw them translated to the origin using a dark gray color. We will also draw a white sphere to get a better understanding of the movement.
    gl::pushMatrices();
    Vec2f center = getWindowCenter();
    gl::translate( center );
    gl::color( Color( 0.2f, 0.2f, 0.2f ) );
    for(vector<shared_ptr<Follower> >::iterator it = 
     mFollowers.begin(); it != mFollowers.end(); ++it ){
     (*it)->draw();
    }
    gl::color( Color::white() );
    gl::drawSphere( Vec3f(), mRadius, 100 );
    gl::popMatrices();

How it works...

We use Perlin noise to calculate the change in the theta and phi members of the Follower objects. We use these, together with mRadius, to calculate the position of the objects using the standard spherical to Cartesian coordinate transformation. Since Perlin noise gives coherent values based on coordinates by using the current position of the Follower objects, we get the equivalent of a flow field. The mCounter variable is used to animate the flow field in the third dimension.

How it works...

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.191.134