2D Global Illumination

Community Forums/Showcase/2D Global Illumination

Noobody(Posted 2011) [#1]
Since I needed a lighting solution for a 2D sidescrolling project, I experimented a bit with global illumination in 2D to bake lightmaps for later use in-game.

The principle it uses, light tracing, is very simple: Shoot rays from each light source and scatter them at intersection points until the rays terminate. Getting it to work non-biased though was a whole different question.
Ultimately I had to come up with a 2.5D-ish kind of concept to make it look good, which made generating light directions a bit more messy (I you take a look at the code you'll see what I mean), but it seems to work quite nicely.

Another issue were efficient intersection tests. Since I didn't want to put any restriction on the kind of environment used (e.g. it doesn't have to consist of straight lines), I implemented a sparse voxel quadtree to ensure fast intersection detection. Interestingly, even though the code is a lot more complex, the test scene renders a lot faster using the SVQ compared to naive line-line intersections.


Screenshot:




Youtube video:



Watch in HD!



The end result is, albeit slow, not too bad and may find its use in games using a topdown view or mostly indoors. For my purpose it is sadly not applicable, since I was going more for a sideview perspective, so I'll have to take a different approach for this.

Download the demo (*.exe + BMax code): Link

Last edited 2011


Kryzon(Posted 2011) [#2]
Totally kick ass man, thanks for sharing.


MCP(Posted 2011) [#3]
Very clever stuff Noobody!


Taron(Posted 2011) [#4]
Very nifty! Thanks for sharing, really. Inspiring, too. I'm really curious about your game then. Looking forward to it! :)


BlitzSupport(Posted 2011) [#5]
Yeah, good stuff, many thanks.


col(Posted 2011) [#6]
Very good. Many thanks for sharing.


Ross C(Posted 2011) [#7]
That's super sweet!


Noobody(Posted 2011) [#8]
Just a small test to see how it could look ingame (due to lack of actual levels, just a bit of concept art from one of the artists):



Light enters through the door, the window and cracks in the ceiling.


Kryzon(Posted 2011) [#9]
Are the light sources painted in a mask-like bitmap, or are they edited another way?


Nate the Great(Posted 2011) [#10]
hey, looks awesome in the video but im not sure whats wrong with it when i run the exe or the code it just draws sparse pixels randomly and doesnt really do anything, is it not for real time or is something wrong with my graphics card/computer?

Looks very cool by the way!

also if it is not real time, is there a real time lighting mod out for blitz max?

Last edited 2011


Andres(Posted 2011) [#11]
i don't get why it traces the rays randomly? or did i miss something?


Space_guy(Posted 2011) [#12]
Looks nice :) You should keep the pencil drawn style :)


Taron(Posted 2011) [#13]
You know, I'm sure, too, that there are alternatives that could yield a brilliant result without stochastic sampling or what that's called. Certainly a cool challenge to think those up! :)

I still like what you've done there, though. And your game design idea up there looks very, very interesting indeed!


Noobody(Posted 2011) [#14]
Are the light sources painted in a mask-like bitmap, or are they edited another way?

The light sources are being painted into a separate lightmap, which is then being overlayed over the tilemap.


is it not for real time or is something wrong with my graphics card/computer?

Don't worry, your computer is fine ;) Global illumination is just very very expensive computational wise. While it is a bit faster in 2D than in 3D (where rendering a single frame can take days), it's still not ready for real-time usage (leaving GPGPU aside).

Also, to keep the demo application from freezing when it's calculating (which gives a bad impression), I lowered the number of rays cast per frame to a reasonable number. Unfortunately, this way the rendering of the image actually takes a lot longer than the lighting calculation itself, so the lighting takes a lot more time to converge. If you increase the number of rays cast per frame though (to, say, one million), you should get a noise-free image after 30-60 seconds.


i don't get why it traces the rays randomly? or did i miss something?

The reason for this is that calculating the lighting directly is impossible. What the program is doing is essentially evaluating the rendering equation. Solving that integral directly is only possible for very simple cases, but for the more general case other means of integration are needed. In visual computing, Monte Carlo integration is a frequently used algorithm because it is well understood, efficient to implement and gives good results (which is why I've been using it here).

How it works is basically choosing random sample points inside the domain of the integral, evaluating the function at these points and summing the corresponding values up to approximate the total value of the integral. The tricky part here is to calculate the probability of a sample to weight it properly, which is usually very counter-intuitive and even subtle errors can have huge effects on the final image.


Certainly a cool challenge to think those up! :)

If someone's actually able to do that, he'd probably suffocate under the mountains of money the movie industry would throw at him :D


Taron(Posted 2011) [#15]
Uh, is that a promiss? :D

Nah, I think as long as you're in a 2d domain, even if you go 2.5d by using depth information, you can accept to use assumptions that don't require sampling and can rely on calculation instead. You have a whole dimension of unknowns less than in fully 3d situations.

I've written the "ZbornToy", which is a 2.5d filter for After Effects, which does a good deal of that, except that I only hooked in a very simplistic ambient occlusion and didn't go into radiosity. I've written all important basic shading features for it, including subsurfacescattering or rather "volumetric translucency" with some adjustable diffusion. Lightsources can be direct light and point light with soft shadows, if desired, but also environmental illumination based on various mapping methods. I've hooked up refraction in and forward, meaning it refracts background and can project refracted lightsources onto the background. So, yeah, I've been in the general realm already and it's almost realtime, while it could be optimized for that purpose, too.

Did that throw any money at me? Nah, not really. Not bad for a few weeks of development, but then... I wasn't really asking for it either.

However, I like this challenge and might have a look and go at it! :}


Kittomer(Posted 2011) [#16]
Even though I don't know very much about the domain of programing raytracing, I am sure you could combine a simple shadow casting engine with a GI solution that has very sparse sampling and then simply interpolates based on the shadow and "geometry" data.
If done cleverly, it probably would reach comparable image quality as this brute force approach.

This STILL is very impressive, wouldn't have thought BlitzMax would be able to handle something like this respectably.


Noobody(Posted 2011) [#17]
Nah, I think as long as you're in a 2d domain, even if you go 2.5d by using depth information, you can accept to use assumptions that don't require sampling and can rely on calculation instead.

That depends on what you mean by 'calculation'. Evaluating the integral directly (e.g. finding a perfect solution that runs in bounded time) is not possible for the general case, but there are different methods of integration other than stochastic sampling. Monte Carlo integration is just one method (one that finds a lot of uses, though), but there are also iterative algorithms available, such as radiosity (which you mentioned). All of them though only yield approximative solutions that get better the longer the program is running (unbounded time).

I would prefer stochastic sampling over radiosity in the 2D case though, since radiosity essentially only calculates light exiting surfaces of geometry. While this works well in 3D (since all visible geometry is essentially a surface), in 2D it would only be able to calculate the lighting conditions on surfaces of obstacles, not on the 'floor' (which comprises the main part of the image) since it's not an actual 2D surface.
Also, while radiosity works well for low-variance diffuse lighting, it performs poorly on sharp shadows, which I'd like to preserve in the final image (there are variants of radiosity solving this by calculating shadows separately, though).

I had a look at ZbornToy and I must say, I'm very impressed. It's pretty!
Also, while Ambient Occlusion is usually done with stochastic sampling adjacent geometry, it *can* be approximated with techniques such as SSAO, which I'm guessing you used. It suffers from a few drawbacks though (with it being an approximation of an approximation and all).

I'm intrigued by the caustics though - did you trace a light ray to each pixel, refract it by the normal at that pixel and then light the point on the background where it ends up after refracting? That's what I'd do, and it probably yields enough samples to compute smooth caustics.

So, yeah, I've been in the general realm already and it's almost realtime, while it could be optimized for that purpose, too.

True, caustics, soft shadows and AO are some of the focus points of global illumination. Basically, if you'd apply AO again and again and again, you'd ultimately end up with GI (since that's basically what radiosity does). Also, nice work on the performance - my code really sucks in that regard. I didn't really care since it's just intended for precomputing lightmaps, where a few minutes of computation time won't hurt, but seeing it in real-time would make an interesting game mechanic.

However, I like this challenge and might have a look and go at it! :}

Please do! I'd be very excited if you found something.


This STILL is very impressive, wouldn't have thought BlitzMax would be able to handle something like this respectably.

Don't underestimate BMax! :) If you only consider raw computations, it is only slightly slower than C/++. The problem is that for one, most C/++ compilers do excessive optimization unmatched by the BMax compiler in more complex pieces of code (though the ASM compiler might help a bit with that), and for the other that BMax has a garbage collector tearing on the performance. While this isn't much noticable in sequential mode, once you enable multi-threaded build, it becomes painfully obvious. I tried to speed it up with multi-threading, but just enabling threaded build already has such an impact on the performance that I didn't really bother with that option.


Taron(Posted 2011) [#18]
I'm thinking about an interesting solution, but it's rather...eh...magical, hahaha. In the ZbornToy I've written a bunch of solutions in there, whereby the shadow casting light is the key element and sets the philosophy. It simply traverses the height image from the lights origin and responds to obstacles in respect to the angle by which the light comes in depth wise. Really simple stuff, but very fast and actually proper. The softness is a bit simplified beyond correctness, but not bad either as it responds to the distance of the last obstacle.
The caustics are the most magical trick, whereby Philipp Spoeth had the brilliant spark to use polygons and we wrote a very fast anti-aliased rasterization routine together. I won't disclose too much in that regard to respect his stakes, too, but you may be able to figure out what the idea is. However, the new after effects versions cause a few small troubles with them, which we had removed completely in the old version. That's a little annoying, but it's still nothing too serious and we'll fix it up, too.

Anyway, the magic idea I have for the calculations of bouncing lights has to do with fluid dynamics and light volumes. I'm not too sure, yet, but I think there's something very brilliant waiting to happen. Think about this for a moment. You might have some fun with it, too.

Last edited 2011


Nate the Great(Posted 2011) [#19]
Not hijacking the thread or anything, but heres a cool little real time lighting demo i came up with while trying to understand some different methods behind 2d lighting

I hesitate to call it real time because with more polygons/lights it becomes useless and slow
click two places to make a line segment



Last edited 2011


taumel(Posted 2011) [#20]
Hmm, i don't get it, it's too slow for realtime purposes and for the rest there do exist solutions which bake lightmaps already, is it for tweaking certain factors you otherwise can't or storing the result in a different way?

@Nate the Great
Cool!


Noobody(Posted 2011) [#21]
Hmm, i don't get it, it's too slow for realtime purposes and for the rest there do exist solutions which bake lightmaps already

There exist 2D lightmapping tools? Do you have a link to that? Because that's exactly what I need. The sole reason I wrote this is because I couldn't find something else that already does what I want.



@NateTheGreat: If you want to go into the realtime realm, a good choice is directly using polygons and letting the graphics card do the work. One way to do this is tracking which side of the object is facing away from the light and then appending a shadow volume to that side and extending it away from the light beyond the viewport. I implemented this a while back in B3D:



The problem here is that rendering of the lights themselves (and not just the shadows) is not as easy to do without an additional buffer. Since rendering to a buffer other than the backbuffer is really slow in B3D, this isn't really possible to do there, but you should be able to do it in BMax.

What you do is basically set the alpha buffer as the render target and then render all the lights by drawing a circle around the light with a color gradient going from opaque in the center to fully transparent on the outer edge. Then you render the shadow volumes like in the B3D code above. After that you set the render target back to normal, render your normal scene and you should have realtime lighting :)
There was a good article on this on Gamedev, but unfortunately, it seems to have 404'd. It was called "2D dynamic soft shadows", I think, so maybe you can find it elsewhere.

So yeah, if you're interested, go for it! I'd love to see this implemented in BMax.


taumel(Posted 2011) [#22]
Not a pure 2D solution i'm aware of but why don't you just build/approximate the 2D geometry and extrude it in the y-axis?

Last edited 2011


Noobody(Posted 2011) [#23]
I considered that, but writing a model exporter, a 2D-to-3D mesh builder (that possibly even has to assign UVs), finding a good 3D lightmapper that supports CLI etc. just seemed like a lot of effort compared to just writing a quick 2D lightmapper myself (I mean, in total this only took about 6-8 hours and was fun to make :) ).

While I'm all against duplicated effort and prefer to use existing tools instead of writing my own, in this case I think adapting an existing solution would have been quite a hassle.


taumel(Posted 2011) [#24]
Don't get me wrong, keep on posting such things. I find such tests very interesting and beside of that it's a lot of fun you learn a lot as well but my first idea was that i would try out a lightmapper as i expect you also have to prepare at least a Photoshoplayer in order to define a scene. Btw did you try out Beast in Unity already?


AvestheFox(Posted 2011) [#25]
This looks amazing!

...

wait, is that Applejack from FiM in the test sketch? o.0


Noobody(Posted 2011) [#26]
Yes, it is. Well spotted.


xcessive(Posted 2011) [#27]
This is beautiful!