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.
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;
We will create the Follower
objects that move organically in a spherical flow field. Perform the following steps to do so:
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; };
Vec3f sphericalToCartesians(sphericalToCartesians( float radius, float theta, float phi );
float x = radius * sinf( theta ) * cosf( phi ); float y = radius * sinf( theta ) * sinf( phi ); float z = radius * cosf( theta ); return Vec3f( x, y, z );
vector<shared_ptr< Follower > > mFollowers; float mRadius; float mCounter; Perlin mPerlin;
setup
method we will begin by initializing mRadius
and mCounter
:mRadius = 200.0f; mCounter = 0.0f;
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 ); }
update
method we will animate our objects. Let's start by incrementing mCounter
.mCounter += 0.01f;
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 ); }
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();
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();
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.
3.145.191.134