Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

The GDI magic - Rendering reflections and smooth shadows

0.00/5 (No votes)
21 Feb 2007 1  
An article on rendering reflections and shadows using Windows GDI.
Screenshot - screenshot.png

Introduction

This article is about a simple approach to rendering reflections and smooth shadows of 3D objects using just Windows GDI. There are no true 3D objects on the screen. We will just play with bitmaps and a few calls to the BitBlt() and PlgBlt() Win32 API functions.

Background

I have always liked how people use CPU power to simulate real-world behavior of objects. In the 3D games available today, I have seen whole virtual worlds created just for one purpose - to have fun and to play. Since I was not a 3D modeling expert, but I wanted to create something that looked real in a scene, I used the only weapon I have at the moment: Windows GDI. I have built a demo scene to show people who wonder how to do things like this. It was more than easy, but took some time to create. Here is how it was done...

The scene background

The background of the scene is a flat polygon with a texture. It is easy to do with a simple call to the PlgBlt() method. It takes as arguments the HDC of the destination, polygon vertices, the HDC of the source bitmap loaded with a simple LoadBitmap() method call, offsets and dimensions of the loaded bitmap, and some masking arguments, which are set to NULL here. See the code below:

// Drawing textured polygon

CBitmap bgBitmap;
bgBitmap.LoadBitmap(IDB_BITMAP_BACKGROUND);
BITMAP bmp;
bgBitmap.GetBitmap(&bmp);
POINT pBgBitmapPoints[3] = {{50, 250}, {380, 250}, {200, 400}};
PlqBlt(hDestDC, pBgBitmapPoints, hSrcDC, 0, 0, bmp.bmWidth, bmp.bmHeight, NULL, 0, 0);

The exact source code can be found in the DrawBackground() method in the demo project.

The object reflection

Now comes the most interesting part: how can we render the reflection of the object so that the surface the object stands on can be seen as shiny and smooth (like a glass surface)? Solution: we will render the same object upside-down on that surface, but using a small trick. First, we render just the sides of the object that can have a reflection. Second, we will do a "decreasing alpha blend" technique while rendering those sides, so that we get a realistic effect. Here is the algorithm:

For each side that has a reflection:

  • Blit a part of the screen which is covered with this side using BitBlt() method on the first bitmap
  • Blit inversed side using PlgBlt() method on the second bitmap
  • Merge these two bitmaps using a decreasing alpha blending technique as you move along each row
  • The result is in the second bitmap now
  • Blit this second bitmap on the screen again using BitBlt() method

The exact source code can be found in the DrawReflectedBox() method in the demo project.

The object shadows

Rendering of the shadow can be done in simple way or a complex one - it's all up to you. I have decided to use a simple technique called "rendering a shadow using ground transformation on the z=0 plane", and I made it even more simple. I didn't use exact math in this example because it is not so important, but even then the shadow still looks real. The light source is at infinity, so the light rays are parallel. This means that the projection of the object on the ground (z=0 plane) keeps the dimensions of the object itself. Knowing that, I did the following:

  • Blit the part of the screen that is covered with this shadow using the BitBlt() method on the first bitmap
  • Blit the part of the screen that is covered with this shadow using the BitBlt() method on the second bitmap
  • Filter the second bitmap using a simple blur filtering technique (the filter size is up to you)
  • Merge these two bitmaps using a constant alpha blending technique as you move along each row
  • The result is in the second bitmap now
  • Blit this second bitmap onto the screen again using the BitBlt() method

The exact source code can be found in the DrawShadow() method in the demo project.

The main object

The main object was rendered similarly to the scene background. It is a box with 3 sides (polygons) with different textures each. It is rendered at the end, as the last object on the scene.

The exact source code can be found in the DrawOriginalBox() method in the demo project.

Question about the speed of rendering

Please understand that this demo is just a simple example that does not try to break any speed record with GDI. It just shows that some things can be done, not that they should be done in this way at all. Please, refer to DirectX/OpenGL documentation for high quality 3D graphics rendering on the Windows platforms.

Points of Interest

I have learned while doing this example that it is possible to create a high quality real-world scene just by using simple Win32 API calls for GDI rendering. So, there is something that can be done (and look nice) with GDI, which has no default support for any of the DirectX/OpenGL advanced rendering modes: antialiasing, shadowing, reflections, etc.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here