If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Main content

Drawing 3D shapes

So now that we have a representation of our cube, we need to find a way to draw it.
To draw a 3D shape on a 2D surface, we need to "project" it. Imagine shining a light behind the cube and projecting its shadow onto a screen. The simplest form of projection is an orthogonal projection, which is what you would get if the beams of light were parallel to one another.
All this talk of projections might sound complicated, but it is very easy to implement: we just ignore the z-coordinates when drawing.

Setting things up

I like to create variables at the top of my programs to control how things are displayed, so I can easily change them later. Here are some variables we will want shortly; feel free to change the values.
var backgroundColor = color(255, 255, 255);
var nodeColor = color(40, 168, 107);
var edgeColor = color(34, 68, 204);
var nodeSize = 8;
Now we add a basic draw function:
draw = function() { 
    background(backgroundColor);
};
We will also need to add the following to our code:
translate(200, 200);
This shifts our canvas 200 pixels right and 200 pixels down, so the pixel at position (0, 0), is now in the center of our canvas. This means that our cube will be drawn in the center of the screen. The reason for doing things this way will become clear when we start to rotate our objects.

Drawing nodes

Inside the draw function we loop through all the nodes and draw an ellipse at the (x, y) coordinate of that node:
fill(nodeColor);
noStroke();
for (var n = 0; n < nodes.length; n++) {
   var node = nodes[n];
   ellipse(node[0], node[1], nodeSize, nodeSize);
}

Drawing edges

Also inside the draw function we add the code for drawing edges. I would add it before the code for drawing nodes, so the nodes are drawn on top of the edges.
stroke(edgeColor);
for (var e = 0; e < edges.length; e++) {
   var n0 = edges[e][0];
   var n1 = edges[e][1];
   var node0 = nodes[n0];
   var node1 = nodes[n1];
   line(node0[0], node0[1], node1[0], node1[1]);
}
This code loops through the array of edges. It gets the two numbers defined by an edge and looks up the corresponding node in the nodes array. Then it draws a line from the (x, y) coordinate of the first node to the (x, y) coordinate of the second node.

Is that it?

We set out to draw a cube, yet all we've done is draw a square and four circles:
We could have drawn that with a lot less effort. However, this really is our cube - it's just that we're viewing it end on. If we can work out how to rotate our cube so it's no longer end on to the screen, we'll see that it's not just a square and four circles.

