2D Lightning Continued

LightningTest-2011-01-16-19-04-05-351

A week ago i received a twitter message about the 2d lightning i posted. He asked me there if i could reproduce some normal mapping effect with correct lightning. I failed in doing so with the previous code i posted, so i picked it up and began coding once again om my lightning. The results where very pleasing for me and the person that asked for it :).

The results ended up like this:

The effect can off course better be viewed in motion, so i created a little movie for you as wel:

See it in HD on youtube

Source Code


Now it is time for some code, i will not discuss all the code that is required for this effect because at the bottom of the page you can also download the sample code that contains everything you saw in the movie.

First of all you will require a color map of your current scene and a normal map (in the sample we only use one texture for this)


Together with a light source these two images become the result you see below (this image has a low ambient light):

The color map and normal map texture will both be rendered to their own rendertarget. This will result into two different textures, one is your color image and one your normals. These two textures are provided to the shader effect as parameters to create a light map, such image you can see below. This is where all the magic happens :).


This lightning texture is then combined with the color map to produce the desired effect. I do it this way because i had some problems before rendering multiple lights, doing it this way i didn’t have any of those problems.

The variables or constants inside the shader effect file (MultiTarget.fx) can be tweaked to your desires. Especially the following line will adjust the specular highlight (or you can disable it this way):

Also the height of the light will have a huge impact on how the coloring will react. The default value of the height of the light will be around 100. Try to lower the number and see what it does :).

If you have any questions remaining, feel free to ask me :).

Update: I updated the sample application to work with xna 4.0. The lights are now working with a generic list; A specular strength variable is used; Shader is slightly modified.
LightningSample-XNA-4.0 (1567) (This requires XNA 4.0 installed)


