Thursday, 4 October 2012

2D Crepuscular (God) Rays

If you follow my blog, you know I have already covered this as a 3D implementation, well while having a nose on twitter, I spotted that x35mm (another XNA developer from the midlands :D) was writing what looked like a pretty cool hack and slash Ronin game for DBP, and I thought, that would look nice with some God rays to give it some more lighting details, so sent x35mm my 2D implementation of the God ray post process, and this is what he did with it:



Pretty cool eh :D
So, I am going to post here this 2D implementation. Keep in mind that this effect will not work on the WP7 device as it uses custom shaders to do what it needs to do, might have a look at a CPU implementation, but I reckon it will suck.
Overview
If you refer back to my post on this for 3D you will see all the same shaders, the difference in this 2D sample is the way the light mask is built. Effectively you need to render your light source, over that render the elements in your scene that are obscuring that source over the top in black to create the light mask, then apply the Crepuscular post processing effect. Then render the scene as you would normaly and then using an additive blend apply the god ray’s over the top.
1. Draw Light Source

Here the post processor is rendering the light mask at the position we want on the screen, as you can see I have put up a few keyboard controls you can use to alter the parameters to the effect, so you can move the light source around, alter the exposure, stuff like that.
2. Draw Light Mask

There are a few items in this scene, a number of “mountain” images which are just wrap able textures and a samurai head icon for my mouse pointer, all png’s so we can use the alpha channel on them to allow light to bleed through. The post process is then applied to this to make the light bleed past the edges of the mask to give the god ray effect.
3. Draw Scene

The scene is then drawn ready to be blended with the processed mask.
4. Combine Scene and Ray Images

Finally it’s all brought together to give the overall effect.
5. Animate It
So I am scrolling the mountains at different speeds to give the sense of movement over the terrain and moving the mouse icon to obscure the light showing how dynamic this technique is. Also at the same time altering the parameters as I go.
As you can see it renders at a good frame rate, 60fps,  and this is at 900x1440 on my laptop. I have not personally ran this sample on my 360, buy x35mm has I believe.
Check out Dawn of the Ronin (pre god rays) here.
The solution for this sample can be found here.
I have also made an update to this so that the effect takes screen resolution into account here.
If you use this technique in your work, please let me know, be cool to see it used in the real world, just like x35mm has done in his game.

