Create 3D Graphics without OpenGL or DirectX

Environment: VC6

This program is doing exactly what you see in the figure above.

First, a little theory:

1. Baric 3D Transformations:

We have Viewer and Cub in a real 3D world.

  • Viewer—It is a three-dimensional point V(x, y, z) that represents yourself.
  • Cub—It is represented by eight edges, every with three-dimensional coordinates P (x, y, z).

1.1 Translation:

Normally, the Viewer is moving around the Cube with a fixed step. The formula is simple:

Vix = Vx + t
Viy = Vy + t
Viz = Vz + t

1.2 Rotation:

It is implemented very simply, by moving the centre of Cub to the beginning of the coordinate system (Viewer), rotating around the desired axis, and then moving it back to its initial position. Here is the rotation formula:

Xi = X * cos(alpha) - Y * sin(alpha)
Yi = X * sin(alpha) + Y * cos(alpha)

It comes from the following picture:

First we have:

Xi = r * cos(alpha + gamma) = r *cos(gamma)*cos(alpha) -
                              r *sin(gamma)*sin(alpha)
Yi = r * sin(alpha + gamma) = r *cos(gamma)*sin(alpha) -
                              r *sin(gamma)*cos(alpha)

But we have: X = r * cos(gamma) and Y = r * sin(gamma)

So here it comes:

Xi = X * cos(alpha) - Y * sin(alpha)
Yi = X * sin(alpha) + Y * cos(alpha)

You could find the whole theory in the chapter titled "Geometrical Transformations" in Computer Graphics Principles and Practice by Foley, Van Dam, Feiner, and Hughes. For the sake of simplicity, I do not represent the transformations in homogenous coordinates and matrixes, although the first verson of the program I developed in this way. Here, I do not cover "Scaling" either.

2. Projection:

Here we have again Viewer, Cub, and Projection Plane in a real 3D world.

  • Projection Plane—the computer screen.
  • Delta—the distance between the Viewer and the Projection Plane.

Below are views along the Y-axis and X-axis.

Every point P (x, y, z) projects into the Projection Plane into two coordinates (Xp, Yp). The formula is simple and it applies to any of the Cub edges:

Xp / delta = X / Z ==> Xp = X * delta  / Z
Yp / delta = Y / Z ==> Yp = Y * delta  / Z

3. Visual Surface Determination:

Here, I implement one very simple algorithm. First, I calculate the cross product (which is the normal vector) of the polygon - Np. Then, I calculate the dot product of the normal (Np) and the line of sight vector (from Viewer to the polygon) - N. If Np x N > 0, this polygon is visible. For details, look at Chapter 5.2, "Cooling and Clipping," in 3D Computer Graphics by Alan Watt.

4. Drawing Projected Points (Pixel by Pixel):

Please refer to Chapter 3, "Basic Graphics Algorithms for Drawing 2D Primitives," in Computer Graphics Principles and Practice by Foley, Van Dam, Feiner, and Hughes. I particularly used "Basic Incremental Algorithm" and "Midpoint Algorithm" for drawing lines and "Cohen-Sutherland Line Clipping Algorithm" for clipping the lines.

5. Here Is the Whole Lifecycle:

  1. We have a 3D transformation on the screen.
  2. Then, visual service determination and projection of the Cub is added.
  3. Then, drawing projected points (pixel by pixel) into DIB are created.
  4. Finally, transfer this DIB onto the screen.

Source Code Notices:

  1. dibcub.cpp—The main module of the program. It is a standard Windows program that responds to some messages:
    • WM_CREATE: Crates a cub (which consists of eight polygons) and a "Viewer" in real 3D coordinates.
    • WM_PAINT: 3D - 2D conversion and draw the Cub pixel by pixel into a DIB. Then, transfer this DIB onto the screen.
    • WM_SIZE: Recreates the DIB object with the new screen size
    • WM_KEYDOWN: Here are implemented the basic 3D transformations depending on the user activity (Please check the theory section.)

  2. figure.cpp/cub.cpp (Cfigure /CCub):
    • Cfigure: Encapsulates the functionality of a 3D figure. It contains a list of the edges of the figure (vertex list) and a list of its polygons. The vertex list consists of real-world points.
    • CCub: A real 3D figure that inherits all basic functionality from the CFigure class. Properly Fills the vertex and polygon lists.
    • CCub::Draw(): Draws the figure on the screen by drawing all its visible polygons.
    • CCub::IsPolygonVisible():Determines whether the polygon is visible from the Viewer.

  3. polygon.cpp/rectpolygon.cpp: Encapsulates - Cpolygon/CrectPolygon classes.

    Polygon is a basic part of every figure. Every polygon contains pointers to the figure's vertex list (edge list) in 3D coordinates. Rectpolygon inherits all the functionality of Cpolygon. In addition, it fills out its surface with a particular colour.

  4. vertex.cpp (CVertex)—Represents a point in a real-world (3D) coordinate system.

  5. primitiv.cpp—Encapsulates some basic graphics algorithms for drawing 2D primitives. At present, they include only lines.

  6. dbstruc.cpp (CDib)—Encapsulates a 16-color DIB. Do not use a color table.
    1 px. = 1 word.

  7. llist.cpp (VLList/ VLListIterator)—Encapsulates a linked list and Iterator to this list.

