Tonight, there will be shadows
Mark Oates

I have a goal tonight to get real-time shadows into my 3D arsenal.

Specifically, I'll be running through this tutorial.

I believe I have everything else setup and have a good shot. I'll be keeping an update log here, so please comment generously so I'm not stuck updating my last post with edits? Thanks!

Ok, first order of business... I need a nice simple "world" model that is good for checking shadows, so I'm off to blender to throw something together.

Alright, I've got a little scene created: :)

Okie dokie, The model is now working correctly inside the program and a good camera angle.

To keep things focused on just-get-things-working, I grabbed the glm:: header files that are used in the tutorial to build the ortho transform for the light projection. Thanks to Elias' cool new feature, I was able to peruse the code much more easily and skim-check if there were any conflicts between the glm matrix and Allegro's ALLEGRO_TRANSFORM. So far no red flags. :)

Alrightie, the difficulty curve has shot up. There are conflicts between my OpenGL/GLSL/version_numbers/Allegro/the_tutorial/my_knowledge_of_anything. For one reason or another, I am unable to create a framebuffer with this simple code (the glCheckFramebufferStatus line keeps sending me to "glCheckFrambufferStatus == FALSE") :( :

1GLuint FramebufferName = 0; 2 bool initialize() 3 //bool make_shadow_texture() 4 { 5 // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer. 6 glGenFramebuffers(1, &FramebufferName); 7 glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName); 8 std::cout << "FramebufferName==" << FramebufferName << std::endl; 9 10 // Depth texture. Slower than a depth buffer, but you can sample it later in your shader 11 GLuint depthTexture; 12 glGenTextures(1, &depthTexture); 13 glBindTexture(GL_TEXTURE_2D, depthTexture); 14 std::cout << "depthTexture==" << depthTexture << std::endl; 15 glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT, 1024, 1024, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0); 16 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 17 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 18 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 19 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 20 21 glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0); 22 23 glDrawBuffer(GL_NONE); // No color buffer is drawn to. 24 25 // Always check that our framebuffer is ok 26### if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 27 { 28 std::cout << "glCheckFrambufferStatus == FALSE" << std::endl; 29 return false; 30 } 31 std::cout << "glCheckFrambufferStatus == TRUE" << std::endl; 32 return true; 33 }

To keep moving what I might have to do is just create a shader on an ALLEGRO_BITMAP that draws the depth in RGB. more investigation to continue...

Also, to anybody who's interested, the tutorial's shaders have #version 330 core at the top, and upon compiling the shader I get the message "ERROR: version 330 is unavailable"

Wooohf! I finally made some headway. This whole time I was having difficulty drawing to an ALLEGRO_BITMAP; It was coming out blank. It turns out it was because the object(s) I was trying to draw (the scene model from earlier) was not being drawn in both views, despite me having created the infrastructure. The static world model was considered a "special case" in the engine and not included in the list of entities to render in all views. Good news is, now I have 2 projections with different rendering methods working simultaniously. :)

Camera View:
Light Projection View:
Depth is right around the corner. :)


What's going on here... :o:o

Mark Oates

A cacophony of stream-of-concious efforts. ;D

When attempting to render to my depth-view, the depth test is somehow exactly flipped.

I'm even at the point now where I'm getting glitch art: 8-)

It looks like the shadows won't arrive for another day. Perhaps my weary body will be better equipped after a nights rest.

Gideon Weems

If this turns out to be good, we should put it on the wiki.

Mark Oates

So, the challenge continues!

In discouragement at being unfamiliar with rendering to a strict depth buffer, I decided to just create a simple shader that would draw depth to an ALLEGRO_BITMAP. What I did was pass into the shader the real-world-position of the camera, and calculate distance() to the real-world-position of the vertex. It worked... ish:
Darker colors represent being closer to the camera, while lighter colors are further.

But why is the floor white? My theory is that since I was calculating the distance to each vertex, glsl was calculating that the four corners of the bottom plane were at a far distance from the camera, and then interpolating those values to fill in the rest of the geometry.

At that point I realized that the camera's position is already (0,0,0) in the shader after the al_porjview_matrix is multiplied to the object in the shader, and I could avoid having to calculate a million distance() formulas, which are really slow. And the depth value that I need is already in the 4th component of the shader's gl_Position.

I coded it in, and it worked!
This is the actual depth data that I need (but to make it more visible, I had to multiply it by 0.05 because my view frustum is verrrrry deep. When I make it a frustum for the light projection, it probably won't need to be nearly as deep)

So much more to do! :D


That stuff is pretty cool Mark !

Mark Oates

Thanks :)

Ok, it looks like I have run into a problem. When drawing to an ALLEGRO_BITMAP that is not the backbuffer (or a subbitmap of the backbuffer), the depth sorting gets all messed up. This is what the scene looks like when drawn per the usual way:


Depth Shader When Drawn On Backbuffer: - Looks good, everything appears to work out.

Depth Shader On A Target Bitamp (that is NOT a Backbuffer): - EEKS!

I'm not sure what's causing this problem. Any ideas? I'll try it without using a shader.

Oh well interesting, it also has the same problem when not using the shader.

Normal Drawing on a Target Bitmap (that is NOT a Backbuffer): - Also EEKS!

Bruce Pascoe

I'm guessing it's because only the backbuffer has a Z-buffer. A regular bitmap is just a flat matrix of pixels, where would you store the depth info?


It's possible for a bitmap to have a z buffer, but Allegro provides no means to create this. See

Mark Oates

I'm guessing it's because only the backbuffer has a Z-buffer. A regular bitmap is just a flat matrix of pixels, where would you store the depth info?

Did not know that.

SiegeLord said:

It's possible for a bitmap to have a z buffer, but Allegro provides no means to create this. See

Would use! I was wondering why offscreen bitmaps don't have multi-sampling.

Either way...



// ... continue on as if nothing happened

Nothing will stop me! >:E


Also, incidentally, I noticed something wrong with the depth image:


I thought there was something wrong with my depth algorithm, so I looked into it and it turns out the geometry was messed up. The cone was hovering above the plane by about 0.2:

So I fixed that:


Ok. I'm excited to finally have a (sort of) depth map! Next up we should see some semblance of shadows. :)

Bruce Pascoe

That hovering cone reminds me of old 90s CGI cartoons (e.g. Beast Wars) where it often looked like the characters were walking just above the ground.

Mark Oates

Ok, I owe you guys an update 8-)

I have achieved semblance of shadows!

This was the very first time where all the components are accounted for!


Everything's all out of wack, but it's all in there. All that's left is to work out the math just inside the shader. I added a bunch of little floating objects to make the depth more palatable. Note that this is not actual shadows mapped, rather it's the depth image just plastered onto the models as if it were a regular plain old texture map.
The floating things are just reflective AllegroFlare logos.

It's cool 'cause if you look at the floor you can see that the rotating logos have "shadows" that are also rotating as well.

That's where it all started.

After that, I needed to make sure the 0.05 that I multiplied to the depth wouldn't be a problem.

^This is me checking that the light depth and camera depth data are not being confused with each other. In this image, the darker pixels represent smaller distance to the light source. So this shows that the light source is on the other side of the camera and that data is separate from the active viewing angle. It also shows that the value ranges for the light depth and camera depth are (essentially) the same.

Good. Everything is in order.

Now it's time to calculate an actual shadow map onto the scene.

And finally, without further ado, here is the first image showing an actual shadow projection:


All screwed up. 8-)

If you look carefully, you can see that the texture is looping for some reason. So somewhere the UV mapping coordinates are scaled incorrectly. So I scale them by some multiple:
^ Oops! wrong direction. :P I multiplied when I should have divided.

Cool, but the alignment is off, too. So I put in 2 dials to let me offset the texture:


At this point I create a bunch of cool abstract art. But it's still a little tough to se what's going on so I put in some crappy texturing to get a better sense of space and depth:


It's so close. 8-) It looks like the depth calculation is correct - shadows are deforming onto the geometry correctly. It's cool how it working and not working at the same time. It's just that there is something amis with the actual projection itself.

... until next time ... ;)


This is a fun and interesting read! Please never stop ☺


Moar ! ;D


This is great and quite like the updates. I would definitely support ($$) an Allegro 3D solution even if free or low cost alternatives exist.

Will you consider integrating for model loading?

Peter Hull

On the other hand, you are 300% over schedule and not finished yet. Work harder, Oates.


Mark Oates

I wanted to add that when trying to correctly align the light texture into the scene, one method I tried was to place the camera and the light at the exact same position. In theory, this would allow me to tinker with the UV position and scale, and see when the shadow projection and scene were in sync:


^ This is the light and the camera placed and viewing the scene from the exact same position, both are rotating around the scene together in sync.
;D Looks cool :D Unfortunately, it didn't prove to be as useful as I had thought so I went back to separate locations.

Also, I found out that you cannot make your own variables in GLSL with a gl_* prefix. I tried naming a variable gl_Position_from_light and got this wonderful work of bow tie art:


and an error message that read ERROR: 0:13: Identifier name 'gl_Position_from_light' cannot start with 'gl_'

Anyway, that's all in the past.


This is where we are now. As you can see by the end of the video, we're so close!


Ahgh! So close you can almost taste it! ;D

If you look closely at how the shadows are distorting, you can see that all the shadows seem to be bending inward towards the back, rather than outward. I think the reason is because I am using a perspective projection for the light, when I should be using an orthographic projection. It's as if the shadows at the center are OK, but further on the edge of the camera, they seem to bend in and shrink towards the back, like what would happen in a perspective projection.

So, if I "unproject" that perspective distortion...

Eh? What do you think? ;)


I dunno, if you're simulating nearby a point light, then a projective transformation makes sense. If you're simulating a far away light, then orthographic would be fine.

Gideon Weems

ERROR: 0:13: Identifier name 'gl_Position_from_light' cannot start with 'gl_'

What sort of pre-processor magic makes this possible? It reminds me of printf-related warnings. The pre-processor sometimes comments about invalid format strings.

Will you be able to do this in real time?

Bruce Pascoe

I always assumed it was the compiler that generated the printf warnings. In that case it's just a matter of looking at the arguments of a printf() call--since the compiler already knows how to read the grammar--and parsing the format string. I would assume it's similar to how JS treats eval() differently depending on whether it's actually called as "eval" versus through an alias.

Peter Hull

And he answered them not, but raised his eyes to the heavens, saying "Tonight, there will be shadows, threads shall be locked, and Phipps shall return."

From the Gospel of St Mark of Oates, 12:28

Bruce Pascoe

Who is this Phipps guy anyway?

Mark Oates

OK! Just got back from a wedding cruise and have had time to work on this puppy. I won't reveal to the end, but things are working and the shadows are straight up sexy.


SiegeLord said:

I dunno, if you're simulating nearby a point light, then a projective transformation makes sense. If you're simulating a far away light, then orthographic would be fine.

A perspective transform should work. In the tutorial, it uses a ortho transform, but there is another later section that says if you want to use a perspective transform then (...) and I didn't read that part. So, I dove into trying to fix it by switching back to ortho and it worked.

Sort of.

Part 1: Getting to the Orthographic Projection

It took awhile to get the ortho transform in place, since you can't just simply swap out al_perspective_transform with al_orthographic_transform. Here's what it looked like when I just swapped them out:


When I looked closer, I could see these tiiiiiiiiny pixels in the top left of the frame, which happens to be the whole scene:


The whole scene be about 10 'units' wide in geometry, which in iso projection is about 10 pixels. So, I had to scale it up and center it to match the view I had previously:



I ended up using the exact same ortho projection for the light and the scene, but from different viewing angles. That way I wouldn't have to worry about possible inconsistencies when merging the two projections for a shadow.

Ok, here's the interesting part. Notice that in the upper right corner (that's the light's view depth map) that the entire scene is black? This happened when I switched to ortho mode.

Something new is up with the depth z-values, so I tried a bunch of different things to see what I could get out of the pixel data. I tried curving the brightness of the colors in a bunch of different ways...


...but found out that the z values were all flat 0.0.

Not sure what to do next, I tried just on a whim drawing only using the green color component (e.g. rgba(color.g, color.g, color.g, 1.0) ) and this came out:


Well that's kinda strange. Previously, I was only using the color.a component, since the z-value for the pixel is stored in there (e.g. rgba(color.a, color.a, color.a, 1.0) ). Why now, would the green value just produce a simple gradient? So I investigated further and drew the image with proper colors rgba(color.r, color.g, color.b, 1.0):


Strange again, I'm not sure what's going on in this picture... but... if you look reeeeeealy closely, you can see some very faint shapes in the RGBA render. Very very faint, and they appear to be in the blue part of the image.

:) It turns out, when using an orthographic projection, depth values are stored in the 3rd color component - blue. Instead of RGBA (or XYZD), it's RGB (XYD). It makes sense; there's no need to transform a z value once it's in orthographic projection. The only coordinates you would need are x, y, and depth. So here's the light projection drawn using only the 3rd color component rgba(color.b, color.b, color.b, 1.0).


With a bit of texture position tweaking, the shadow mapping lines up! ;D ;D 8-) 8-)


When everything is lined up there's really bad "shadow acne" but who cares!! It works!!!


Part 2: Getting Rid of the Noise

The hard part is over, from this point on it's a battle of filtering out shadow noise, and I ended up using a solution of my own (not mentioned in the tutorial).


Will you be able to do this [] in real time?

Funny you mentioned that actually. As I was trying to figure out shadows, it was a bit difficult to see what was going on with the checker texture (because of that exact "checker shadow illusion") so I swapped out with a grass texture.

By filtering out for the "shadow acne" noise, at best I was able to get it to look like this:


and at worst it looked like this:


If you look closely, the worst-case-scenario-stripes-pattern is isolated to geometry that is facing away from the light, so to catch this case I ended up using a combination of plain-old-school shading combined with the casted shadows.

Here's the scene rendered with plain-old-school shading. This uses the light's point as the source and does not have a falloff for distance. Essentially, a plane is brighter when it's facing the light, and darker as it turns away:


Then I curve it, so it's either black or white. Anything above 0.5 is pushed to 1.0 and anything below it is pushed to 0.0. Looks like this:


Then I combine this shading data with the previous shadow map, using only the darkest colors (works by simply multiplying color1 * color2). Looks like this:


All I have to do now is nicely blend the shadow with the normal shading of the scene and...


...that is the first actual fully properly shadow mapped scene. And, it turns out that when I change the camera's projection to perspective, it worked without a hitch!! ;D


(notice it renders the skybox now that the projection is perspective.)

Part 3: The Future

I started combining the shadows with different colors, and several different materials and shaders:


Things are starting to get serious:



Bruce Pascoe

That last shot looks awesome. Who knew Allegro could do full 3D (as opposed to 2.5D)? Not me! :)


Mark you rock.

Edit: This really is awesome.

duncan perham

WOW, thats pretty awsome, any chance you could do a full idiots guide tutorial for it :). I would love to be able to do a lot of that stuff.

Edgar Reynaldo

This ^^

It would be awesome if you could write some kind of guide to creating shadows.


Really really remarkable, as in Mark's standards.

That's some serious 3D!

Chris Katko

Yeah, I'm going to eventually need to do everything he's done so far. It'd be great to see a tutorial / write-up. :)

Neil Roy

That's a nice little tutorial series. Just checked it out myself.


Nice work. Have you played around with Blender's water/soft body simulation physics? It's pretty neat. It takes a beast of a cpu to render, though.

Neil Roy

I never really tried doing 3D with Allegro, but this makes me reconsider it.

Mark Oates
Neil Roy said:

I never really tried doing 3D with Allegro, but this makes me reconsider it.

Awesome! ;D

I want to create a nice clean tutorial that walks through Allegro in 3D. Something where, by the end of the first 2 paragraphs or so you have a foolproof 3D view already setup. The foundations are ALLEGRO_VERTEX and al_perspective_transform(). Wrapping all of that into a clean tutorial that's enjoyable and stupid easy would be a lot of fun.

I should listen to the sloth:

Neil Roy

I want to create a nice clean tutorial that walks through Allegro in 3D. Something where, by the end of the first 2 paragraphs or so you have a foolproof 3D view already setup. The foundations are ALLEGRO_VERTEX and al_perspective_transform(). Wrapping all of that into a clean tutorial that's enjoyable and stupid easy would be a lot of fun.

That would be really nice. I have done some things with 3D, but none of it uses shaders, just the old style OpenGL stuff. I just recently FINALLY learned how to use shaders and was surprised that they were simpler to use that I thought, it's just now I need to unlearn what I learned about opengl previously! ;)

I done a simple, blocky style City3D (before Minecraft, I didn't know I was ahead of my time back then ;D )... I used Allegro for the simple 2D level editor for it. The program itself is straight Windows + OpenGL code, nothing else, which i am sort of proud of. ;)


I also created a little test program to experiment with terrain generation years ago and every once in a while I putter around with it. It's Glut + OpenGL.


Anyone is welcome to download them and play around with them (Windows). They're all available here...

Thread #615966. Printed from