42 comments:

  1. Heyhodidllydoo Charles!
    I've not tried my hand at game development for a while but seem to be working my way back into it now that I know an artist who wants to make games! Awesome stuff! I remember reading your blog years back about game states and thought I'd look you up again to find out everything's changed! And you're doing DirectX! You crazy crazy man!

    Anywho, I was just wondering whether you will be uploading the source to this technique? My skills at writing shaders are shaky at best and could do with a bit of hand holding for this particular one =)

    ReplyDelete
  2. Hey Sparkey,

    Yes Ill get the source code up here soon, now someone has asked for it, ill try and get it up in the next 48 hours :)

    I have always done DirectX, XNA is DirectX :P

    ReplyDelete
  3. Replies
    1. There is that, but zips are so much simpler for folk to just dld..

      I use Assembla for my own projects, guess I should get a GitHub account for stuff like this really..

      Delete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. XNA is the nice fluffy version of DirectX =p

    Many thanks Charles! Once things start coming together I'll see about posting some screenshots somewhere =)

    ReplyDelete
  6. That does indeed look quite delicious. :-)

    ReplyDelete
  7. Hey, could you please resubmit the code. It seems the URL for download do not work anymore :( Pleaaaaase...

    ReplyDelete
    Replies
    1. The first link should be fine, I know there is an issue with the second one :S Will sort it when I can :D

      Delete
    2. Turns out my server is down, getting the host to sort it now :S sorry

      Delete
    3. the first link for source download keeps telling me "Service Unavailable" :(

      Delete
  8. will wait, not a problem :). This stuff deserves waiting :).

    ReplyDelete
    Replies
    1. lol, thanks, hope you like the sample, the server is back up now so you should be able to get it :D

      Delete
    2. Thanks, downloaded both versions. Will try it on Windows version of Undead Carnage.

      Delete
    3. Sweet, wang me some screen shots when you can :D

      Delete
  9. Thanks for this, it's a great release and very, very helpful (I'll send you a link once I've integrated it into a 2d sky system I'm developing). The zip for the screen res fix is still corrupt, any chance you could upload it?

    Thanks again for this, really great work :)

    ReplyDelete
    Replies
    1. Sorry still not got round to fixing that, will try and do it soon, will post here to let you know when its up.

      Glad you like the sample :D

      Delete
  10. Hey all, uploading the resolution project again, can you guys give it a go and let me know if you still have issues with it?

    ReplyDelete
  11. I've actually implemented my own version of the resolution fix, but the download works great now, thank you :)

    ReplyDelete
    Replies
    1. Yay! Did you do it the same way I did or do you have a better way?

      Delete
  12. I wouldn't say necessarily a better way, but I made sunSize a vector and then stretched the X based on screen value.
    I'd upload it but I've made quite a few changes to integrate the code into my own engine, such as reducing to two RenderTargets that get repeatedly flipped for PostProcessing and, instead of having the shader use black and white, I've switched it to using alpha. This means I can render the scene once to a transparent rendertarget then use it as the mask *and* the scene, saving on render calls.
    Honestly a great bit of code to work with, I appreciate the time you put in.

    ReplyDelete
    Replies
    1. I like the render to a transparent buffer, good idea.

      Seriously, I am glad you like the sample, it's so nice to get feedback on my posts, it seems to happen rarely now lol But it's great to hear people using the code and finding it helpful :D

      I really need to find more time and get posting some more stuff..

      Delete
  13. Excellent effect!
    What's the best way to have it render on top of a background (in both your 2D and 3D samples, it's just a black bg)?
    Because the spritebatch uses additive blending, anything in the background you draw before ppm.Scene will be noticable through the full colour layer you render last. Help!
    Thanks

    ReplyDelete
    Replies
    1. You are blending the scene with the effect, as in the samples.

      Delete
    2. I don't follow. Can't seem to get a background (skycube) working properly in the 3D version either. It always seems to end up distorting the background in problematic ways. How do you do it so that only the flare image is radial blurred?

      Delete
  14. I'm a big fan of this one, but playing around in widescreen resolutions makes the sun stretch out. Is there a way to fix that? I would assume it's in the ScreenQuad class, but I can't seem to figure out a way to make it work.

    ReplyDelete
    Replies
    1. Yes, i did a fix for that a while ago, think it should be in the latest download, get it again and have a look, if its not there let me know

      Delete
    2. I just grabbed the latest one, and it looks like it's still making an egg shape out of the sun in widescreen.

      Delete
    3. It will if you don't set the resolution parameter, if there is no resolution parameter, let me know and ill get the latest one up here... saying that, it might be in my 3D version of this and not the 2D one.... let me know either way :D

      Delete
  15. Hi,
    I'm interested to try this, though i'm a newbie.
    Could you please provide more details about the XNA, which version you used in .NET Framework and visual studio?

    Thanks :)

    ReplyDelete
    Replies
    1. It's XNA 4 for the PC so .NET 4.5 VS2010

      Delete
    2. This is awesome! :D I'll try to port it to MonoGame :)

      Delete
    3. Cool, glad you like it, I have already ported this as well as the 3D version to Unity :)

      Delete
    4. Currently, i've stumbled by the issue that i can't make an image background for this shader. Resulting shader RT is always solid colored blue :( And, honestly, i don't get it which operation is causing this. Can you please point me where to look? Of cause i can use additive blending, but background is too bright with this.

      Delete
    5. Finally. I think i've managed to do backround image. It seems like i've messed up some blend modes and draw orders while trying to include this shader in my engine. Thanks again! It looks good on MonoGame :)

      Delete
    6. I actually tried to port this over to monogame. It's good to know it's doable I am having problems with the shaders but I will keep trying thanks for doing this.

      Delete
  16. This look really good!!!
    I've tried to implement it in my game but I'm having some issue, probably related to blend state or render state.

    Since I already play with the color of my sprites I would like to avoid drawing theme once in Color.Black and then again in Color.White.

    To do so I created another RenderTarget2D that I called blackScene, I call Clear(Color.transparent) on it, and I've added an intermediate draw() call: drawing only 'scene' to 'blackScene' and tinting the whole thing in Color.Black.
    However, this tend to mess-up everything that isn't fully opaque or fully transparent and the whole picture look 'darkened' (Actually everything that should be semi-transparent is blackened).

    If I only render 'blackScene' I can see that all my sprites borders are white... and I don't understand why :)

    Best regards,
    Nicolas

    ReplyDelete
  17. I would really like to know how this works, I am a beginning programmer working in SDL, but if I knew how the shaders worked I think I could pull it off! Please let me know

    ReplyDelete
    Replies
    1. Download the source and have a look, any questions let me know, I covered this in an earlier post, it was for 3D but the principle is the same.

      Delete