Point Lights and Shadow Maps

Nov 14, 2009 at 2:19 AM

First of all I'd like to say kudos on the engine. I know you might hear it alot, but it never gets old.. this engine is really a great piece of work.

I've been messing with Xen trying to get the hang of how things are done. I created a new project to learn how to get started from the bottom up, and everything is going well until now. I managed to get the new project off the ground, implementing both JigLibX and Xen, and they are running great. I even managed to copy over the shadow map example from the tutorials to the new project, which hasn't caused me any grief until now.

I'm not familiar with how lighting is properly done, especially with the more complex aspects (also the reason why I decided to give Xen a try). I understand a bit how the directional static shadow maps are rendered but when it comes to point lights I'm at a loss. I've tried a very basic method using six (6) different DrawTargetTexture2D objects for a point light (one for each side), adding and then drawing each one individually. Soon after I realized this was a bad idea because my game was lagging due to the multiple "extra" draw calls. Would it be possible to get a simple snippet of how to properly add a point light with shadows, using Tutorial25 as the referenced starting point?

I'd also like to know if it's possible to create dynamic point lights, and if so, what's the best way to go about implementing one of these. Static lights are straightforward but the only way I see adding dynamic point lights would be to constantly add/remove the light upon position change, but this seems like the wrong way. I don't know how to access a point light I've already added to the MaterialLightCollection object to change it's position.



Finally, this is a bit off-topic but, I notice with the FirstPersonControlledCamera3D class that, while moving the mouse, the cursor doesn't stay within the window bounds. Is it possible to keep the mouse in the center of the window, and have the camera rotate properly, using the existing FirstPersonControlledCamera3D class or would I have to create a new class that inherits ICamera to do this correctly?

Nov 14, 2009 at 12:47 PM

I'll answer in reverse :-)

You can centre the mouse to the middle of the window, quite easily in fact - it's just not that easy to find. state.PlayerInput[..].InputMapper.CentreMouseToWindow. Set this to true on the player input that is set to keyboard/mouse control and it'll be taken care of for you.

When you add a light to a light collection, it will return an interface from the Add method. Either an IMaterialPointLight or an IMaterialDirectionalLight (both of which extend IMaterialLight). This allows you to modify the position / colour / attenuation, etc.

Now, point light shadow mapping.
This is, as you might guess, a rather tricky thing to do.
There aren't really any alternatives to rendering all 6 directions, however you would usually not do this as separates textures - you would use a single cube map. So a DrawTargetTextureCube - this means you would only be drawing a single pass to apply the lighting to the scene.
However - there is one little catch with this. And it's quite complicated.
It's all to do with 3D coordinate systems and 'handedness': http://msdn.microsoft.com/en-us/library/ee415205(VS.85).aspx

Basically, Direct3D is left-handed, but XNA is right-handed. It hides this under the hood. This works fine with one exception: Cube maps.
The result will be one axis on your cube map will be flipped. And it's actually surprisingly difficult to counteract it. The simplest way is to flip the x-axis on the camera matrix (so scale by -1) and then reverse the face culling (also through the camera). This is one of those things that will appear broken until you get it right - so don't bother with this until it becomes an obvious problem.

I'm not sure if that's what you were after - but what you are trying to do is a fairly complex thing. And realistically, it's something that needs a fairly complex rendering architecture behind it to get good performance. Xen isn't designed to handle very complex scene management logic for you - it's designed to be low level so that sort of thing can be easily added on top (as it's almost always specific to each application).

Nov 14, 2009 at 2:13 PM

Thanks for the quick reply! You're right, some things are dug deep in there and it makes it harder to find, but it's good to know that it's there. ^_^"

I was going to attempt to use a DrawTargetTextureCube, but I'm not exactly sure how to use it. I mean, I can initialize it and all that but I'm not sure how the shadow camera should be facing for it to render correctly, before constructing the variable. I understand directional lighting because there's only 1 direction the camera needs to be facing, it makes sense. But with point lights it goes in all directions so am I supposed to modify the camera for each direction or does it know what's behind the camera view? I'm a little confused with this.

I'm also not sure how to get/set the individual faces for the TextureCube, is it just one big texture? Once I figure that out I can try to work it in with a BlurFilter, since I don't see a way to pass it anything other than a Texture2D object. Not to mention modifying the ShadowedSceneDrawer class to draw TextureCube objects. I understand handedness, but didn't know that XNA differed from D3D (never got into D3D before XNA and it was easy enough until the lighting came into play).

I was looking for a way to somehow "combine" the shadow depths into one before rendering it with the scene. Instead of using multiple Texture2D or TextureCube objects for each new light, just use one (1) global Texture2D adding (or multiplying?) the pixels of each subsequent shadow map for each additional light source. I now realize this isn't possible since all the lights are facing different directions, so I have to figure out how to utilize TextureCubes and go from there. Thanks for the help. If you can provide anymore to clear up some things, I'd be most grateful.

Nov 14, 2009 at 5:03 PM
Edited Nov 14, 2009 at 5:04 PM

DrawTargetTextureCube automatically deals with setting up the faces. When you call draw it will draw all 6 faces with the correct FOV, etc.
You can't blur a cubemap - well you can, but it would be very, very difficult. So I'd suggest simply using a different shadowing technique. Exponential shadow maps have their uses, but are not always applicable.

Also unfortunate, but XNA does not allow access to surfaces, which would be the individual cube map faces.