Jump to content
  • Advertisement
Sign in to follow this  
mark ds

Self-shadowing terrain idea - asking for feedback

This topic is 2123 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been wrestling for some time on how to produce decent quality, self-shadowing terrain that effectively shadows all other objects in the world. A couple of days ago I had an idea.

 

For any given point on the terrain, the direct sunlight contribution can be calculated thus:

 

While the sun is below the horizon or behind occluders (hill, mountains etc.) it's contribution is 0. We can describe this as a function of time - between 9pm and 10am, for example, a point of the terrain is NOT lit by the sun.

 

While the sun is fully visible it's contribution is 1. The can happen at between say 10.30am and 8.30pm.

 

The in between times (as the sun appears over distant peaks) would essentially represent the penumbra period - where we could interpolate, based on time of day, a contribution factor between 0 and 1.

 

However, whilst the above would work well for the ground, no information would be available to correctly light buildings or other objects.

 

So my idea is to represent sun light using two height values per point on the terrain (probably per vertex in a heightmap). The first value is the height at which the sun begins to fully light a point above the terrain, and the second value the point below which no sunlight has an effect (the first value would always be higher than the second).

 

So a fragment shader would effectively be:

 

if fragment is above first value, sunlight contribution = 1;

else if fragment is below second value, sunlight contribution = 0

else interpolate between the two.

 

This contribution would be included in a g-buffer (after a depth only pass).

 

The sun changes position slowly over time, so I think it's perfectly possible to recalculate new values by dealing with a fairly small sector of the 'sunlightmap' each frame and storing the results in a 16-bit RG texture.

 

I haven't really fleshed out this idea, but I thought I'd throw it out there and hopefully get some feedback.

 

Cheers.

 

 

 

 

Edited by mark ds

Share this post


Link to post
Share on other sites
Advertisement

So my idea is to represent sun light using two height values per point on the terrain (probably per vertex in a heightmap). The first value is the height at which the sun begins to fully light a point above the terrain, and the second value the point below which no sunlight has an effect (the first value would always be higher than the second).

 

 

I'm not sure I follow. What defines these "height points" and what heightmap are you storing them in? And how does that give you a shadow? All I can think of is it would give you a weird height-based darkening of the world, even if the sun is fully visible (think a beach scene). I guess I'm just confused. :B

 

