Main content
Computer programming
Generating 3D shapes
So we have a cube now, but what if we want to change its position or size? Or what if we want a rectangular cuboid or many cuboids? With our current code, we would have to change the nodes one-by-one, which would be nuisance. What we would like is a simple method to create a cuboid with a certain position and dimensions. In other words, we want a function that maps a position and dimensions into an array of nodes and an array of edges.
Defining a cuboid
A cuboid has three dimensions: width, height and depth:
It also has a position in 3D space, giving us six parameters. There are a couple of ways we could define the position of the cuboid: we could define its center or we could define one corner. The former is probably more common, but I think the latter is easier to use.
Our function needs to return both the nodes and edges array. One way to return two variables is to package the variables into an object, with a key for
nodes
and a key for edges
. Note, you can use any string to refer to the variable, I just find it easier to use the same word.// Create a cuboid with a vertex at (x, y, z)
// with width, w, height, h, and depth, d.
var createCuboid = function(x, y, z, w, h, d) {
var nodes = [];
var edges = [];
var shape = { 'nodes': nodes, 'edges': edges };
return shape;
};
If we used that function to create a cuboid, we'd access the first node like this:
var shape = createCuboid(0, 0, 0, 100, 160, 50);
var node0 = shape.nodes[0];
This will set
node0
to the first value in the nodes
array. At the moment, however, there are no values in the nodes or edges arrays.We define the nodes as being every combination of position with or without the corresponding dimension. Edges are defined the same way as before (except rather than define each of the edges individually first, I define them all at once). Note that this function allows you to specify negative dimensions for the cuboid.
var createCuboid = function(x, y, z, w, h, d) {
var nodes = [[x, y, z ], [x, y, z+d], [x, y+h, z ], [x, y+h, z+d], [x+w, y, z ], [x+w, y, z+d], [x+w, y+h, z ], [x+w, y+h, z+d]];
var edges = [[0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7]];
return { 'nodes': nodes, 'edges': edges};
};
We can then create a cuboid with width 100, height 160, depth 50 and one node on the origin like this:
var shape = createCuboid(0, 0, 0, 100, 160, 50);
Since our previous code just references global
nodes
and edges
variables, we need to store our object's properties into those globals:var nodes = shape.nodes; var edges = shape.edges;
You can see the complete code below.
Working with multiple shapes
We can create shapes with different dimensions, what if we want more than one? Whenever we want a variable number of things, an array is useful, so lets create an array of shapes.
var shape1 = createCuboid(-120, -20, -20, 240, 40, 40);
var shape2 = createCuboid(-120, -50, -30, -20, 100, 60);
var shape3 = createCuboid( 120, -50, -30, 20, 100, 60);
var shapes = [shape1, shape2, shape3];
Now we need to change the display and rotate functions to work with an array of objects. Start by wrapping the code to display edges in a for loop that loops through all the shapes:
// Draw edges
stroke(edgeColor);
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
var nodes = shapes[shapeNum].nodes;
var edges = shapes[shapeNum].edges;
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]);
}
}
And a similar for loop for displaying nodes:
// Draw nodes
fill(nodeColor);
noStroke();
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
var nodes = shapes[shapeNum].nodes;
for (var n = 0; n < nodes.length; n++) {
var node = nodes[n]; ellipse(node[0], node[1], nodeSize, nodeSize);
}
}
We could add a similar for loop to each of the rotate functions, but I think it's more flexible to pass the array of nodes to each function - that way we can rotate shapes independently of one another. For example, the rotateZ3D() function would look like this:
var rotateZ3D = function(theta, nodes) { ... };
Now when we use the mouse to rotate, we have to loop through the shapes and call the function for each one:
mouseDragged = function() {
var dx = mouseX - pmouseX;
var dy = mouseY - pmouseY;
for (var shapeNum = 0; shapeNum < shapes.length; shapeNum++) {
var nodes = shapes[shapeNum].nodes;
rotateY3D(dx, nodes);
rotateX3D(dy, nodes);
}
};
Make sure you remove any other calls to the rotate functions that do not pass in any nodes. You can see the complete code below.
Want to join the conversation?
- How would you create a three-dimensional shape with faces?(55 votes)
- At some point I would like to add that to the tutorial. It requires a fair bit more work and mathematics. Here's a program to get you started: https://www.khanacademy.org/cs/shaded-box/1066372924(67 votes)
- In the future will Khan Academy have a 3D canvas?(23 votes)
- Processing has rudimentary 3D built in. However it is disabled, which forces us to learn more about the math behind 3D graphics rather than letting the computer do everything for us.
https://github.com/musicalglass/AKA/wiki/3D(31 votes)
- Is there a way to make 3D shapes without using trigonometry? I am only nine years old, but I love doing coding. But of course I don't know trigonometry.(24 votes)
- I don't think that you need to know Trig to use these functions anymore than you needed it to invoke
rotate
in 2D land. Similarly you don't need to know Linear Algebra to usetranslate
,scale
orrotate
. Knowing the higher maths may help some appreciate what is going on (or send them running for the exits).(18 votes)
- Is it possible to generate a 3D object isometrically? If so, what are the mathematics used in creating one?(8 votes)
- Yes it is and the mathmatics used is multiplecation(1 vote)
- Is it possible to use anything other than points and lines in the construction of 3D shapes? For example, if I wanted to create a 3D cylinder, could I somehow use ellipses for the 2 faces, or would I have to substitute a tediously crafted regular polygon that has sufficient sides to bear at least a resemblance to a real circle?(5 votes)
- A simple
for
loop would take the tedium out of crafting a regular polygon and give you what most graphics hardware likes to process. Notwithstanding, math can be used to transform an ellipse into its corresponding ellipse in projected 3D.
https://www.khanacademy.org/computer-programming/regular-polygons/1304459398(5 votes)
- How would you make a 3D shape smaller as it gets further away from the "camera" (I know that there isn't exactly a camera, but how would you make things smaller as their Z value decreases?)(5 votes)
- First assume the camera is fixed and located at a fairly far away positive Z location, most likely centered on the canvas, e.g.
Then the perspective point,camera = (0, 0, 1000)
pp
of pointp
is computed with
Working backwards, note that (1) the value ofd = distanceBetween(camera, p)
scaleFactor = camera.z / d
pp = (scaleFactor*p.x, scaleFactor*p.y, p.z)pp.z
should not matter since perspective is only used for rendering onto the 2D canvas, (2) thescaleFactor
is one (a noop) at the 3D universe's origin, and less than (or greater than) one for points farther (or nearer), and (3)distanceBetween
simply uses the Pythagorean theorem for computing the distance between two 3D points.
See line 305 at https://www.khanacademy.org/computer-programming/3d-perspective-shading/4521460558135296(2 votes)
- What does the keyword
map
do in javascript, and if possible could you also write a simple code to demonstrate how it can be used. Thanks in advanced! :)(3 votes)- The function allegedly maps a value in one range to its corresponding value in another range. In fact, the value and its mapping can be "out of range".
Given any numberx
, themap
function returnsy
where
wherey = mx + b
x
is the first argument to the function andm
andb
are determined by last four arguments to the function. Specificallym
andb
are determined by the Algebraic line passing through the two points(low1, low2)
and(high1, high2)
.
For example, we (should) know that water freezes at 0° Celsius or 32° Fahrenheit and boils at 100° Celsius or 212° Fahrenheit. What is the Celsius equivalent of 86°F?
What is the Fahrenheit equivalent of -40°C?answer = map(86, 32, 212, 0, 100);
answer = map(-40, 0, 100, 32, 212);
So themap
function lets us use linear equations without knowing Algebra.(5 votes)
- could you make a tetrahedron with this?(2 votes)
- Certainly. You can make any shape with flat surfaces.(4 votes)
- Are there any ambitious coders out there with the ability to expand upon this code to create a Menger sponge?(2 votes)
- I'll bite. Here is brute force Menger Sponge: https://www.khanacademy.org/computer-programming/menger-sponge/5000305485152256(3 votes)
- Does anyone know how to upload textures onto cubes in P3D?(1 vote)
- The Processing documentation site has this example of P3D texturing:
size(400, 400, P3D);
noStroke();
PImage img = loadImage("shells.jpg");
textureMode(NORMAL);
beginShape();
texture(img);
vertex(40, 80, 0, 0);
vertex(320, 20, 1, 0);
vertex(380, 360, 1, 1);
vertex(160, 380, 0, 1);
endShape();
I frankly haven't the slightest clue whether you are able to do such a thing within the bounds of KA, and I'm struggling to find it in me to make that discovery.
We are here to help you out, but one of the biggest things you have to learn is to a) use the right tool for the right job; you're not going to successfully build Superliminal on KA (even if you had the know-how), b) take things at a slower pace, and don't get into a project that's so out of your depth that you must ask questions at every turn, and c) USE GOOGLE. It's your best friend. Near-unlimited knowledge at your fingertips. If I lacked a functioning search engine, it's likely that a third of my answers would not be present.(5 votes)