33 Comments

  • Tony Ross says:

    Just found this after several wasted days trying to create a similar effect without success.

    Renders perfectly on the XBOX but not on my PC (I get a multicoloured mist over the top of everything)
    I think my GFX card is just too underpowered (lack of vram or features possibly)

    Fantastic work……well done!
    I’m off to PC World ;)

  • Chad S says:

    What is the purpose of calculating ‘halfVec’ in your shader?

    I see where you use it, I just can’t figure out how you came up with needing that value.

    Great work btw!

  • Fuzzle says:

    Just wanted to say, I found this and other portions of your blog very informative and useful. Thank you for taking the time to show this and give great examples of its use.

  • edvinn says:

    Hi there , I’m curious about the distortion effect you had in your game , is there by any chance you would create a tutorial for that ? :D

    Nice work for the light effect , nice , beautiful and well done.

  • Shane says:

    I’m just curious how multiple lights are acheived…I have tried adding more lights to the array but that doesn’t seem to make them appear at all.

    What am I doing wrong?

    Also wanted to know if you would be willing to explain more in depth with maybe an XNA 4.0 example? That would be awesome since 3.1 is rotating out soon.

    Thanks for the work you’ve done on this.

    • Vinnie says:

      Hey shane, i will maybe look into making it for xna 4.0 but i doubt it because i dont have that much time to even work on my own game… :(. It shouldn’t be to difficult to convert this to xna4.0, i have done it for my own game as wel.

      As for the more lights, maybe change this line
      private Light[] _lights = new Light[2]; >> List _lights…

      Be sure to enable to lights when adding the to the collection, as it checks if it’s enabled:
      if (!light.IsEnabled) continue;

  • Aaron says:

    I would love this for 4.0, but I can’t figure out whats going on in the 3.0 code(I’ve never been good at converting) Think you can post a demo of your one that you made for 4.0?(In the last reply, you said you did have one) Because this thing is just what I need!

  • Jeroen A says:

    Thanks for updating the code to 4.0 :)
    I was wondering how you create those normal maps. Do you just manually create these images?

  • Robin says:

    Bedankt voor het updaten!

    BTW ben je nog bezig met je game? Hoop het wel want ziet er heel goed uit!

  • Magikos says:

    Download link is still busted. :-(

  • Download link seems to be broken again :/

  • Vinnie says:

    Be sure to check the blog download section, there all the downloads are working. I shall update the links inside this post to point to the correct download link.

  • Vinnie says:

    Download link inside the blog post should now also be working :)

  • Robert says:

    Hi, I’m currently making a 2D game engine, and I’ve come very far and I’ve implemented your lighting engine, but im’ running into an issue where the shadow map and normal map won’t scroll with a camera code I have. I’m very interested in learning how to change your code to accept a ViewMatrix from a camera class. As of now I can get color Map to scroll just find, (as thats the one actually printed using SpriteBatch)

    Do you have any idea how to help? Feel free to send me an email, and we can chat about it.

    • Vinnie says:

      Hi Robert, it looks to me that you are not drawing your objects with the translation matrix taken into account from your camera?
      SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearWrap, DepthStencilState.Default, RasterizerState.CullNone, null, ActiveCamera.TransformMatrix);
      - Something like this. This line is what i use to draw my entire map onto the render target,

  • Robert says:

    See, my problem doesn’t exist there. I had that part of the code working. I do pass in my camera transform and for the most part that works, but the light map follows the player, whereas the actual colormap (the map itself) will scroll.

    http://www.flickr.com/photos/56407385@N04/6242467616/

    that is the shot before I move the camera, and the next photo is what happens when the camera moves to follow the player. Notice how the lightmap and particles stay with the camera rather than their original positions?

    I had corrected this by just making the Rendertargets as large as my map i’m loading in ( i made an editor and serilized a type to load in) but if the map is larger than 4096×4096 then it can’t/won’t render it in HiDef profile. (I also ran into an issue where i had to swtich to hiDef because it said texture sizes had to be powers of two for the reach profile, which i eventually want to go back to because I wanna do a game for WP7 using this engine, but my presentation parameters were the same as your demo above… so that was confusing).

    Below is some code I used to maybe help you understand what i’m doing…

    Please note that if I do not pass int he transform matrix on both sprite batches, or set the camera to Vector.Zero I get some weird resuls… such as a dual scrolling approach or even no scroll at all…

    protected override void Update(GameTime gameTime)
    {
    // Allows the game to exit
    if (Keyboard.GetState().IsKeyDown(Keys.Escape))
    this.Exit();

    testerPlayer.Update(gameTime);

    for (int x = 0; x < particleIndicies.Count; x++)
    {
    particleEffects[x].Trigger((testerLevel.getSpriteInformation(particleIndicies[x]).Position –
    testerLevel.getSpriteInformation(particleIndicies[x]).ParticlePositionalOffset));

    particleEffects[x].Update((float)gameTime.ElapsedGameTime.TotalSeconds);
    }

    testerWorld.Step((float)(gameTime.ElapsedGameTime.TotalMilliseconds * 0.001));
    this.Window.Title = "FPS: " + (1 / gameTime.ElapsedGameTime.TotalSeconds).ToString();
    testerPlayer.ElapsedTime += (float )gameTime.ElapsedGameTime.TotalSeconds;
    testerCamera.Move(testerPlayer.Position, false);
    testerCamera.LookAt(testerPlayer.Position);
    base.Update(gameTime);
    }

    ///
    /// This is called when the game should draw itself.
    ///
    /// Provides a snapshot of timing values.
    protected override void Draw(GameTime gameTime)
    {
    GraphicsDevice.Clear(Color.Black);

    DrawTheWorld(colorMap);
    DrawTheWorld(normalMap);

    spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, lights._lightCombinedEffect,testerCamera.GetViewMatrix(new Vector2(1.0f)));

    lights.GenerateShadowMap(shadowMap, normalMap, colorMap);

    GraphicsDevice.Clear(Color.Black);
    lights.drawCombinedMaps(spriteBatch, shadowMap, normalMap, colorMap);
    spriteBatch.Draw(colorMap, testerCamera.Position, Color.White);

    foreach (ParticleEffect effect in particleEffects)
    myRenderer.RenderEffect(effect);

    spriteBatch.End();
    base.Draw(gameTime);
    }

    private void DrawTheWorld(RenderTarget2D currentTarget)
    {
    GraphicsDevice.SetRenderTarget(currentTarget);
    GraphicsDevice.Clear(Color.Black);

    spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null,testerCamera.GetViewMatrix(new Vector2(1.0f)));

    foreach (LevelObject lvlObj in levelObjects)
    lvlObj.Draw(spriteBatch);

    testerPlayer.Draw(spriteBatch);

    spriteBatch.End();
    GraphicsDevice.SetRenderTarget(null);
    }

  • Robert says:

    I really appreciate your help. I’m relatively new to XNA programming but have been programming for years both professionally and for fun. :)

  • Vinnie says:

    Hehe it looks like your light objects are not moving with your camera and are just standing still (am i right?). I was updating my lights like this:

    // Update the position of the light based on the scaling factor
                ShaderLightPosition.X = (ActualPosition.X - CameraManager.ActiveCamera.CameraPosition.X) / GameRenderCore.LightMapRenderTargetScalingFactor;
                ShaderLightPosition.Y = (ActualPosition.Y - CameraManager.ActiveCamera.CameraPosition.Y) / GameRenderCore.LightMapRenderTargetScalingFactor;
                ShaderLightPosition.Z = ActualPosition.Z / GameRenderCore.LightMapRenderTargetScalingFactor;
                ShaderLightRadius = LightDecay / (float)GameRenderCore.LightMapRenderTargetScalingFactor;
    

    This shaderlightposition is then used to pass to the shader itself, we have to help the shader a bit as he doesn’t know a camera even exists :P. A handy method is also to check of your lights are actually in screen, and only pass them to the shader when they are actually inside the screen.

    • Robert says:

      Yes you are right, the lights just stand still. That’s what I thought too, that the shader was drawing to a flat position regardless of the camera. So where in your light code would you set it to adjust to the camera position?

      In that code example I see “GameRnderCore.LightMapRenderTargetScalingFactor” what is this referring too? What does that code do? Does it help the shader realize the position of the camera relative to it’s drawing position?

      Sorry I’m a bit confused.

  • Ollhax says:

    Heya! Love this tutorial, it really helped my get started with my lighting system.

    One question though, how on earth do you support as many lights as you seem to do (looking at the Wages of War trailer) on the Xbox? Even after adding some big optimizations (render up to 16 lights at once + have one shader for each number of lights to avoid dynamic looping) and still I can only render 15 or so lights before the framerate dips on the Xbox.

    I also experimented with real deferred rendering (i.e. with a G-buffer) but did not finish the implementation when I realized that it would work pretty bad with alpha blended sprites and a forward rendering system would be simpler.

    Thankful for any hints :)

  • Josh says:

    Hey, im having alot of trouble figuring out how to create a flashlight effect. I would REALLY appreciate it if you could please post an example of the shader code that would handle it?

Leave a Reply