Want to join the conversation?

  • hopper happy style avatar for user fdshgs gfsdgsd
    i can't really understand the
    var node0 = nodes[n0];
    var node1 = nodes[n1];
    line(node0[0], node0[1], node1[0], node1[1]);
    part
    I don't know why the edges[e][1]; and edges[e][0]; numbers have to get inside the nodes[n0]; and nodes[n1]; can someone explain me it simpler? and why they have to go inside?
    (74 votes)
    Default Khan Academy avatar avatar for user
    • leafers sapling style avatar for user Peter Collingridge
      From the author:I'm going to assume you understand how arrays work. Each node is an array of 3 numbers, representing a point in 3D space. For example, a node might be [100, 200, 0], which would represent a point 100 pixels along horizontally, 200 pixels down vertically and 0 pixels "into" the screen.

      nodes is an array of nodes, and so an array of arrays. var node0 = nodes[n0]; gets the node at index n0, which might be the 4th node ([-100, 100, 100];). When we do node0[0], that gets the x-value for that node, and node0[1] gets the y-value.

      edges is also an array of arrays. edges[e] gets the the edge at the eth index, which might be the edge between nodes 1 and 3. That would be an array, [1, 3]. So when we do edges[e][0] that get the first node for that edge (which is 1), and edges[e][1] gets the second node for that edge (which is 3). We can use these values to look up the node positions in the nodes array.

      I hope that makes a bit more sense, but maybe I've just made things more confused.
      (122 votes)
  • piceratops ultimate style avatar for user Che-Kai Chang
    When it says, edges[e][0], I assume it is going to the eth array in edges and then first value in the eth array?

    I am just confused because it was not covered in the array tutorial.
    (15 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user Sudarshan Regmi
    If I put any value of edges from line 16-26, will it work effectively?
    (8 votes)
    Default Khan Academy avatar avatar for user
  • hopper jumping style avatar for user The Human Turkey
    Do I absolutely NEED to learn trig in order to make 3D shapes? I was wondering, because I am only in 6th grade and I don't think I will be learning trig for at least another 3 years.
    (8 votes)
    Default Khan Academy avatar avatar for user
  • starky tree style avatar for user Jonathan Yu
    Sorry, I don't really understand this part
    var edge0 = [0, 1];
    var edge1 = [1, 3];
    var edge2 = [3, 2];
    var edge3 = [2, 0];
    var edge4 = [4, 5];
    var edge5 = [5, 7];
    var edge6 = [7, 6];
    var edge7 = [6, 4];
    var edge8 = [0, 4];
    var edge9 = [1, 5];
    var edge10 = [2, 6];
    var edge11 = [3, 7];

    what are the numbers in the arrays??
    (3 votes)
    Default Khan Academy avatar avatar for user
  • male robot hal style avatar for user 猫
    What is nodes.length equivalent to? Is it the number of nodes or something else?
    (3 votes)
    Default Khan Academy avatar avatar for user
  • hopper jumping style avatar for user Yuya Fujikawa
    In the for loop of //drawEdges,
    I don't understand this part the code..
    Thanks to Ethan Luis McDonough, I understand the first part but not this part..
    Can someone please explain this a little simpler?

    var node0 = nodes[n0];
    var node1 = nodes[n1];
    line(node0[0], node0[1], node1[0], node1[1]);
    (3 votes)
    Default Khan Academy avatar avatar for user
    • duskpin tree style avatar for user budster the dingo
      This section is in the loop that loops through the edges array. The edges array contains the indexes of the two nodes that the edge will be drawn between. We set the variable node0 to the index of the first point and node1 to the index of the second point. Then, we get the x and y of each node with the indexes we just retrieved and draw a line segment between those points.

      Here's the code with more detailed comments:
      // we loop through the edges array
      for (var e = 0; e < edges.length; e++) {
      // we get the indexes of both of the nodes in our segment
      var n0 = edges[e][0];
      var n1 = edges[e][1];

      // we get each node point by using the indexes we had previously retrieved
      var node0 = nodes[n0];
      var node1 = nodes[n1];

      // we display the line segments
      line(node0[0], node0[1], node1[0], node1[1]);
      }
      (3 votes)
  • blobby green style avatar for user Giao Nguyen
    In the movies these days, does it normally take this long to create a 3d shape, or is it much simpler due to having your own software?
    (2 votes)
    Default Khan Academy avatar avatar for user
  • leafers seed style avatar for user john salomon
    how can i add z axis/perspective projection?
    (2 votes)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Bob Lyon
      Consulting https://en.wikipedia.org/wiki/3D_projection#Weak_perspective_projection we conclude that we must also compute X-perspective and Y-perspective coordinates for each node, so we'll use indices 3 and 4 respectively to hold those values.

      "Perspective" is from an observer's point of view, which we usually call a "camera". So we locate the camera on the Z axis and look back at the nodes. The camera also has a "focal length". So we declare two global variables to describe the camera:
      var zCam = 300; // camera's location at (0, 0, zCam)
      var f = 220; // camera's focal length

      Then we always compute new perspective coordinates whenever nodes change. I suggest a function:
      /* Computes X and Y perspectives of node. */
      var camPerspective = function(node) {
      node[0+3] = node[0] * f / (zCam - node[2]);
      node[1+3] = node[1] * f / (zCam - node[2]);
      };
      and a loop to that invokes the function on all nodes:
          for (var n = 0; n < nodes.length; n++) {
      camPerspective(nodes[n]);
      }
      and then modifying edge rendering to use the perspective coordinates:
              line(node0[0+3], node0[1+3], node1[0+3], node1[1+3]);
      and doing the same for node rendering.

      Or, check out https://www.khanacademy.org/computer-programming/3d-cameras/4592485916508160
      (2 votes)
  • blobby green style avatar for user Arunabh Bhuyan
    Can someone please explain me, why we used this:

    fill(nodeColor);
    noStroke();
    for (var n = 0; n < nodes.length; n++) {
    var node = nodes[n];
    ellipse(node[0], node[1], nodeSize, nodeSize);
    }

    Instead of this:

    ellipse(node, node, nodeSize, nodeSize);
    (2 votes)
    Default Khan Academy avatar avatar for user