Some time ago a friend of mine, who is very good in this matter, told me that if you want to be good with computer graphics, you should not start with OpenGL or DirectX or any other graphic "toolkit" but simply from the ground up. Let me admit that I am not a Computer Graphics guru. I did this in my free time—just for fun! Computer graphics has always been a challenge for every software developer. It is a challenge for your maths skills, it is hard to be debugged, and finally it is a big pleasure when you see your results in full motion on the screen—much better than implementing "boring" business rules. What do you think? Well, if you are interested in 2D graphics, you could look at A Simple, Flicker-Free 2D Animation Demo.

Downloads

Download demo project - 24 Kb
Download source - 34 Kb


Comments

  • great,nice,but..

    Posted by Legacy on 05/09/2003 12:00am

    Originally posted by: Mike

    Hello at all
    i have a little problem.I want to create 2 axis(x,y) with negative numbers(it's possible) in 2 dimensions.
    I must create this program in C++ langage....so can one of you help me please??
    Thanks in advance...

    Reply
  • nice, but...

    Posted by Legacy on 02/24/2003 12:00am

    Originally posted by: johann

    There is a painting error by resizing the window.
    Make the window smaller during you slowly move the mouse: the cub image is corrupted !

    • found a fix

      Posted by fvaneijk on 09/26/2004 06:15am

      replace
      
      void CDib::Pixel(int x, int y, COLORREF color)
      {
      	x = max(x, 0);
      	x = min(x, m_biWidth-1);
      	y = max(y, 0);
      	y = min(y, m_biHeight-1);
      
      	*(m_pBitMap + y*m_biWidth + x) = (WORD) (
      					(((GetRValue(color)) & (31 << 3)) << 7)|
                            (((GetGValue(color)) & (31 << 3)) << 2)|
                            (((GetBValue(color)) & (31 << 3)) >> 3));
      }
      
      with 
      
      void CDib::Pixel(int x, int y, COLORREF color)
      {
      	x = max(x, 0);
      	x = min(x, m_biWidth-1);
      	y = max(y, 0);
      	y = min(y, m_biHeight-1);
      
        *(m_pBitMap + y*this->m_Pitch/2 + x) = (WORD) (
      					(((GetRValue(color)) & (31 << 3)) << 7)|
                            (((GetGValue(color)) & (31 << 3)) << 2)|
                            (((GetBValue(color)) & (31 << 3)) >> 3));
      }
      
      note the use of m_Pitch/2 instead of m_biWidth

      Reply
    Reply
  • real Nice!

    Posted by Legacy on 02/23/2003 12:00am

    Originally posted by: James

    THis shows how math plays the role not the OpenGl or DirectX that we don't really need to learn Graphics helps me learn more advance math skills!

    Reply
  • Nice, thank you!

    Posted by Legacy on 02/22/2003 12:00am

    Originally posted by: rayzhao

    It is a great tutorial material. Thank you very much. I appreciate it.

    rayzhao

    Reply
  • Misconcept shows in it

    Posted by Legacy on 02/21/2003 12:00am

    Originally posted by: Vitaly

    Such math lay-downs are versatile, and justify in many fields of application but in one you chose. This is called devolution/degeneration in the background of DirectX and OpenGL. What's the point?

    Cheers!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • Live Event Date: November 18, 2014 @ 11:00 a.m. EST As you embrace the hybrid world of on-premise and cloud applications, often accessed via mobile devices, you now have to be concerned that cybercriminals have yet another vehicle to attack your business. In fact, the average cost of cybercrime has increased over 10% in the last year, and this applies to businesses of all sizes. Attend this webinar to hear David Monahan, Security Research Director at EMA, and Dana Epp, recognized security luminary from …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds