Jump to content

  • Log In with Google      Sign In   
  • Create Account

Shadow mapping advice


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1 mv348   Members   -  Reputation: 253

Like
0Likes
Like

Posted 01 September 2012 - 12:54 AM

So I believe the next step in m engine is to add shadows. After doing some research, shadow mapping seems to be a popular and fast method.

The theory makes sense to me, but I find it slightly surprising that it uses a full scene render per light-source. I've noticed that modern games seem to use a pretty high number of light sources! Is this technique advisible for a large number of lights?

Moreover, the method works by rendering from the lamps point of view, so the light is limited to a frustum pointing in one specific direction. I presume you would have to do multiple renders for a light spreading out in all directions?

I read some hardware offers 'shadow mapping support'. What kind of support is available? I'm using OpenGL/GLSL

Sponsor:

#2 Hodgman   Moderators   -  Reputation: 31951

Like
1Likes
Like

Posted 01 September 2012 - 01:12 AM

I find it slightly surprising that it uses a full scene render per light-source. I've noticed that modern games seem to use a pretty high number of light sources

Yes, some new games do use as many as a thousand dynamic lights, however, only a small number of them will actually be shadow-casting lights.

Also, in specific scenes / specific scenarios, you don't need a "full scene" render per light.
e.g. I once worked on a racing game where all of the environment shadows used static light-map techniques, so only the dynamic cars themselves needed shadow mapping. We also used "deferred shadow maps" where you accumulate the shadow information into a full-screen texture. We would render each car individually into it's own shadow map (which makes good use of low resolution shadow textures), and then composited each single shadow into the full-screen texture. This meant we had to render (at most) an extra numCars * numLights for shadows -- not wholeScene * numLights.
You should also perform frustum culling (and full visibility culling if your scene supports it) on the light, so that you're only rendering objects that are visible from it's viewpoint. If no cars were visible from a particular light, then no extra rendering was performed for it.

Moreover, the method works by rendering from the lamps point of view, so the light is limited to a frustum pointing in one specific direction. I presume you would have to do multiple renders for a light spreading out in all directions?

Yes, the most obvious solution is 6 different 90º frustums rendered to a "cube-shadow-map". There's also hemisphere-based solutions that use fewer frustums (Dual-Paraboloid SM or Dual Sphere-Unfolding SM), or complex distortion methods like CSSM, but these alternatives generally exhibit more distortion than the basic solution.

Edited by Hodgman, 01 September 2012 - 01:20 AM.


#3 MJP   Moderators   -  Reputation: 11790

Like
1Likes
Like

Posted 01 September 2012 - 12:35 PM

The "hardware support" for shadow maps amounts to "free" depth comparison and 2x2 PCF filtering when sampling the shadow map. Usually it's implemented by rendering your shadow map depth to a depth buffer, then sampling that depth buffer with a special instruction and/or sampler state in the shader. Unfortunately I'm not familiar with how it's done in GLSL so I couldn't tell you offhand.

#4 Ignifex   Members   -  Reputation: 507

Like
1Likes
Like

Posted 01 September 2012 - 03:07 PM

For OpenGL / GLSL, you need to set up your depth texture to do a comparison upon lookup using for instance:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

Then, in your shader, you should give your shadowmap uniform the type sampler2DShadow. This will allow you to call the regular texture function, or one of its variants, with a 3 dimensional texture coordinate. The third coordinate is the depth value used for comparison.

#5 mv348   Members   -  Reputation: 253

Like
1Likes
Like

Posted 15 September 2012 - 11:12 PM

Thanks for all the input guys! I just got shadow-mapping workingwith a single light, for a single model. So now I am moving towards multiple lights, and multiple models.

To avoid excessive extra-rendering, I am planning on enclosing each lamp in a bounding volume (i.e. an Axis Aligned Bounding Box) and checking for intersections with each models bounding volume. I admit I haven't read up much on this just yet, there's something called 'Cascaded Shadows' that I need to look into, but I wanted to get your input on the algorithm I came up with today.

