Curve : from 2D to 3D
Published on 03/10/2010
6 min read
The point is to make 2D Curves go 3D.
openGL + SDL + C++ with Visual Studio
Step 1: curve definition
It is several points added one by one. In openGL, the more points are numerous, the more the curve gets a high definition but the more the application will be slowed. Actually, in openGL, a curve gets a determined number of points: 20, 30, 100!
Each point is linked to the following thanks to a line (
GL_LINES). A point is defined by 3 coordinates: x, y and z. To draw a curve, we use the plan XY with Z=0 (The less numerous the coordinates are, the faster the calculations are).
Step 2: To configure the camera
In the case we draw a curve:
gluOrtho2D(0,width,0,heigth);is enough because this function specifies the area visible by our camera. If we configure this function with the window sizes, the mouse coordinates calculated by openGL correspond to the mouse coordinates on the window.
In the case we add a 3D view too (for 3D renders: revolution and extrusion for example):
- We use :
gluOrtho2D(0,width,0,heigth);to determine the area visible by our camera. So the angle of the camera view is 90 degrees (his shape being proportional: width/heigth). Besides, this camera displays objects from 0.1 to 2000.
To use this camera (after configuring it), we call gluLookAt wich places and direct the camera.
Step 3: from 2D to 3D
Actually I don’t really translate a 2D space to a 3D space; I simulate a 2D space with a 3D space: to do this, I put the camera on an axis (Z) in front of the plan XY and lock it. My 3D space consists of unlocking the camera so that this one looks the scene center with any angles.
At first sight, it seems really easy to do that. However the distance between the camera and the plan modifies the render quality. Actually:
In real 2D (only coordinates XY),
gluOrtho2D automatically delimits the visible area so that we can draw our curve. In simulated 2D (with the coordinate Z = 0), this area is not specified, it is determined according to
gluLookAt() configuration. So the bigger the vision angle is, the bigger the visible area is. Besides the more the camera go away from, the bigger the visible area is too.
How to know the size of the area on which we draw? We had to use the trigonometry. Actually, when the camera looks at a position, it is done with a specific angle and a specific distance from the plan. So we get a triangle like represented opposite.
Therefore, we settle the distance of the camera according to the size of the plan.
Step 4: to create a background
It is really easy to put a background in a 2D space with
gluOrtho(). We have go to create a quad with it size equal to the size window and we add a texture on it. It size is the one described in
In a 3D space, we need:
- To position a quad in the space center
- To calculate the size of the quad. This will contain a texture for the background. To calculate this size, we use the same process that the one in step 3 but we know the distance between the camera and the plan and we try to find the plan size. This distance represents the one between the camera and the space center and the one between the space center and the plan. Actually the plan is not positioned in the space center to avoid any mesh cut it.
- To move the quad behind the space center
- To angle the quad according to the camera position. In my program, I use the two camera rotation angles on my quad to make it in front of the camera.
In openGl, a resizing process for textures is applied on texture with a width and a height not equal that is why – for instance - the text become blurred. Besides, to avoid distance effects (blur) due to perspective and distance, I mustn’t use mipmap.
Step 5: draw a curve
When I click on the screen, you get the mouse positions on screen thanks to SDL. I use
event.button.button == SDL_BUTTON_LEFT to be certain to click with the mouse left bottom. Then, I read the mouse position with
event.motion.x for x et
event.motion.y for y.
If your system coordinates are differents between SDL and openGL, you had to do:
height_screen- event.motion.y for y
Once I get mouse position, I have go to use formulas to interpolate a curve between two points. These interpolations can be calculated in various ways. In the video, I encoded Bezier and B-spline curves. In another project I encoded Hermite and Newton curves (easier than Bezier and B-spline).
May be you wonder what means C0, C1 et C2? It is a way to link two curves. Actually, the more the points are numerous, the more the calculations are long. This is the reason why it is easier to create several little curves and to connect them to each other. So C0 connects two curves without interpolation. C1 connects two curves considering the curves shape. C2 connects two curves factoring in the shape and the acceleration.
Step 6: create shapes with curves
Creating curves this ways (step 5) is not really useful so why not trying to create shapes now? For this try, I have tested two processes: extrusion and revolution.
The extrusion is simple. I use once more the curve points with a new depth and I create quads to link each four points. In openGl, these quads can be full or not. In my project, when quads are full I had light to estimate easier the shape created. Without light, the shape seems flat.
For the revolution, I choose a rotation angle (10° for example) and calculate the new position of each point for each angle (10, 20, 30… 360). Besides, I choose a distance from the center to determine the shape size. The rotation must turn around an axis what means one coordinate doesn’t change. In this example, only X and Y change but not Z.
newX = cos(angleParcourt)*oldX; newY = sin(angleParcourt)*oldX; newZ=oldY;