Lighting and Allegro 5
Myrdos

I was looking at the Allegro 5 examples, some of which are very sexy, and I thought it would be cool to add a 'headlights' effect to my game like so:

{"name":"one_headlight.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/4\/343d78b66f23dbb6f3ccf7e52c2c93cd.png","w":641,"h":364,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/4\/343d78b66f23dbb6f3ccf7e52c2c93cd"}one_headlight.png

Can it be done with Allegro, or would it require OpenGL? I'd like the light to be as realistic as possible.

Also, would it be possible to use Allegro for the drawing, and OpenGL only for the lighting? Or, once you use OpenGL, do you have to use it for everything?

Trent Gamblin

If you just want to do what's in the screenshot, you can do that easily with primitives and blending. I don't know how realistic you want to go though... you can mix opengl and allegro calls no problem as long as you don't mess up opengl state and you know how allegro is affecting opengl state as well.

Myrdos

I don't know how realistic you want to go though...

Well, I'd like to have the light fade with distance. The only blending function I see is "al_set_blender". Would I have to set the blending function for every pixel in software, depending on how far the pixel is from light source(s)?

Quote:

as long as you don't mess up opengl state and you know how allegro is affecting opengl state as well

Sounds tricky, but doable.

Trent Gamblin

Fading with distance shouldn't be a big problem if the light is a constant shape. You could have a bitmap that is a white triangle/trapezoid that fade from full alpha (or whatever you want) down to no alpha to the end. Then draw that with additive blending, maybe draw it more than once for a better effect. al_set_blender is all you need. You can achieve an additive blender with al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ONE).

Myrdos

You could have a bitmap that is a white triangle/trapezoid that fade from full alpha (or whatever you want) down to no alpha to the end.

I like that! I think it might even be possible to add shadowing by blitting the terrain in front of the car onto the 'light triangle', such that the terrain areas have zero alpha.

Hmm... I could even pre-process my terrain so that the edges of the terrain are illuminated, but the interior remains dark.

{"name":"another_headlight.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/a\/fa2c531a2279ea9733927c783005c6cf.png","w":530,"h":322,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/a\/fa2c531a2279ea9733927c783005c6cf"}another_headlight.png

[EDIT] Oops, forgot to ask: Why would applying the light triangle multiple times improve the results?

Trent Gamblin

The only reason it might help is to make it brighter. You could also draw it with less or more alpha, even modifying them in real time so that it looks like it's "glowing". I don't recall what sort of transparency you'll see if you have say full alpha and draw additively... that's why I suggested drawing more than once. If it's 100% white at that point then drawing again wouldn't help but if was 50% you could get a brighter light by drawing more than once.

Myrdos

I guess I'll experiment with it and see what it looks like.

Thanks for all the help!

[EDIT] Hmmm... no way to give cookies. :-/

Trent Gamblin

That's ok, I need to watch my weight anyway :P.

Matthew Leverton

It shouldn't brighten the area behind the hill as dramatically as your screenshot shows, although it may not really matter much in the context of the game.

Trent Gamblin

See the last image in the thread. He plans to draw the "scenery" back over top of the light except for an edge which I think will work ok.

Myrdos

It shouldn't brighten the area behind the hill as dramatically as your screenshot shows, although it may not really matter much in the context of the game.

I know, if you can think of a way to add shadows I'd be interested...

My attempt at a light so far has been of limited success:

{"name":"light1.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/f\/5f1f05301f61f01d14e87401ebf8faf2.png","w":648,"h":485,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/f\/5f1f05301f61f01d14e87401ebf8faf2"}light1.png

It's creating a kind of 'fog' effect, rather than illuminating anything. I'm drawing it like so:

         al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ONE);
         al_draw_scaled_bitmap(spotlight, 0, 0,
                               al_get_bitmap_width(spotlight), al_get_bitmap_height(spotlight),
                               50, 60,
                               al_get_bitmap_width(spotlight) * 2, al_get_bitmap_height(spotlight) * 2,
                               0);

The spotlight bitmap is made of white pixels with varying amounts of alpha. Any ideas on how to improve this?

Trent Gamblin

What would really improve it is if you can somehow get the background image into a trapezoid and draw that with additive blending... I'm not sure how you'd do that without shaders except using the stencil buffer. Both are kind of "exotic". To do it with the stencil buffer you'd have a stencil the shape of the trapezoid and a translucent->black version of the trapezoid. You would draw from the display (or if that turns out to be too slow, you could have an intermediate buffer) to a bitmap with the stencil on so only the trapezoid gets drawn (the rest would be pure alpha or black) and then draw the black/alpha bitmap over that before drawing it with additive blending.

EDIT:
Scratch that, you could use have extra black in the black/alpha image wherever the trapezoid doesn't touch. So no need for stencil or anything exotic.

gnolam

Hint: Lightmaps don't actually lighten pixels, they darken them.

Myrdos
gnolam said:

Hint: Lightmaps don't actually lighten pixels, they darken them.

So... I should selectively darken the terrain, except where the light is. Makes sense...

Scratch that, you could use have extra black in the black/alpha image wherever the trapezoid doesn't touch. So no need for stencil or anything exotic.

I not sure if I completely follow you here, but is that similar to what I have now? The parts of the spotlight that aren't 'light' are black with 0 alpha.

Let me run this past you:

1) I fill the back buffer with black.

2) I draw my terrain to a bitmap, using 80% transparency.

3) I draw the light to the bitmap, but use blending such that the alpha value of the light pixels are added to the terrain, but not the colors.

4) I blit the result to the back buffer

Is that type of blending possible?

Trent Gamblin

{"name":"604620","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/4\/842ef10cbe84998c0a06ce138a69313f.png","w":646,"h":430,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/4\/842ef10cbe84998c0a06ce138a69313f"}604620

That's what I got with what I said above. Easier for me to post code than to describe it. The image I used is also attached.

#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_image.h> 3 4int main(void) 5{ 6 al_init(); 7 al_init_image_addon(); 8 9 ALLEGRO_DISPLAY *display = al_create_display(640, 400); 10 al_clear_to_color(al_map_rgb_f(0, 0, 0)); 11 12 ALLEGRO_BITMAP *allegro = al_load_bitmap("allegro.pcx"); 13 ALLEGRO_BITMAP *trap = al_load_bitmap("trap.png"); 14 ALLEGRO_BITMAP *buf = al_create_bitmap(640, 400); 15 ALLEGRO_BITMAP *tmp = al_create_bitmap(320, 200); 16 17 al_set_target_bitmap(buf); 18 al_clear_to_color(al_map_rgba_f(0, 0, 0, 0)); 19 al_draw_tinted_bitmap(allegro, al_map_rgba_f(0.2, 0.2, 0.2, 1.0), 0, 0, 0); 20 al_draw_tinted_bitmap(allegro, al_map_rgba_f(0.2, 0.2, 0.2, 1.0), 320, 0, 0); 21 al_draw_tinted_bitmap(allegro, al_map_rgba_f(0.2, 0.2, 0.2, 1.0), 0, 200, 0); 22 al_draw_tinted_bitmap(allegro, al_map_rgba_f(0.2, 0.2, 0.2, 1.0), 320, 200, 0); 23 24 al_set_target_bitmap(tmp); 25 al_clear_to_color(al_map_rgba_f(0, 0, 0, 0)); 26 al_draw_bitmap_region(buf, 160, 100, 320, 200, 0, 0, 0); 27 al_draw_bitmap(trap, 0, 0, 0); 28 29 al_set_target_bitmap(buf); 30 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ONE); 31 al_draw_bitmap(tmp, 160, 100, 0); 32 al_draw_bitmap(tmp, 160, 100, 0); 33 34 al_set_target_backbuffer(display); 35 al_draw_bitmap(buf, 0, 0, 0); 36 al_flip_display(); 37 38 al_rest(10); 39 40 return 0; 41}

EDIT: I'm going to explain what the code does so I can use it as a blog post or wiki article.

The key thing happening here is you have a completely rendered scene, except for the light(s), and you can quickly draw parts of that scene to a different destination bitmap. You need an image defining the shape and cutoff of the light as well.

In this example, our "scene" is just the allegro.pcx image drawn 4 times to fit a 640x400 window, at 20% brightness. That's what the first lines of code after loading bitmaps does.

After that's done, the area that will be lit is drawn to a "temporary"/work bitmap. Then the light-shape-definition bitmap is drawn over top of that, which gives us a bitmap with the colors we want (color directly from the scene) and the shape (due to drawing the trapezoid over top).

The last thing that needs to be done is to set the target back to your scene and draw the "temporary" image we made back over top of the scene at the same point we copied from earlier. We use additive blending to draw it, and draw it as many times as needed to give the desired brightness.

Mark Oates

I'm confused.

1) draw scene
2) draw shadow layer with subtractive blender
3) problem???

Trent Gamblin

1) subtractive blending is not supported on all chips
2) you still have to do basically the same thing anyway. What am I missing?

Mark Oates

The multiply blender would work, but it doesn't appear that that's available until 5.1. Until then, I would then do it as thus:

al_draw_bitmap(af_get_image("scene.png"), 0, 0, NULL); // < draw scene

af_push_state(ALLEGRO_STATE_BLENDER); // < obvious what this does, I guess
al_set_blender(ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ONE, ALLEGRO_ONE);  // < makeshift subtractive blender
al_draw_bitmap(shadow_layer, 0, 0, NULL); // < draw scene
af_pop_state();

The work-around being that you'll have to draw your shadow layer inverted. Black is white and white is black - because the blender is subtracting more when the value is white.

scene:
{"name":"604625","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/8\/08948338172946a48c2856efb03cfa55.png","w":962,"h":723,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/8\/08948338172946a48c2856efb03cfa55"}604625
shadows (drawn inverted):
{"name":"604628","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/0\/90812f9929357cb5da1cddf831d87512.png","w":962,"h":723,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/0\/90812f9929357cb5da1cddf831d87512"}604628
composite (drawn with ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ONE, ALLEGRO_ONE):
{"name":"604629","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/5\/d5a6ac624bfa87c0c4c1043dc8dec8ec.png","w":962,"h":723,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/5\/d5a6ac624bfa87c0c4c1043dc8dec8ec"}604629
There's some really sexy color curves on the low-level shadows. :)

[edit] er... is ALLEGRO_DEST_MINUS_SRC the part that's not supported on all chips? Which chips are affected?

Elias

Well, in DirectX you have to query a flag and in OpenGL it's an extension you check for. So there at least were chips at one point which did not support it. I'd say all GPUs with shader support support it as it's now just a matter of shipping the right shader in the drivers for emulating the old fixed pipeline blending.

Trent Gamblin

Ok, now how do you move the light? You have to recreate the shadow layer each time it moves, and it will be constantly moving, so like I said, basically the same thing I posted but inverted, or am I still missing something?

Mark Oates

Ok, now how do you move the light?

You just draw the light in a different place.

Quote:

basically the same thing I posted but inverted

I guess mine has less code?... I dunno. Right now I'm brain-mush confus from trying to learn shaders. :P

Trent Gamblin

You can't just draw it in a different place. In your example is has to be drawn over the whole scene and thus fill the whole screen so moving it would make gaps in places. This is for a car. Imagine the car driving along then going up a hill. Now imagine there are 5 cars all with headlights on.

Myrdos

Trent: I've got your example working. \o/

I've got some code that draws a spotlight onto a bitmap, with a variable angle, beam length, brightness and aperture size. Do you think it would be worth submitting it to the Allegro dev team? It requires math.h.

Trent Gamblin

I don't personally think it fits in Allegro... seems too high level. You could post it on the wiki. I'm sure that would be useful for someone.

Mark Oates

You can't just draw it in a different place. In your example is has to be drawn over the whole scene and thus fill the whole screen so moving it would make gaps in places.

The shadow layer is a second buffer, just like the the display backbuffer. In the normal backbuffer you draw your scene, the sprites, trees, enemies and whatnot. As you draw their sprites (or in a separate process), you draw their shadows to that "shadowbuffer."

Here's your light triangle (thumbnail doesn't render correctly because it has alpha:
{"name":"604636","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/6\/e624155cc668c4b6c3d2e6674ed83fa7.png","w":950,"h":605,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/6\/e624155cc668c4b6c3d2e6674ed83fa7"}604636

Once all that's done you put the two together.

This is basically how I would do it, except I would try to see how many more features I could work in (except I would probably get frustrated and quit after I couldn't get them all in.)

  1. cast rays and draw long shadows

  2. desaturate and blue-tint the shadows.

  3. introduce surface hilights and shine onto some sprites

  4. blur increases from their light sources

  5. overall amplify the light blending that's closest to the light source.

  6. put in a little mini lens-flair

Most of these require shaders. The end might look something like this (photoshop):

{"name":"604637","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/5\/25aca9c4603800a22883474df0ba7228.png","w":958,"h":660,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/5\/25aca9c4603800a22883474df0ba7228"}604637

Myrdos

I just noticed something, you use the following tint when drawing the background: al_map_rgba_f(0.2, 0.2, 0.2, 1.0)

But I had thought pre-multiplied alpha worked like so: al_map_rgba_f(r * alpha, g * alpha, b * alpha, alpha)

Mark Oates: If you get that working with Allegro 5's graphics, you be sure to let me know. :o

Trent Gamblin

Yeah, it does work like that. Alpha is 100%, you don't want to make the image transparent, just darker (and that was just so you can more easily see the light.) The .2 makes is 20% brighness. So it's 0.2*1.0 = 0.2.

Myrdos

That makes sense. :)

Dario ff

I've used the method described by Mark multiple times. It works. 8-) Probably the easiest way you can get to "blend" the lights, just draw them as simple objects into the "shadow" buffer and then flip it with a blender.

auamidium

Not sure how to do quotes, but I'm trying Mark Oates' idea right now with this code:

#SelectExpand
1al_draw_bitmap(testPng,0,0,0); 2 3 al_set_target_bitmap(lightLayer); 4 al_clear_to_color(al_map_rgb_f(0,0,0)); 5 centered_draw_bitmap(lanternLight, 300,300,0); 6 7 al_set_target_backbuffer(display); 8 9 push_allegro_state(ALLEGRO_STATE_BLENDER); 10 al_set_blender(ALLEGRO_DEST_MINUS_SRC,ALLEGRO_ONE,ALLEGRO_ONE); 11 12 al_draw_bitmap(lightLayer,0,0,0); 13 14 pop_allegro_state();

Where I am trying to draw an 800x600 blank white space called lightLayer and draw lanternLight on top of it, which is just a black circle.

I'm getting this (see attachment), in the image after blending, which I don't understand, since the edges of my lanternLight png square should have been mixed into the white of lightLayer.

Also, a bit off topic, but I don't understand what states are used for. Is it that I can't blend anything without toggling some kind of blending-mode on/off?

And do I need to make another buffer like in Allegro 4 if I can just use al_set_target_backbuffer and al_flip_display?

Thanks :)

Edit: I just noticed the space outside the black square is brighter than the should-be-lit space inside the circle. That would imply what I'm doing is completely faulty. Is there some understanding I am obviously missing by my codes?

Mark Oates

You're clearing to black:

al_clear_to_color(al_map_rgb_f(0,0,0));

You should clear to white:

al_clear_to_color(al_map_rgb_f(1,1,1));

(or, just al_clear_to_color(al_color_name("white"));)

[edit:] also, if you use bitmaps for (black) lights, they should be on a clear alpha backdrop, that way you can add multiple lights together correctly without overlapping another light.
{"name":"604647","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/d\/2d4a104f19ecae662e9db43598aa53b5.png","w":1093,"h":557,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/d\/2d4a104f19ecae662e9db43598aa53b5"}604647
604648

Thread #607967. Printed from Allegro.cc