views:

961

answers:

3

I am writing simple hex engine for action-rpg in XNA 3.1. I want to light ground near hero and torches just as they were lighted in Diablo II. I though the best way to do so was to calculate field-of-view, hide any tiles and their's content that player can't see and draw special "Light" texture on top of any light source: Texture that is black with white, blurred circle in it's center.

I wanted to multiply this texture with background (as in blending mode: multiply), but - unfortunately - I do not see option for doing that in SpriteBatch. Could someone point me in right direction?

Or perhaps there is other - better - way to achive lighting model as in Diablo II?

+1  A: 

As far as I know there is no way to do this without using your own custom shaders.

A custom shader for this would work like so:

  1. Render your scene to a texture
  2. Render your lights to another texture
  3. As a post process on a blank quad, sample the two textures and the result is Scene Texture * Light Texture.

This will output a lit scene, but it won't do any shadows. If you want shadows I'd suggest following this excellent sample from Catalin Zima

Martin
I always consider it quite rude to downvote an answer without commenting on why. Mine is essentially what aphid suggested, but without code samples.
Martin
+1  A: 

Perhaps using the same technique as in the BloomEffect component could be an idea.

Basically what the effect does is grabbing the rendered scene, calculates a bloom image from the brightest areas in the scene, the blurs and combines the two. The result is highlighting areas depending on color.

The same approach could be used here. It will be simpler since you won't have to calculate the bloom image based on the background, only based on the position of the character.

You could even reuse this further to provide highlighting for other light sources as well, such as torches, magic effects and whatnot.

Peter Lillevold
This is similar to what I suggested, but probably better because you can nab sample code from the bloom sample
Martin
+2  A: 

If you were to multiply your light texture with the scene, you will darken the area, not brighten it.

You could try rendering with additive blending; this won't quite look right, but is easy and may be acceptable. You will have to draw your light with a fairly low alpha for the light texture to not just over saturate that part of the image.

Another, more complicated, way of doing lighting is to draw all of your light textures (for all the lights in the scene) additively onto a second render target, and then multiply this texture with your scene. This should give much more realistic lighting, but has a larger performance overhead and is more complex.

Initialisation:

RenderTarget2D lightBuffer = new RenderTarget2D(graphicsDevice, screenWidth, screenHeight, 1, SurfaceFormat.Color);
Color ambientLight = new Color(0.3f, 0.3f, 0.3f, 1.0f);

Draw:

// set the render target and clear it to the ambient lighting
graphicsDevice.SetRenderTarget(0, lightBuffer);
graphicsDevice.Clear(ambientLight)

// additively draw all of the lights onto this texture. The lights can be coloured etc.
spriteBatch.Begin(SpriteBlendMode.Additive);
foreach (light in lights)
    spriteBatch.Draw(lightFadeOffTexture, light.Area, light.Color);
spriteBatch.End();

// change render target back to the back buffer, so we are back to drawing onto the screen
graphicsDevice.SetRenderTarget(0, null);

// draw the old, non-lit, scene
DrawScene();

// multiply the light buffer texture with the scene
spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
graphicsDevice.RenderState.SourceBlend = Blend.Zero;
graphicsDevice.RenderState.DestinationBlend = Blend.SourceColor;
spriteBatch.Draw(lightBuffer.GetTexture(), new Rectangle(0, 0, screenWidth, screenHeight), Color.White);
spriteBatch.End();
Aphid
Perfect answer, thank you!
PiotrK