On the topic though, I think my favorite approach so far is what was done for Crysis. For the sun, you render the terrain (only the terrain, low LODs too if you want) to a low-resolution VSM shadow map and blur it. Then during your main sun shadow pass you use the darkest of both shadow terms as your shadow term (so that you don't get a multiplying effect).

 

Edit: Oh you added a graph, sweet. How do you get the highest points though? Wouldn't this break anywhere the terrain was uneven?

Edited by Styves

Share this post


Link to post
Share on other sites

I've added an image to illustrate.

 

You'd have a normal heightmap which contains a single height value. A second heightmap would have two values, the height above which is fully lit (shown in blue), and the height below which is fully in shadow (shown in red). A fragment would get it's derive it's lighting calculation depending on it's vertical location in the world - so a point half way between blue and red would get 50% sunlight (or 50% shadow, depending on your viewpoint!).

 

This second 'sunlight heightmap' would get updated over time as the time-of-day progresses.

Share this post


Link to post
Share on other sites

I don't know what you mean by "the height at which is fully lit" and "the height below which is fully in shadow". Are you just randomly picking numbers based on the time of  day and interpolating or are you reading the depth of the heightmap to compute these values? And are they local to each point?

 

It sounds like a lot of work for a really coarse approximation. If you need 2 heightmap values for such an approximation, it would probably be easier and visually more pleasing to simply render a terrain shadow map, cached over a few frames and rendered over the world - you'll get proper terrain occlusion and all for pretty cheap.

 

 

Though I'm still a bit confused on this so I'll wait to see how this goes. Maybe you can try implementing your idea and posting some results as a proof of concept?

Share this post


Link to post
Share on other sites

If I understood correctly, you have a shadow function that depends on time (let's call it s(t)) and you want to describe this function. For this, you want to make is a smooth step function (_/---) so you just need to store the coefficients. Keep in mind though, that you need more than 2 values. s(t) looks more like this (sunrise ___/--day--\___ sunset), so you need at least 4 values. Still, it should be pretty easy to implement. This, of course, assumes fixed relation between the ground and the sun.

Share this post


Link to post
Share on other sites

This reminds me of Polynomial texture maps.  You can store the coefficients of a quadratic function at^2 + bt + c such that at any time t you can calculate a sunlight value.  This works perfectly as long as you have only two transitions, from dark->light->dark or light->dark->light.  More transitions require more terms.  But for terrain without holes ( like a tunnel or a building with translucent windows ), this should work.

 

http://www.hpl.hp.com/research/ptm/papers/ptm.pdf

Share this post


Link to post
Share on other sites

If I understood correctly, you have a shadow function that depends on time (let's call it s(t)) and you want to describe this function. For this, you want to make is a smooth step function (_/---) so you just need to store the coefficients. Keep in mind though, that you need more than 2 values. s(t) looks more like this (sunrise ___/--day--\___ sunset), so you need at least 4 values. Still, it should be pretty easy to implement. This, of course, assumes fixed relation between the ground and the sun.

 

Totally correct. I envisage having 'snapshots' of the shadow information on the GPU as a two channel texture, updated at regular intervals on the CPU. This wouldn't be a problem because, in reality, many regions wouldn't change very often - only as a shadow passes over them.

Share this post


Link to post
Share on other sites

This reminds me of Polynomial texture maps.  You can store the coefficients of a quadratic function at^2 + bt + c such that at any time t you can calculate a sunlight value.  This works perfectly as long as you have only two transitions, from dark->light->dark or light->dark->light.  More transitions require more terms.  But for terrain without holes ( like a tunnel or a building with translucent windows ), this should work.

 

http://www.hpl.hp.com/research/ptm/papers/ptm.pdf

 

I wasn't aware of that paper, interesting.

You are correct about only two transitions. I'm basing my assumptions on the sun rising and setting vertically to guarantee the simplest shadow information.

Share this post


Link to post
Share on other sites

This is called "Horizon Mapping" (http://research.microsoft.com/en-us/um/people/cohen/bs.pdf) iirc.  Your implementation of 2 values per point will only work for 1 axis of light though. Take your image for example, what happens when the sun moves over the sky to the other side and begins to set? The occlusion heights from that direction are different, and now your precomputed 2 values are no longer correct.

Share this post


Link to post
Share on other sites


We can describe this as a function of time - between 9pm and 10am, for example, a point of the terrain is NOT lit by the sun.
 
While the sun is fully visible it's contribution is 1. The can happen at between say 10.30am and 8.30pm.
I've you've not read this already, then check out the GPU gems chapter on occlusion interval maps; it's a very simillar idea biggrin.png

https://developer.nvidia.com/content/gpu-gems-chapter-13-generating-soft-shadows-using-occlusion-interval-maps

 


So my idea is to represent sun light using two height values per point on the terrain (probably per vertex in a heightmap). The first value is the height at which the sun begins to fully light a point above the terrain, and the second value the point below which no sunlight has an effect (the first value would always be higher than the second).
The nice thing about the above technique is that you can precompute it once, and then use that data to perform shadowing all day long without any further occlusion computation.

 

With this idea, your two height values change over the course of the day, so whenever the sun moves, you need to generate new height values for the whole terrain... That's fine, but you're just giving up one nice advantage of horizon or occlusion-interval maps.

 

I imagine that when the light moves, you'll basically have to run a real-time light baker over the scene, which, for each terrain lightmap texel, will find the two height values.

You could do this by rendering two directional shadowmaps from the sun's point of view, facing along the two lines in your diagram. For each lightmap texel, you'd then check if it is in shadow, and if so, raycast upwards until you find a height that is not in shadow.

 

That rebaking might be expensive, but you could split it up over multiple frames -- generating part of the shadow map and/or part of the lightmap each frame. A game that I'm currently working on does this with regular lightmaps. Over the course of a few minutes, I use 1ms of GPU time each frame to bake a new lightmap, and when it's complete, I swap it with the current lightmap and start baking the next one. This let us add dynamic time-of-day to the game, while keeping our very, very cheap to render light-mapped shadows.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!