Light and shadow system

BlitzMax Forums/BlitzMax Programming/Light and shadow system

UnderwoodNullium(Posted 2011) [#1]
I'm working on a light and shadow system. Each shadow is a polygon made with the angles between objects' edges and the lights. The lights are LIGHTBLENDed together and form a cool effect. There's also a line of sight part which blacks out 'walls' also, using the same way as the shadows. Also I wanted to have images that are only visible within the lights (I'm using SHADEBLEND right now).

Everything's fine except for a few things.

1) The images should be blended with the lights so that any image outside the lights is blackened by darkness. I did this with SHADEBLEND, but I need to have lots of images with transparency drawn before this. How it is now, images are mashed together no matter the alpha.

2) I want the shadows to 'disappear' as that part of the polygon is further from the light. I think I need to use a gradient circle (like my light image used) and blend it to the shadow somehow. I did something close once.

I really am a noob with the blend modes, most of the work was trial and error.

That's about all I can think of. The code's below, with the images. Any help would be amazing!

nullium.fileave.com/Random/Shadow.zip

Last edited 2011
I'm not worried about 2) anymore above.



Last edited 2011


ima747(Posted 2011) [#2]
Very cool, hope to see more!


UnderwoodNullium(Posted 2011) [#3]
Thanks for the post ima, it's gonna be for a top-down zombie game (like nazi zombies but more to do, like moving furniture to block doorways, etc) soon. I was going to possibly make a library for people to use after I get these few issues I'm having fixed.

Any ideas guys?


col(Posted 2011) [#4]
Hiya.
Very good start:)

One method to fix your shadow issues is to construct the shadow volumes to confine only in the area that is lit. You are extending the shadows infinitely, then when another light crosses this extended shadow volume its getting lit which is incorrect.

EDIT:- To do this, give each light a 'distance' variable and use it to construct a shadow volume for the polys.

Looks good, very promising.

Last edited 2011


UnderwoodNullium(Posted 2011) [#5]
Thanks col! I'm trying desperately to do exactly that, but I want a kind of gradual dissipation of the shadow from the light, like a gradient for the shadow. I'm trying to do it with the different blends (and blend the shadow with it's light with a larger distance)... It's all driving me mad. ;)


col(Posted 2011) [#6]
You're thinking along the correct lines.

Try this...
The light image is 150x150, use 150 as the radius, or if you scale it then scale the radius too, then...
In the DrawShadowsAndLights(...) function, instead of multiplying the shadow poly verts by LARGENUMBER, use the radius. This should build the shadow volume within the constraints of the of the light image. Remeber, you only want to create a shadow volume within the lit area, not in the dark areas.

Then blend, using LIGHBLEND to light or SHADEBLEND to darken.

EDIT:- Sorry slight bullshit above :D
You need to make the shadow the radius from the centre of the light source to its radius. So when you build the shadow volume and you generate the verts, you need to know the distance from the corner to the light source centre, then subtract that distance from the radius to give you the remaining distance from the corner to the lights radius. Use that second distance instead of the LARGENUMBER.

Last edited 2011


UnderwoodNullium(Posted 2011) [#7]
Thanks again Dave. Basically I'm not as worried about the shadows so much as I'm worried about the images being draw correctly 'under the lights', if I could get that working correctly I'd be a happy camper! Haha.

Basically exactly how it looks now, except I don't think it's correct. All I really want is the whole level, complete with some transparent images and some nontransparent ones, being illuminated only if any light source is overlapping it. I can achieve that effect kinda with what I have now, but the images I had to SHADEBLEND after all the lights were LIGHTBLENDed. This leads to a weird effect and the images don't have transparency.

I know I'm not making any sense, haha. Basically I'm trying to get it to work where the images that make up the whole level (including transparent ones, are 'shown' through the lights, like this.



That's the main light function. It works kinda, but not the way I want. At first, the lights are LIGHTBLENDed together, the I draw the images that are shown through the lights using SHADEBLEND, the the line of sight part. Unfortunately SHADEBLEND isn't what I'm looking for I think... Any ideas? I really do appreciate it.

Last edited 2011


col(Posted 2011) [#8]
Unless I'm mistaken.... I think what you are after there will need some kind of multipass which BMax can't do natively, Or maybe some kind of trick with overlaying a quad to darken?? There are lots of other ways to do in a single pass :) but it would mean using the gl driver and the raw gl.. commands which BMax can do easily :) Of course you need to learn OpenGL to do that :P


UnderwoodNullium(Posted 2011) [#9]
I was afraid of that. ;) Thanks again though, I'll pour over some gl stuff soon and hopefully get this project jumping. Thanks Dave, I appreciate it.


UnderwoodNullium(Posted 2011) [#10]
Man, I've been pouring over tons of stuff in the forums and can't really find anything. I found one gl example that used a mask and it let me see underneath the lights (and what was under was perfect), but the mask didn't support smooth transparent images for lights. Just one color... Hmm. I must be missing something.


Kryzon(Posted 2011) [#11]
Hi Underwood, I didn't quite understand what you're trying to achieve.

I mean, the environment is really only showing through the parts that are lit, consequence of you drawing things with ALPHA or LIGHTBLEND, then later drawing what you want to be occluded with SHADEBLEND.

Check this post: 2D Flashlight Effect.

Is there any way you can mockup in your fav. image editing program how you want it to be displayed?


UnderwoodNullium(Posted 2011) [#12]
Thanks for the link Kryzon, and that's pretty much how far I've gotten it so far, but I'm still having a problem with the SHADEBLEND part... It's mixing my two 'shown through the lights' images (the floor and a crate), which I think col said Blitz won't allow (since I want them some images to have transparency, others not).
I noticed in your example you created an image of all the rects that are on screen before any blending (I'm guessing to blend everything in one go), I also tried grabbing the whole screen to process that one image (after everything was alphablended the way I neeed it) but it was too slow (but worked).
Here's the crate and floor how it is currently, I can't figure out how to not make the crate blend with the floor (maybe it's doing this because the floor and crate are SHADEBLENDed in one pass together?) but still have the same lighting effect. Thanks for helping, you have no idea how appreciative I am! :)
	UpdateLights()

	SetAll(shadeBLEND,1,255,255,255,0,1,1)
	TileImage(floorimage,0,0)

	SetAll(shadeBLEND,1,155,155,155,0,1,1)
	DrawImage(stoveimage,600,600)

The SetAll function just does this: SetAll(blendmode,alpha,r,g,b,rotation,scalex,scaley)



Last edited 2011


Kryzon(Posted 2011) [#13]
Hmm, I understand. It's complicated indeed; you'll need to use a framebuffer - this is equal to making another "backbuffer" to which you can draw stuff into.
With an extra framebuffer you'll be able to achieve this level of composition.

It shouldn't be too hard. The hardest part should be finding the right sequence of steps and interfacing with Max2D's images.
Most of the hassle of implementing FBO's has already been covered in this thread (most posts have some useful information or instruction on using the shared code).
Low-level use of FBO's can be read Here.

With an extra framebuffer you'll have two buffers to draw to: the default backbuffer and this extra framebuffer you created.
Every frame you do this (in the order presented):
•Bind the extra framebuffer {

 -> CLS

 -> Draw your environment and characters with all the blendings you want

}


•Unbind the extra framebuffer { 

 -> This gets you back to the backbuffer

}


•Draw normally as you would {

 -> CLS

 -> Draw all the light-shapes with any blending you want (preferably ALPHA) to
 set up the visibility.

 -> SetBlend SHADEBLEND

 -> Draw a fullscreen rect. Use a custom DrawRect() function that binds the
 framebuffer's color attachment texture to use as its own. Since this texture holds 
 the game environment and characters, they'll be blended against the light-shapes.

}
This would work just like that "2D Flashlight Effect", but now you can have animated backgrounds with moving characters etc.
The original effect didn't need a framebuffer because the screen was static, so it could just be grabbed once into a pixmap and drawn forever.

Since grabbing things is slow for real-time purposes, we use a framebuffer. If the framebuffer technique doesn't work, you can resort to GrabPixmap() or GrabImage() anyway.

Last edited 2011


UnderwoodNullium(Posted 2011) [#14]
Thanks for the links and pseudo code. I'll do as much research on FBOs, it looks like what I need. I'll write in a day or two how it went. Thanks again!


UnderwoodNullium(Posted 2011) [#15]
I researched as much as I could, and I'm still pretty lost when it comes to FBOs. I understand the principle, but I've unfortunately haven't expanded into OpenGL or even using other's modules much. I always want to code stuff on my own but man, it seems beyond my reach a bit. I apologize. I would appreciate any more help anyone could give. After it starts to work I'll clean my messy bad code a bit and maybe put it in the archives for others to use.


UnderwoodNullium(Posted 2011) [#16]
I've been trying tons of alternatives, but I know this can be done. Any ideas?


VomitOnLino(Posted 2011) [#17]
Why don't you use Render2Texture, I think TonyG fixed Indiepath's module and made it work with DirectX 7/9.

I myself took a good long look at Indiepath's code and wrote (for port-abilities sake) a closely similar OpenGL implementation. It works even on the GMA 900 and it is dead simple to use.

Basically: Init RTT, Draw into RTT buffer, Cls, Draw other stuff, Draw RTT texture on top; or do whatever else with it. Repeat.

You should be able to find it by searching these forums for "Render to Texture" or some variation thereof.

If you're still stuck then I guess I could slice out that part of my game's source, I guess that's only fair as I wouldn't have been able to do it without Indiepath's module for insight. The advantage of "my" implementation is that it works nicely with Oddballs ODD2 module and Blitz' own VirtualResolution.

Last edited 2011


UnderwoodNullium(Posted 2011) [#18]
Thanks so much for the comment Vomit. I have been doing SO much research lately on OpenGL stuff and etc... there's so much info out there. I'm so used to just using blitzmax commands and not really expanding out much. I got it working correctly using pixmaps, but it's still pretty slow (10-15 fps). It's almost use-able! I'll continue working on it and report back.


VomitOnLino(Posted 2011) [#19]
Well if you are dead set on using only Blitz native stuff then I guess pixmaps are as good as it's going to get.

Have you considered using small/tiny pixmaps. As the lightning and the shadows are blurry (I'd even go so far and say that's desirable) you can probably get away with rendering to a 128x128 or 256x256 pixmap.

Even so I still think pixmaps will only get you so far, so I'd still really recommend checking out this thread (Dug it up for you):
http://www.blitzbasic.com/Community/posts.php?topic=73223#867195


Taron(Posted 2011) [#20]
The issue I found with openGL is that vertical blank flipping doesn't take on many windows laptops for some odd reason. I otherwise love it and it's fairly easy to do the wildest things with it.

Once you wrap your mind around setting up a framebuffer, which can be super easy, you can go nuts with effects and it's faster than the render to texture approach, if I'm not mistaken. I did a bunch of fun tests with it a while back...
http://www.blitzmax.com/Community/posts.php?topic=88944#1010487

Anyhow, the flip 1 ignoring with openGL framework on laptops still gives me grief.


*(Posted 2011) [#21]
I would love to see code that would allow multiple layers of images and lighting, I have a game in the offing that has torches everywhere that require lighting up the surrounding area but atm the way I work it is do luminance checks on the areas around the torches and if the square isnt in there i lower it to ambient level and if it is I set it to max. When I render it I paste a sprite over the torch this works ok but I cant have torches together as the images overlap.

I would like to see some simple code that would allow torches to be together :)

I will look at FBO's when I have time but atm its rather limited.


Noobody(Posted 2011) [#22]
Since the images you posted seem to have 404'd, I'm not exactly sure what look you were going for, but I tried to come up with something based on your description:



It uses an FBO to allow correct blending of multiple lights, meaning that basically the intensities of lights add up and that shadows cast by one light source really only occlude that light source and none of the others. Since we're already using an FBO, I added soft shadows as a small gimmick. It's basically a GLSL gaussian blur, the radius of which depends on the distance to the next light source.

It requires two full-screen RGBA textures, which is a reasonable cost, and should run fast enough. It should also integrate well with Max2D (although I didn't thoroughly test that), and there are no requirements over the blendmodes you have to use to render your normal scene.

Here's the source code with the additional required files: Link

For good measure, I also added a version without soft shadows ("2D Hard Shadows.bmx") if you don't want that and a simpler version that just renders lights without shadows, which will only require one full-screen texture ("2D Lighting.bmx").


There are compatiblity issues with FBOs (or there used to be, at least), so you might want to implement a version without them to have something to fall back on if everything else fails. If the code is to your liking and you're interested, I'll see whether I can write a DX9 version for better compatibility (no guarantees for DX7 though - never worked with it and never will).


Taron(Posted 2011) [#23]
Uh, I'm shocked that all the texture calls in the GLSL sections won't bring it to a grinding hold. It's still quite quick! Curious. Very nice, of course! Thanks for sharing. :)


Noobody(Posted 2011) [#24]
Well, 14 texture lookups per pixel aren't really that costly. If you consider a typical 3D game scene, there's enough overdraw to exceed that number quite easily :)

Or, in the words of a friend of mine, "The GPU is a huge interpolation machine" - it is made for these kind of things.


Taron(Posted 2011) [#25]
I might've done something funky back in the day, because I had a pretty big slowdown after a bunch of calls. Not sure what went wrong then. Anyway, it's comforting to see and hear! Thanks! :}