I did these photos for a friend of mine who were representing Toni & Guy in the fight for the Norwegian title "Årets Frisør". We were on a very limited budget (If we can use the word budget at all) so we ended up using the cheapest kind of industrial lighting and black rug taped to the wall. The backdrops were created separately from a bunch of cityscape photos I caught downtown of Oslo, late fall 2009. Still, I think the result came out pretty nice.
A bunch of thankyous to Daniel for lending me a helping hand & offering creative input, the splendid models, Charlotte, Thea & Julie, who showed up for free & did an awesome job (considering the circumstances), Charlotte nr. 2 for doing the makeup & of course Benjamin, the hairdresser.
A while back I was working on a project for a client when I ran into some issues trying to implement a semi 3d design concept. When I did the sketches in photoshop I thought it was gonna be a breeze to implement, but after the design was approved by the client and the deadline was set, I really ran into some trouble. I can´t really go into specifics about this particular project, but the bottom line was that I needed to apply the rules of 3d perspective in some situations, while bending the same rules to fit a dynamic two dimensional layout in other. The result I was looking for just couldn´t be achieved by applying the built in 3d features of Flash 10, or any of the other open source 3d libraries like papervision3d or away3d. At least not without having a great deal of in depth knowledge of how these libraries work from the ground up. That is why I decided to get my hands dirty with the basics of 3d math.
I found a couple of articles on the subject while browsing for some theory, but since I'm no mathematician, It took me a while to wrap my head around the concepts and really understand the theory, as well as how to take advantage of this in actionscript. After a while though, I finally pieced together the parts I needed to build some simple 3d geometry from scratch, while having full control over each point in both 2d and 3d space. Later on I structured the formulas and functions needed to execute these tasks, into three simple classes that are easy to use, even if you don’t understand the underlying math. Later on I'll explain how to make use of these classes to build the example you see at the very beginning of this article, but first I will try to explain my understanding of the underlying theories.
Some quick theory.
Perspective projection is the theory of how to display and move 2d coordinates in relation to each other to make the illusion of 3d space on a flat surface. In our case: it helps us place, draw and move things in the display list, in a way that makes them seem like they exist in 3d space. When doing 3d in flash it can be helpful to imagine the stage as a plane which we view through a camera viewfinder, and that the objects we want to draw are floating behind it. Take a look at the following illustration:

Here we see the stage from above, with a camera pointed directly at it's center, and a cube floating behind it. We also see some dotted lines drawn from the center of the camera to each of the cubes corners and the points where these lines intersect with the stage (marked with four small dots). These dots show us where the corner points of the cube should be drawn to create the illusion that we are looking for. The placement of these points clearly show that the four corner points further away from the camera, are placed closer to the where the camera is pointed. The result of drawing this on the stage would look something like this:

The Field of View and the focal length
Take a look at figure 1 again and take notice of the terms ‘Field of View’ & ‘Focal Length’. These terms are very essential to the concept of perspective projection as they lay down the ground rules for how scale and distance is depicted:
The field of view is the angle of the observers (or the camera’s) field of vision. It is a value between 0 and 180, where a value close to 180 exaggerates the perspective, as if the image was shot with a fisheye lens, while a value closer to 0 “flattens” the image and under-exaggerates distances between points.
The Focal Length on the other hand is the z-distance from the observer (camera) to the viewing plane (in this case the stage). This value is closely coupled with the field of view as this distance will have to vary depending on the field of vision, for the outer boundaries of the viewing frustum to line up with the stage (more on this later). The focal length is also the main component in the simple formula that is used to calculate the scale and offset (commonly reffered to as the t value) of each point:

What we can read from this formula is that the scale and offset of each point is in fact the ratio between the distance from the camera to the stage, and the distance from the camera to the point. The only thing which is left then is to apply the scale and offset to each point by multiplying the x, y, and scale by the t value:
To make these concepts a bit easier to work with in actionscript I wrote three simple classes which provides a good foundation for doing custom 3d transformations and perspective projection in Flash: Point2d, Point3d & Matrix3d. This is the same kind of classes that make up the foundation for well known 3d libraries like papervision3d and away3d, only these classes are a bit easier to work with outside the context of a framework. (There is also a set of equivalent object types that ship with flash player 10 but I have experienced some bugs and actual miscalculations when using them and it seems I am not the only one)
Well, enough with the shit chat, let's get started with building our spinning cube! But first:
Ok. The first thing we will do is set up the base class, it´s imports and class properties, before populating these properties with some values:
package{import __AS3__.vec.Vector;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import net.bgstaal.perspectiveprojection.Matrix3d;
import net.bgstaal.perspectiveprojection.Point2d;
import net.bgstaal.perspectiveprojection.Point3d;
[SWF(widht="600", height="330", backgroundColor="0xFFFDE2")]publicclass SpinningCube extends Sprite
{privateconst COLOR:Number = 0x000000;
privatevar _cubeWidth:Number;
privatevar _cubeHeight:Number;
privatevar _cubeDepth:Number;
privatevar _cubeX:Number;
privatevar _cubeY:Number;
privatevar _projectionCenter:Point;
privatevar _fieldOfView:Number;
privatevar _focalLength:Number;
privatevar _pivotPoint:Point3d;
privatevar _3dPoints:Vector.;
privatevar _2dPoints:Vector.;
privatevar _rotationX:Number = 0;
privatevar _rotationY:Number = 0;
privatevar _rotationZ:Number = 0;
publicfunction SpinningCube(){
init();
}privatefunction init ():void
{
setProperties();
}/**
* Set the inital properties for our perspective projection scene
*/privatefunction setProperties ():void
{// set the size properties of the cube
_cubeWidth = 200;
_cubeHeight = 200;
_cubeDepth = 200;
// Set the position of the cube to the center of the stage
_cubeX = (stage.stageWidth - _cubeWidth)/2;
_cubeY = (stage.stageHeight - _cubeHeight)/2;
// Set the projection center (vanishing point) to the center of the stage
_projectionCenter = new Point(stage.stageWidth/2, stage.stageHeight/2);
// Set the pivot point to be at the 3d center of the cube
_pivotPoint = new Point3d(_cubeX + (_cubeWidth/2), _cubeY + (_cubeHeight/2), _cubeDepth/2);
// Set the the field of view to a numver between 1 & 179.
_fieldOfView = 55;
// Calculate the focal length based on the width of the stage and the field of view.var a:Number = _fieldOfView/2;
var b:Number = 90 - a;
var bRad:Number = b/180*Math.PI;
var opposite:Number = stage.stageWidth/2;
_focalLength = opposite * Math.tan(bRad);
}}}
All of these class properties should be self explanatory by now, except for maybe pivotPoint & projectionCenter:
The pivot point is basically the registration point of the object we are applying transformations to. In this case; The point which the cube is rotated around.
The projection center is where we would like the center of our imaginary camery to be directed. In this case; The dead center of the stage.
Also take notice of how the focal length is calculated (the last couple of lines in the setProperties method) based on the field of view and the stage width. This is done so that the projections coordinate system is aligned with the stage coordinate system. The following figure demonstrates how these calculations are done by splitting the viewing frustum into two right angled triangles and applying some simple trigonometry:
Next, we set up the 'create3dPoints' method to create the eight corner points of our cube:
privatefunction create3dPoints ():void
{
_3dPoints = new Vector.();
// add points to create a rectangle of the supplied width and height// at the supplied x and y coordinates and a z value of 0
_3dPoints.push(new Point3d(_cubeX, _cubeY, 0));
_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY, 0));
_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY + _cubeHeight, 0));
_3dPoints.push(new Point3d(_cubeX, _cubeY + _cubeHeight, 0));
// then add the same points again with a z value of the supplied depth
_3dPoints.push(new Point3d(_cubeX, _cubeY, _cubeDepth));
_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY, _cubeDepth));
_3dPoints.push(new Point3d(_cubeX + _cubeWidth, _cubeY + _cubeHeight, _cubeDepth));
_3dPoints.push(new Point3d(_cubeX, _cubeY + _cubeHeight, _cubeDepth));
}
Then we set up the projectPoints method which loops through the our eight corner points, calls the project() method (with the focal lenght and projection center supplied as parameters) on each of the instances and populates the _2dPoints Vector with Point2d instances.
/**
* Loops through and projects the 3d points to create a vector of 2d points.
*/privatefunction projectPoints ():void
{
_2dPoints = new Vector.();
for(var i:int = 0; i < _3dPoints.length; i++){var point3d:Point3d = _3dPoints[i];
// calls the project function on each point3d wich reaturns a point2dvar point2d:Point2d = point3d.project(_focalLength, _projectionCenter);
_2dPoints.push(point2d)}}
If we take a look at the internals of the Point3d.project() function, we recognize the formula for calculation of the t-value at the first line. The next step would normally be to offset the x & y coordinates by this value, but before we can do that we have to make shure that the projection is performed around the cameras projection center. This is done by negatively offsetting the coordinates by the projection center's coordinates, applying the perspective projection, and then reversing the offset. If we just applied the projection right away the final result would look as if the camera was pointed at the top left corner of the stage (0, 0).
publicfunction project (focalLength:Number, projectionCenter:Point = null):Point2d
{var t:Number = focalLength / (focalLength+z);
if(!projectionCenter){
projectionCenter = new Point(0, 0);
}var xOffset:Number = projectionCenter.x;
var yOffset:Number = projectionCenter.y;
var x:Number = this.x;
var y:Number = this.y;
var z:Number = this.z;
x -= xOffset;
y -= yOffset;
x = (x*t)+xOffset;
y = (y*t)+yOffset;
returnnew Point2d(x, y, t);
}
All that is left now before we can get some visual feedback is to draw the points on screen.
To do that we first set up the drawPoints() function:
/**
* Draws a circle on stage as a visual representation of each point. Notice that we multiply the radius of each circle by the Point2d instance's t-value to make the scaling of each point smaller or bigger depending on its distance from the camera and thus enhancing the illusion of 3d space.
*/privatefunction drawPoints ():void
{var radius:Number = 5;
graphics.clear();
for(var i:int = 0; i < _2dPoints.length; i++){var point2d:Point2d = _2dPoints[i];
graphics.beginFill(COLOR);
graphics.drawCircle(point2d.x, point2d.y, radius*point2d.t);
graphics.endFill()}}
Then we add add another method to collect all function calls related to the projection:
Compile & run the application and you should get the following result:
I know. Not too exciting yet... But at least we can see that we have done something right! The four corner points at the back of the cube are rendered smaller, and closer to each other, which gives them the appearance of being further away from the camera. Let's move on to the next step and add some rotation by putting the Matrix3d Class to use:
Since all the functionality of creating and multiplying matrices is abstracted in to the Matrix3d class, adding rotation to the cube is as simple as calling a single static function to create a rotation matrix before applying it to the 3d points. We don't even have to think of how these calculations are done, it just works! (that is if we actually supply something else then 0, 0, 0 as parameters, which we will do pretty soon) If you are anything like me though, you would wanna know every little part of how these calculations are done. That is why I will try to explain, as briefly as i can, the basics of matrices and how this is applied to 3d mathematics: (If you're really not that in to masochism you can skip this part and go right on with finishing the application)
Matrices
A matrix is basically a two dimensional array (or table) of values. Like this one:
[1, 0, 0, 0
0, 1, 0, 0
0, 0, 1, 0]
Pretty simple right? Maybe too simple? Well, the power of matrices lies not in the structures themselves but in the way they can be applied to / multiplied with other matrices of various dimensions. They are multiplied in a way where each value in a row is the result of the first value in that row multiplied with the first value in that collumn plus the second value in that row multiplied with the second value in that row and so on:
By studying the multiply() function closely we can see the same pattern emerging:
We abide to the same rules when applying transformation matrices to points. By treating the point as a one dimensional Matrix (also called a vector) and cross multiplying the values in its single column with each of the values in the transformation matrix's rows, we get a new transformed version of that point:
These concepts can be a bit hard to grasp but the bottom line is that this special way of "cross-calculating" the values gives each cell in the table special properties when it comes to transformation. The most obvious ones is the diagnoal values from the top left cell to the third bottom cell. These cells are the ones that relate directly to the scale of each dimension:
[x, 0, 0, 0
0, y, 0, 0
0, 0, z, 0]
The following matrix (called the identity matrix) for instance, will multiply the x, y and z scale by 1, and therefore leave the subject unchanged:
[1, 0, 0, 0
0, 1, 0, 0
0, 0, 1, 0]
Rotation matrices look a bit more complicated but is really quite simple to use too. Here are the rotation matrices for all three axes: ("r" being the level of rotation we want to add, in radians)
You might also be wondering what the fourth collumn in the matrix is for? Since there is no fourth row in the matrix, the values in the fourth column has no values to be multiplied with and is therefore just added to the result. Any value placed in one of the cells in this column will therefore be added as an offset (also called translation) along that axis. The following matrix is an example of an translation matrix witch will offset the subject by 2 pixels along the y axis.
[1, 0, 0, 0
0, 1, 0, 2
0, 0, 1, 0]
The Matrix3d class has methods for creating all these types of transformations through the static methods createScalingMatrix(), createXRotationMatrix(), createYRotationMatrix(), createZRotationMatrix(), createRotationMatrix() & createTranslationMatrix(). do multiplication through the static methods multiply() & multiplySeveral() & apply the transformations to Point3d instances throught the apply() method on either a Matrix3d object (which returns a new Point3d instance) or a Point3d object (which applyes the transformations to that particular instance).
Finishing up
Ok. So now we have set up our geometry, projected & drawn it on stage, and written the method responsible for rotating the cube. Next, we will add a call to this method from the createProjection() function: (Note: It is important to add this function call before calling the project() and draw() methods as we want to add the rotation before actually drawing the geometry on stage)
If we run the application now we won´t see any changes to the output because the _rotationX, _rotationY & _rotationZ properties are still set to 0. We could very easily add some rotation instantly by setting these properties to something else then zero, but since we want the cube to rotate over time we set up an enter frame listener, call the createProjection() method from there, and then increment one or several rotation properties by the desired amount:
Recompiling and running the app now should give something like the following result:
Now we can clearly see that the points are moving in relation to each other in a way that seems in tune with our perception of 3d space. Still, we have left out a crucial part of making the cube seem volumetric: Drawing the lines that connect the points together. I have intentionally left this part out until now to emphasize the fact that computed 3d geometry is nothing more then a collection of points in 3d space (refereed to as vertices in 3d lingo) that together make up a mesh. To make it more obvious, let's connect the dots by adding a drawLines() function:
/**
* Draws the lines that connects the eight corners of the cube
*/privatefunction drawLines ():void
{
graphics.lineStyle(1, COLOR);
// draw the first rectangle
graphics.moveTo(_2dPoints[0].x, _2dPoints[0].y);
graphics.lineTo(_2dPoints[1].x, _2dPoints[1].y);
graphics.lineTo(_2dPoints[2].x, _2dPoints[2].y);
graphics.lineTo(_2dPoints[3].x, _2dPoints[3].y);
graphics.lineTo(_2dPoints[0].x, _2dPoints[0].y);
// draw the second rectangle
graphics.moveTo(_2dPoints[4].x, _2dPoints[4].y);
graphics.lineTo(_2dPoints[5].x, _2dPoints[5].y);
graphics.lineTo(_2dPoints[6].x, _2dPoints[6].y);
graphics.lineTo(_2dPoints[7].x, _2dPoints[7].y);
graphics.lineTo(_2dPoints[4].x, _2dPoints[4].y);
// draw lines between the corners of the rectangles
graphics.moveTo(_2dPoints[0].x, _2dPoints[0].y);
graphics.lineTo(_2dPoints[4].x, _2dPoints[4].y);
graphics.moveTo(_2dPoints[1].x, _2dPoints[1].y);
graphics.lineTo(_2dPoints[5].x, _2dPoints[5].y);
graphics.moveTo(_2dPoints[2].x, _2dPoints[2].y);
graphics.lineTo(_2dPoints[6].x, _2dPoints[6].y);
graphics.moveTo(_2dPoints[3].x, _2dPoints[3].y);
graphics.lineTo(_2dPoints[7].x, _2dPoints[7].y);
}
and call that function from the createProjection() method:
Recompiling and running the application now should result in something like the spinning cube at the top of this article.
Now we can see clearly that the points in fact make up the geometry of a cube. Still, this simple mesh is not very interesting, and it the process of making it seems maybe a bit too elaborate for such a simple result. If we just wanted to make wireframe 3d-primitives (or even more advanced textured geometry) it would take a lot less work to just use a library like away3d or papervision3d. The real power of using this manual approach lies in the control we now have of each point in both 2d and 3d space. At anytime we can easily replace the dull, circular representation of each point with any displayObject to create something like a spinning cube of menu-items or a sphere of photos as an alternative to the well known photo carousel. Once we start playing with these concepts the possibilities are endless!
I will follow up this article shortly with some pretty interesting examples of what can be achieved by putting these techniques to use.
[Note: The preceding examples are structured in a way that aims to make the code readable and is no way optimized to ensure high performance]
We just launched a quiz game over at Bleed, as a fun way of promoting Ditt Distrikt's new community information services. The game challenges the user to play against other's from their local area to see who has the most geography skills. While doing so, the players also contribute to raising their home county's average score in the pursuit of being the best county in Norway.
I guess I'm the latest person to announce this, but nothing is better than hearing it directly from the source right? Anyways, the big news is that I'm going back to work at Bleed! (Well, actually I have been working there for about a month already, but things have been so busy that I have had no real time to sit down and think about it, let alone write about it).
For those you who don't know: I used to work there until some of my colleges broke out to start a new company, Anti, and I was asked to join. I stayed there for about a year before deciding to return to to my former employer a couple of months ago. As much as I like the people at Anti and the stuff they produce, I started feeling a bit lonely as the only developer/designer with a clear focus on interactive medias.
Looking forward, I am really excited about all the cool stuff happening at Bleed: We have just launched a new web & identity, our new offices are nearly finished and a couple of promising interns are joining us over the next couple of months. With several new & exciting clients and plans for an independent exhibition/book release in the pipeline, I'm shure that this is the beginning of the Bleed renaissance!
I'll keep you posted as soon as I have more news. In the meantime, be shure to check out our new site and take a look at some photos from our first week in the new offices:
I have really had a lot of things going on both at work & at home for the last couple of months, which has resulted in no posts at all since June. Pretty lame stuff! Anyways! One of the projects I have been working on is the Anti Sweden website, which was launched just a few weeks ago. The site is a pretty simple, news driven site, with some very basic shop functionality and one or two nifty graphical details.
About the project:
"We at the Norwegian design office Anti wanted to use our experience and knowledge of design to create our own brand - a brand that reflected our love of fashion, of graphic design, and which reflected the unremitting darkness that is at the heart of the Norwegian identity and is the seat of true Black Metal. Anti Sweden is that unholy creation!"
I just love the cover art for the Joy Division album Unknown Pleasures and I have had this idea for a while to try to create something interactive based on the main illustration. So here is my first attempt: The "Joy Division Equalizer". For now it is just a rough sketch and the code is very very quick and dirty, but I think it looks kinda' cool. Click here to check it out.
Here is the dirrrrrrty source: (Written & Compiled in Flex Builder 3)
We held a name ceremony for my daughter Isa this weekend (as a non-religious alternative to baptism), and I decided it was a great opportunity for me to do some print work for the first time in ages. Not to extensive though, simply a royal-ish kind of symbol made from the letters of her first name & some invitations. We also got the symbol printed on the cake, which I think came out pretty nice.
We launched the website for norwegian photography agency Tinagent a couple of months ago and so far the feedback we have gotten is just incredible! We are proud to say that the site was awarded with gold at both Visuelt and Gulltaggen, which is two of the most prestigious awards aiming to credit Norwegian graphic design and interactive marketing.
The front end is developed for Flash Player 10 using Flex Builder 3, backend is developed in php by norwegian company Modulez, graphic design by the excellent Martin Stousland and interactive design + front end development by yours truly.
Check out the site here, and be sure to spend some time browsing through the splendid work of some of Norway's most talented photographers.