Simple 3D drawing

For simple 3D drawing in openFrameworks, follow these steps:

  1. Add the ofEnableDepthTest() function call in the beginning of the testApp::draw() function to enable z-buffering. If you omit it, all the graphics objects will be rendered without respect to their z coordinate in correspondence with the graphical primitives' rendering order.
  2. Draw primitives as follows:
    • The ofLine( x1, y1, z1, x2, y2, z2 ) function draws a line segment between points (x1, y1, z1) and (x2, y2, z2). There is an overloaded version of the function, ofLine( p1, p2 ), where p1 and p2 have type ofPoint. Use the ofSetColor() and ofSetLineWidth() functions to adjust its rendering properties of color and line width.

      Note

      In Chapter 2, Drawing in 2D, we used the ofPoint class to represent 2D points using its fields x and y. Actually, ofPoint has a third field z, which, by default, is equal to zero. So ofPoint can represent points in 3D. Just declare ofPoint p and work with values p.x, p.y, and p.z.

    • The ofTriangle( p1, p2, p3 ) function draws a triangle with vertices in points p1, p2, and p3. Use the ofSetColor(), ofFill(), ofSetLineWidth(), and ofNoFill() functions to adjust its rendering properties.
    • The ofRect( x, y, z, w, h ) function draws a rectangle with the top-left corner at (x, y, z) and the width w and height h, oriented parallel to the screen plane. If you need to get a rotated rectangle, you need to rotate the coordinate system using the ofRotate() function.
      ofBeginShape();                  //Begin shape
      ofVertex( x1, y1, z1 );          //The first vertex
      ofVertex( x2, y2, z2 );          //The second vertex
      //...
      ofVertex( xn, yn, zn );          //The last vertex
      ofEndShape();                    //End shape

    To draw arbitrary polygons—for example, quadrangles—use the following method:

    If ofFill() was called before drawing, the shape will be drawn filled and closed. If ofNoFill() was called before drawing, just an unclosed polygon will be drawn.

  3. Translate, scale, and rotate the rendered objects by manipulating the coordinate system:
    • The ofTranslate( x, y, z ) function translates the coordinate system by vector (x, y, z)
    • The ofScale( x, y, z ) function scales the coordinate system by factors (x, y, z)
    • The ofRotate( angle, x, y, z ) function rotates the coordinate system along vector (x, y, z) by angle degrees

As in a 2D case, use ofPushMatrix() and ofPopMatrix() to store and retrieve the current coordinate system in a matrix stack.

Now we will illustrate these steps in an example.

The triangles cloud example

Let's draw 1500 random triangles, located at an equal distance from the center of the coordinates. This will look like a triangle cloud in the shape of a sphere. To make the visualization more interesting, colorize the triangles with random colors from black to red and add constant rotation to the cloud.

Note

This is example 07-3D/01-TrianglesCloud.

The example is based on the emptyExample project in openFrameworks. In the testApp.h file, inside the testApp class declaration, add arrays vertices and colors to hold the vertices and the colors of the triangles and variables nTri and nVert corresponding to the number of triangles and their vertices:

vector<ofPoint> vertices;
vector<ofColor> colors;
int nTri;	      //The number of triangles
int nVert;      //The number of the vertices equals nTri * 3

The setup() function fills the arrays for the triangles' vertices and colors. The vertices of the first triangle are stored in vertices[0], vertices[1], and vertices[2]. The vertices of the second triangle are stored in vertices[3], vertices[4], vertices[5], and so on. In general, the vertices of the triangle with index i (where i is in range from 0 to N-1) are stored in the vertices with the indices i * 3, i * 3 + 1, and i * 3 + 2.

void testApp::setup() {
  nTri = 1500;        //The number of the triangles
  nVert= nTri * 3;    //The number of the vertices

  float Rad = 250;    //The sphere's radius
  float rad = 25;     //Maximal triangle's "radius"
                      //(formally, it's the maximal coordinates'
                      //deviation from the triangle's center)

  //Fill the vertices array
  vertices.resize( nVert );        //Set the array size
  for (int i=0; i<nTri; i++) {     //Scan all the triangles
      //Generate the center of the triangle
      //as a random point on the sphere

      //Take the random point from
      //cube [-1,1]x[-1,1]x[-1,1]	
      ofPoint center( ofRandom( -1, 1 ),
                 ofRandom( -1, 1 ),
                 ofRandom( -1, 1 ) );
      center.normalize(); //Normalize vector's length to 1
      center *= Rad;      //Now the center vector has
                             //length Rad

      //Generate the triangle's vertices
      //as the center plus random point from
      //[-rad, rad]x[-rad, rad]x[-rad, rad]
      for (int j=0; j<3; j++) {
          vertices[ i*3 + j ] =
                      center + ofPoint( ofRandom( -rad, rad ),
                                  ofRandom( -rad, rad ),  
                                  ofRandom( -rad, rad ) );
      }
  }

  //Fill the array of triangles' colors
  colors.resize( nTri );
  for (int i=0; i<nTri; i++) {
      //Take a random color from black to red
      colors[i] = ofColor( ofRandom( 0, 255 ), 0, 0 );
  }
}

The update() function is empty here, and the draw() function enables z-buffering, which rotates the coordinate system based on time, and draws the triangles with the specified colors.

void testApp::draw(){
  ofEnableDepthTest();    //Enable z-buffering

  //Set a gradient background from white to gray
  //for adding an illusion of visual depth to the scene
  ofBackgroundGradient( ofColor( 255 ), ofColor( 128 ) );

  ofPushMatrix();    //Store the coordinate system

  //Move the coordinate center to screen's center
  ofTranslate( ofGetWidth()/2, ofGetHeight()/2, 0 );

  //Calculate the rotation angle
  float time = ofGetElapsedTimef();    //Get time in seconds
  float angle = time * 10; //Compute angle. We rotate at speed
                           //10 degrees per second
  ofRotate( angle, 0, 1, 0 );    //Rotate the coordinate system
                                 //along y-axe
  //Draw the triangles
  for (int i=0; i<nTri; i++) {
      ofSetColor( colors[i] );	//Set color
      ofTriangle( vertices[ i*3 ],
                     vertices[ i*3 + 1 ],
                     vertices[ i*3 + 2 ] ); //Draw triangle
  }

  ofPopMatrix();    //Restore the coordinate system
}

Run the code and you will see a sphere-like rotating cloud of triangles as shown in the following screenshot:

The triangles cloud example

To draw the background, we use the ofBackgroundGradient( color1, color2, type ) function. It creates the gradient filling of type type for the entire application's screen, with colors interpolated from color1 to color2. The possible values of type are as follows:

  • OF_GRADIENT_CIRCULAR – This type gives a circular color gradient with the center being the center of screen. This is the default value.
  • OF_GRADIENT_LINEAR – This type gives you a top-to-bottom gradient.
  • OF_GRADIENT_BAR – This type gives you a center-to-top and a center-to-bottom gradient.

Note that each triangle moves and rotates on the screen but its color always remains unchanged. The reason for this is that we don't use light and normals, which control how a graphics primitive is lit and shaded.

The simplest way to add lighting and normals is using the ofMesh class, which we will consider now.

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

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