This Lesson introduces one of the 3D renderers available in Processing, and we also start to see how external files (in this case image files) can be imported into Processing for use in a composition. We also learn how to save a still shot of our composition directly from our program, obviating the need to take a screen shot of it if you want to save it.
By the end of this lesson, you should be able to:
This lesson will take us one week to complete (23 - 29 Jun 2021). The deliverables for this lesson are programming exercises, detailed on the next pages.
Assignment | description | submitted for grading? |
---|---|---|
Exercise 6.1 | Draw three shapes to the screen all at once. Have each of them rotate about their own centers: one around the x axis, one around the y axis, one around the z axis | submit EITHER this program OR 6.3 to the dropbox. |
Exercise 6.2 | Finish the fourth side of the cube from Example 6.4. Change the program to rotate the cube with the mouse. | No |
Exercise 6.3 | Use beginShape(TRIANGLES) to draw a four-sided pyramid with mouse-controlled rotation in 3D space. | submit EITHER this program OR 6.1 to the dropbox |
Exercise 6.4 | Export one of your programs as an app that can be run outside of the Processing environment. | No |
Exercise 6.5 | Find/create a smallish snapshot so you can play around with it in a program. | No |
Exercise 6.6 | Using a head shot of you or a friend, make an animated person that walks across the screen, or jumps up and down, or says something with a speech bubble, by importing an image of the face and drawing the rest of the body with your programming skills. | Yes - submit this. |
If you have any questions, please post them to our Questions? discussion forum (not e-mail) in Canvas. I will check that discussion forum daily to respond. While you are there, feel free to post your own responses if you, too, are able to help out a classmate.
Last week when we rotated shapes, all the rotations were in the plane of the computer screen, meaning that the rotation was happening around the Z axis, if you think of the Z axis as a line drawn poking straight through your screen. Processing has a 3D renderer to allow you to work in a three-dimensional coordinate system. The default is that the positive Z direction is out of the screen towards you and the negative Z direction is into the screen away from you. In order to let Processing know that you want to have a 3D coordinate system, you have to specify the renderer when you call size(). We are going to work with the P3D renderer but there are other ones, such as OpenGL. You can look up the other ones at the Processing website if you want to.
Here are a series of three simple programs to demonstrate rotations in 3D. First, start with the rotation we know about from last week: rotation around the Z axis.
//rotating orange square float rotAngle = 0 ; void setup (){ size ( 400 , 400 ); } void draw (){ background ( 255 ); translate ( width / 2 , height / 2 ); rotate (rotAngle); rotAngle+= 0.01 ; fill ( 250 , 100 , 13 ); rectMode ( CENTER ); rect ( 0 , 0 , 200 , 200 ); } |
Now let’s take that same square and rotate it around the X axis. That program looks like this:
//rotating orange square //rotates around X axis float rotAngle = 0 ; void setup (){ size ( 400 , 400 , P3D ); } void draw (){ background ( 255 ); translate ( width / 2 , height / 2 , 0 ); rotateX(rotAngle); rotAngle+= 0.01 ; fill ( 250 , 100 , 13 ); strokeWeight ( 4 ); rectMode ( CENTER ); rect ( 0 , 0 , 200 , 200 ); } |
Notice the different things in this program: When I called size(), I specified P3D after I gave the dimensions of the window. When I called translate(), I used three arguments. The third one is for translation in Z. I used the command rotateX instead of just rotate.
Here’s the same program again except with rotation around the Y axis:
//rotating orange square //rotates around Y axis float rotAngle = 0 ; void setup (){ size ( 400 , 400 , P3D ); } void draw (){ background ( 255 ); translate ( width / 2 , height / 2 , 0 ); rotate (rotAngle); rotAngle+= 0.01 ; fill ( 250 , 100 , 13 ); strokeWeight ( 4 ); rectMode ( CENTER ); rect ( 0 , 0 , 200 , 200 ); } |
The only thing different between the program in Example 6.2 and the program in Example 6.1 is the use of rotateY instead of rotateX.
Now let’s modify our rotating square so that we are controlling the rotation with the mouse. You know all the commands to make this work already. Here’s how it is done. Decide how much of a rotation you want to allow (a full rotation is 360 degrees, or 2*PI radians). Then map this angle to the width or height of the screen based on the mouse location.
Here is an example of rotation about the Y axis based on the X location of the mouse. Does this seem counterintuitive? Think about it for a minute. Rotation around Y basically means side-to-side rolling, so to me it seems more intuitive to control this motion with the side-to-side, or X, position of the mouse.
//rotating orange square //rotates around Y axis with mouse void setup (){ size ( 400 , 400 , P3D ); } void draw (){ background ( 255 ); translate ( width / 2 , height / 2 , 0 ); float rotAngle = 2 * PI * mouseX / width ; rotateY(rotAngle); fill ( 250 , 100 , 13 ); strokeWeight ( 4 ); rectMode ( CENTER ); rect ( 0 , 0 , 200 , 200 ); } |
The most important line of this program is the one that says:
float rotAngle = 2*PI*mouseX/width;
That line does a lot for you. It tells Processing that you want one whole rotation to occur over the distance that equals the width of the window and you want the amount of rotation to be dependent on the X position of the mouse. It is a good idea to experiment with putting different numbers into this line to see what they do. For example, try getting rid of the number 2 or changing it to 4 and see what happens. What would you have to change to get one whole rotation about the X axis based on the Y position of the mouse? Here's a screencast of the mouse-controlled rotation program [3] (and a captioned version [4]) so you can see what it looks like when it is running.
Knowing how to rotate a flat shape in space is the first step to drawing an actual shape in three dimensions. There is a little bit of a trick to drawing in three dimensions and that is that the usual shape primitives, such as rect() and ellipse() don’t accept Z coordinates as arguments. If you want to build a three-dimensional object, you have to use beginShape() and endShape() because the vertex() command does accept Z coordinates.
If you have ever taken a drawing class, then you probably have learned how to draw 3D shapes on paper using perspective techniques, like in my sketch of a make believe city street:
Even with no formal training, most people I've ever met can draw a cube in perspective by drawing two overlapping squares and then connecting up all the corners.
Those techniques are great for visualizing a three-dimensional world on a flat canvas, but when we want to use computer graphics, we won't use these techniques. Instead we have to imagine where the vertices of our three dimensional shape really are with respect to each other, set those vertices, and then if we want the shape to be seen in perspective, we let the computer create the viewing angle.
So in the program examples below where we are going to draw a cube that rotates about its own center, we have to imagine where the 8 vertices are with respect to the center of a cube:
Let’s draw a cube that will rotate about its center. We’ll start by drawing one face of the cube, like this:
float theta= 0 ; float d = 100 ; //length of an edge of the cube void setup (){ size ( 600 , 400 , P3D ); } void draw (){ background ( 255 ); stroke ( 0 ); //make the origin the center of the cube translate ( width / 2 , height / 2 , 0 ); rotate (theta); theta+=. 01 ; beginShape (QUADS); //back face of the cube fill ( 255 , 0 , 0 ); vertex (-d/ 2 , -d/ 2 , -d/ 2 ); vertex (d/ 2 , -d/ 2 , -d/ 2 ); vertex (d/ 2 , d/ 2 , -d/ 2 ); vertex (-d/ 2 , d/ 2 , -d/ 2 ); endShape (); } |
Notice that I used the argument QUADS with beginShape(). That lets Processing know that I’m trying to draw something with four sides. This will become useful later because if we want to draw several four-sided shapes in a row, we don’t have to use separate beginShape()/endShape() pairs. You’ll see what I mean with the next step for this project when we add the front face of the cube:
float theta= 0 ; float d = 100 ; //length of an edge of the cube void setup (){ size ( 600 , 400 , P3D ); } void draw (){ background ( 255 ); stroke (s); //make the origin the center of the cube translate ( width / 2 , height / 2 , 0 ); rotateY(theta); theta+=. 01 ; beginShape (QUADS); //back face of the cube is red fill ( 255 , 0 , 0 ); vertex (-d/ 2 , -d/ 2 , -d/ 2 ); vertex (d/ 2 , -d/ 2 , -d/ 2 ); vertex (d/ 2 , d/ 2 , -d/ 2 ); vertex (-d/ 2 , d/ 2 , -d/ 2 ); //front face of the cube is yellow fill ( 255 , 255 , 0 ); vertex (-d/ 2 , -d/ 2 , d/ 2 ); vertex (d/ 2 , -d/ 2 , d/ 2 ); vertex (d/ 2 , d/ 2 , d/ 2 ); vertex (-d/ 2 , d/ 2 , d/ 2 ); endShape (); } |
See how there is only one beginShape() / endShape() pair. That is how putting QUADS as the argument to beginShape() saves us some work. When you put QUADS in as the argument to beginShape(), Processing expects you to write down a list of vertices in groups of 4 and it will make each group of 4 into a closed quadrilateral. That way you only have to do beginShape() / endShape() one time, just enclose all the vertices in there. You can do this in 2D also if you want to make a series of quadrilaterals.
OK, now to add a side face:
float theta= 0 ; float d = 100 ; //length of an edge of the cube void setup (){ size ( 600 , 400 , P3D ); } void draw (){ background ( 255 ); stroke ( 0 ); //make the origin the center of the cube translate ( width / 2 , height / 2 , 0 ); rotateY(theta); theta+=. 01 ; beginShape (QUADS); //back face of the cube is red fill ( 255 , 0 , 0 ); vertex (-d/ 2 , -d/ 2 , -d/ 2 ); vertex (d/ 2 , -d/ 2 , -d/ 2 ); vertex (d/ 2 , d/ 2 , -d/ 2 ); vertex (-d/ 2 , d/ 2 , -d/ 2 ); //front face of the cube is yellow fill ( 255 , 255 , 0 ); vertex (-d/ 2 , -d/ 2 , d/ 2 ); vertex (d/ 2 , -d/ 2 , d/ 2 ); vertex (d/ 2 , d/ 2 , d/ 2 ); vertex (-d/ 2 , d/ 2 , d/ 2 ); //a green side face fill ( 0 , 255 , 0 ); vertex (-d/ 2 , -d/ 2 , -d/ 2 ); vertex (-d/ 2 , -d/ 2 , d/ 2 ); vertex (-d/ 2 , d/ 2 , d/ 2 ); vertex (-d/ 2 , d/ 2 , -d/ 2 ); endShape (); } |
It’s an exercise for you to finish the cube. Note that since we are looking at this cube with a straight-on perspective, you can just draw the four side faces and forget about the top and bottom because you'll never see them. It is a common trick in computer graphics not to draw anything that won't be seen. If we were going to allow rotation around the x axis, then you'd probably want to draw the top and bottom faces.
In fact, Processing does have two intrinsic functions called box() and sphere(). For real, you’d probably want to use the box() function to draw a 3D cube. I made you do it the hard way so you’d see how to draw other less regular shapes.
Wouldn’t it be great to show your pals what you’ve been up to without making them download Processing and compile your program?! I’m sure you’ve been wanting to do this. Well, you can. All you have to do is choose “Export Application . . .” from Processing's File drop-down menu. Processing will pop up a dialog box asking you what kind of platform (mac, windows, or linux) you are intending to run it on. Then it will make a new folder inside your sketch folder with the app in it. Try it out!
6.1 Draw three shapes to the screen all at once. Have each of them rotate about their own centers: one rotates around the z axis, one rotates around the x axis, and the other rotates around the y axis.
6.2 Finish the 4th side of the cube. Change the program to rotate the cube with the mouse.
6.3 Use beginShape(TRIANGLES) to draw a four-sided pyramid with mouse-controlled rotation in 3D space.
6.4 Export an app so you or someone else can run one of your programs.
Turn in either 6.1 OR 6.3 to its assignment dropbox in Canvas
It is also useful to take advantage of the 3D renderer if you want to do the equivalent of "flipping" a shape over the x or y axis, which is easier than trying to recalculate the vertices in your head.
void setup (){ size ( 200 , 200 , P3D ); noStroke (); noLoop (); } void draw (){ fill ( 0 ); capitalE( width / 2 , height / 2 ); fill ( 255 ); pushMatrix (); translate ( width / 2 , height / 2 , 0 ); rotate ( PI ); capitalE( 0 , 0 ); popMatrix (); fill ( 255 , 0 , 0 ); pushMatrix (); translate ( width / 2 , height / 2 , 0 ); rotateY( PI ); capitalE( 0 , 0 ); popMatrix (); } void capitalE( int x, int y) { beginShape (); vertex (x,y); vertex (x,y+ 100 ); vertex (x+ 40 ,y+ 100 ); vertex (x+ 40 ,y+ 90 ); vertex (x+ 10 ,y+ 90 ); vertex (x+ 10 ,y+ 55 ); vertex (x+ 29 ,y+ 55 ); vertex (x+ 29 ,y+ 45 ); vertex (x+ 10 ,y+ 45 ); vertex (x+ 10 ,y+ 10 ); vertex (x+ 33 ,y+ 10 ); vertex (x+ 33 ,y); endShape ( CLOSE ); } |
//twirling squares //they revolve around the center of the window //press the mouse inside the window to make them go float boxDiam = 40 ; float boxAngle1 = 0 ; void setup (){ size ( 400 , 200 ); } void draw (){ rectMode ( CENTER ); background ( 247 ); stroke ( 0 ); translate ( 200 , 100 ); rotate (boxAngle1); fill ( 255 , 0 , 0 ); rect ( 0 , 0 ,boxDiam,boxDiam); fill ( 250 , 108 , 13 ); rect ( 60 , 0 ,boxDiam,boxDiam); fill ( 250 , 203 , 13 ); rect ( 120 , 0 ,boxDiam,boxDiam); fill ( 182 , 250 , 13 ); rect ( 180 , 0 ,boxDiam,boxDiam); if ( mousePressed == true ){ boxAngle1 += . 05 ; } } |
Here is a 13 second video demonstration (video has no sound)
Note the difference between this program and the previous one. In this one I use a series of pushMatrix() / popMatrix() pairs around each rectangle so that they each rotate about their own centers.
//twirling squares //they each rotate about their own centers //press the mouse in the window to make them go float boxDiam = 40 ; float boxAngle1 = 0 ; void setup (){ size ( 400 , 200 ); } void draw (){ rectMode ( CENTER ); background ( 247 ); stroke ( 0 ); pushMatrix (); translate ( 200 , 100 ); rotate (boxAngle1); fill ( 255 , 0 , 0 ); rect ( 0 , 0 ,boxDiam,boxDiam); popMatrix (); pushMatrix (); translate ( 260 , 100 ); rotate (boxAngle1); fill ( 250 , 108 , 13 ); rect ( 0 , 0 ,boxDiam,boxDiam); popMatrix (); pushMatrix (); translate ( 320 , 100 ); rotate (boxAngle1); fill ( 250 , 203 , 13 ); rect ( 0 , 0 ,boxDiam,boxDiam); popMatrix (); pushMatrix (); translate ( 380 , 100 ); rotate (boxAngle1); fill ( 182 , 250 , 13 ); rect ( 0 , 0 ,boxDiam,boxDiam); popMatrix (); if ( mousePressed == true ){ boxAngle1 += . 05 ; } } |
Here is a 13 second video demonstration (video has no sound)
In this one, I translate to (200,100) so that the center of the red box is the origin around which the rotation happens. The important thing to remember with rotations is to translate first, and then rotate.
//twirling squares //they revolve around the center of the window //in 3D! //press the mouse to make them go float boxDiam = 40 ; float boxAngle1 = 0 ; void setup (){ size ( 400 , 200 , P3D ); } void draw (){ rectMode ( CENTER ); background ( 247 ); smooth (); stroke ( 0 ); translate ( 200 , 100 ); rotateY(boxAngle1); fill ( 255 , 0 , 0 ); rect ( 0 , 0 ,boxDiam,boxDiam); fill ( 250 , 108 , 13 ); rect ( 60 , 0 ,boxDiam,boxDiam); fill ( 250 , 203 , 13 ); rect ( 120 , 0 ,boxDiam,boxDiam); fill ( 182 , 250 , 13 ); rect ( 180 , 0 ,boxDiam,boxDiam); if ( mousePressed == true ){ boxAngle1 += . 05 ; } } |
Here is a 13 second video demonstration (video has no sound)
You can use Processing to import an image and display it in the display window. The procedure is somewhat similar to the procedure to load a font and display text. You first need to put the image you want into the sketch’s data folder, or else in your program you need to specify the entire pathname to locate the image in your computer’s file system. There are several ways to put an image in your folder. The easiest is to drag the icon for the image from wherever it lives on your computer and then drop it onto your open Processing editing window. You will get a message in the console saying “1 file added to sketch” or something similar. In order to interact with your image in your program, you declare the variable type PImage, you use loadImage() to get it into the program and then you use image() to display it. Here is a simple example:
// display an image of Massimo's head PImage img; size ( 400 , 400 ); img = loadImage ( "massimo_head.egg_e5137.png" ); image (img, 0 , 0 ); |
Images are treated just like any other object, so using all the skills we know already, we can plot this image to the screen multiple times, or have it move or twirl it around, or plot it to the screen with a mouse click, or change its size, and so forth. Here are a couple of examples of that:
// display three images of Massimo's head // they move across the screen PImage img; float x = 0 ; void setup (){ size ( 800 , 800 , P3D ); img = loadlmage( "massimo_head.egg_e5137.png" ); } void draw (){ background ( 200 ); for ( int i= 0 ;i< 550 ;i=i+ 250 ){ image (img,x,i); } x++; if (x>= width ) { x= 0 ; } } |
In the program above, I use a for loop to draw three of the images to the screen. They move across the screen from left to right and when they get to the right side they start over again on the left side.
You can change the overall color and opacity of an image with tint(). The tint() command takes 3 rgb color arguments -- or one argument for greyscale -- and an optional fourth argument to specify opacity. If you want to change the opacity of an image without changing its colors, use white (255) as the tint color, and use the desired opacity as the second argument, so in that case you’d just have two arguments in tint(). Here is an example of tint():
// display images of Massimo's head with a mouse click // images are tinted green and have some transparency Plmage img; void setup (){ size ( 800 , 800 ); img = loadlmage( "massimo_head.egg_e5137.png" ); } void draw (){ } void mouseClicked(){ tint ( 59 , 98 , 5 , 128 ); scale ( random ( 0.5 , 2.5 )); image (img, mouseX , mouseY ); } |
Notice anything unusual about the program in Example 6.6? Look at the draw() block. There's nothing there! The deal is that if there is a setup() block, then there has to be a draw() block as well. However, it doesn't have to do anything, it can just be a place holder. You want to do all your file input in setup() so that it only has to happen once. This goes for images and fonts and other data files. In the program I wrote, all I wanted was for the image to display when I clicked the mouse inside the display window. So, I used the mouseClicked() function to do all the action in the program. But since I was importing an image, I had to use setup(), and since there was a setup(), there had to be a draw(). I made draw() empty since I didn't need it for anything.
You can save a snapshot of your creation with the save() command. You can specify TIFF, TARGA, JPEG, or PNG files. The default if you don’t specify a file extension is TIFF. The default place your exported image file will go is into the same folder where your sketch lives. Here’s an example in which a key press saves a file called testimage.jpg:
// display an image of Massimo's head // save it with a key press PImage img; void setup (){ size ( 400 , 400 ); img = loadImage ( "massimo_head.egg_e5137.png" ); } void draw (){ image (img, 0 , 0 ); if ( keyPressed == true && key == 's' ){ save( "testimage.jpg" ); println ( "Your image has been saved! Yay!" ); } } |
6.5 Find or create a smallish snapshot to play around with images.
6.6 Using a head shot of you or a friend, make an animated person that walks across the screen (or jumps up and down or says something with a speech bubble) by importing an image of the face and drawing the rest of the body by hand.
Submit 6.6 to its assignment dropbox in Canvas. Please zip your whole folder and submit that, or else make sure you also submit your image file(s) along with your pde file so I can run your program!
In this lesson, you learned how to use the 3D renderer to draw shapes and simulate their movement in 3D space. You also learned how to import an image file into a program and work with it just the same as any shape you would draw.
(These are copied and pasted from the pages in Lesson 6 where they originally appeared)
6.1 Draw three shapes to the screen all at once. Have each of them rotate about their own centers: one rotates around the z axis, one rotates around the x axis, and the other rotates around the y axis.
6.2 Finish the 4th side of the cube. Change the program to rotate the cube with the mouse.
6.3 Use beginShape(TRIANGLES) to draw a four-sided pyramid with mouse-controlled rotation in 3D space.
6.4 Export an app so you or someone else can run one of your programs.
6.5 Find or create a smallish snapshot to play around with images.
6.6 Using a head shot of you or a friend, make an animated person that walks across the screen (or jumps up and down or says something with a speech bubble) by importing an image of the face and drawing the rest of the body by hand.
Turn in either 6.1 or 6.3, plus 6.6 to their dropboxes in Canvas. There is no discussion for this lesson.
Links
[1] https://www.e-education.psu.edu/earth801/sites/www.e-education.psu.edu.earth801/files/screencasts/rotatingSquare.mp4
[2] https://www.e-education.psu.edu/earth801/sites/www.e-education.psu.edu.earth801/files/screencasts/rotatingSquareCC.mov
[3] https://www.e-education.psu.edu/earth801/sites/www.e-education.psu.edu.earth801/files/screencasts/rotatingSquareMouse.mp4
[4] https://www.e-education.psu.edu/earth801/sites/www.e-education.psu.edu.earth801/files/screencasts/rotatingSquareMouseCC.mov
[5] https://www.e-education.psu.edu/earth801/sites/www.e-education.psu.edu.earth801/files/screencasts/massimoDisplaySave.mp4
[6] https://www.e-education.psu.edu/earth801/sites/www.e-education.psu.edu.earth801/files/screencasts/massimoDisplaySaveCC.mov