Here's the psuedo-code. Should be pretty self explanatory:


Algorithm:

PositionModels()
PositionsLights()
/////////////////
// SHADOW PASS //
/////////////////



for  light in AllLights
{
   // determine which models are hit by the light
   for model in AllModels:
   {
	 if (collision(light.volume, model.volume)
	 {
		 light.modelsInView.add(model);
		// give model access to light data, including its shadow map
		model.shadowCasters.add(light);
	  }
   }  

  // if any models were hit by the light
  if (light.modelsInView.Count > 0)
  {
	 prepareShadowRender(light);
			  
	 // render all models in this light's view to the light's shadowmap
	 for model in light.modelsInView
	 {
		model.render();
	 }
	  
  }

}

//////////////////
// FINAL RENDER //
//////////////////

for model in models:
{
   for light in model.shadowCasters:
   {
	  shaderSetupShadowMap(light)
   }
   model.render();
  
}



Obviously the relevant lists need to be cleared after each rendered. Further optimizations can obviously be made, such as caching shadows for stable objects. But I think the general structure is captured here, wouldn't you say?

Any input/feedback would be appreciated.

Edited by mv348, 15 September 2012 - 11:23 PM.


#6 RetroIP   Members   -  Reputation: 294

Like
0Likes
Like

Posted 16 September 2012 - 12:36 PM

Your approach seems correct form my view, I have something similar, but not in OpenGL.

//Generating shadows
if(shadows)
{
	 foreach light
	 {
		  foreach object
		  {
			   if(distance(object,light)<30
			   {
				    render object to shadowmap for this light
			   }
		  }
	 }
}
//Rendering lights with shadows
foreach light
{
	 if(shadows && cameradistance<***)
	 {
		  render light with shadow map
	 }
	 else
	 {
		  render light
	 }
}

Edited by RetroIP, 16 September 2012 - 12:38 PM.

Small gate to retron element on [http://retron.sk] | ProjectNN - Experimental AI project which will cover Survival, Social and Battle simulation of various lifeforms.

#7 mv348   Members   -  Reputation: 253

Like
0Likes
Like

Posted 17 September 2012 - 08:25 PM

Thanks! Glad I'm on the right track!

I have a question though. The algorithm I outlined should work fine provided I am using spot-lamps with a relatively small area of effect. However, using directional lights, such as sunlight, poses a bit of a problem. I would have to render the entire scene from a sufficient distance to capture everything. A single shadow-map spread out over an entire scene is going to look awful pixelated for large, outdoor environments.

How can I address this issue?

#8 MJP   Moderators   -  Reputation: 11790

Like
1Likes
Like

Posted 17 September 2012 - 11:09 PM

Cascaded shadow maps is the most common solution for directional lights, and I'd recommend it as a starting point.

#9 mv348   Members   -  Reputation: 253

Like
0Likes
Like

Posted 18 September 2012 - 11:19 PM

Thanks so much, MJP!

So I started reading through this article by Nvidia. I'm making some headway with it, but its a bit difficult to follow at times. Some of the details are posted in the references, but require a subscription to read.

Any particular tutorial or paper suggestions would be welcome.

#10 L. Spiro   Crossbones+   -  Reputation: 14423

Like
1Likes
Like

Posted 19 September 2012 - 05:01 AM

To avoid excessive extra-rendering, I am planning on enclosing each lamp in a bounding volume (i.e. an Axis Aligned Bounding Box) and checking for intersections with each models bounding volume.

For directional lights, I have written a tutorial on how to “capture” only objects that are inside the camera frustum or could cast shadows into the camera’s frustum, tightly excluding all other objects.

Tutorial: Tightly Culling Shadow Casters for Directional Lights (Part 1)
Tutorial: Tightly Culling Shadow Casters for Directional Lights (Part 2)


L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS