I just might be on the right track with this. The main trick here is horizon mapping, something I’ve been meaning to try out probably since around 2000 when I saw it in a GDC paper and then made a half-assed attempt at implementation of using Shader Model 1.0. I’ll hit you with a brief explanation of how it works.
First off, for each point on the heightfield, you draw lines in different directions and find which point in the world marks the horizon in that direction from the starting point. For example, in the image above, if you were standing in the middle of the frozen lake and looked east (towards the top-right of the image) the horizon in that direction would be the top of the ridge in the center of the image. Then, compute what angle that point is at above a flat horizon, as visible from the starting point. In my implementation, I’m only checking the eastern and western horizons, for reasons that will become clear in a moment.
At render time, I supply this horizon information to the shader, along with the angle of the sun. Once that’s done, all I need to do is see if the sun angle (indicated in this image by red lines) is below the eastern or western horizons — and that determines if the pixel is in shadow or not! Not only does this shade the unlit side of a hill, it also casts realtime shadows basically for free — nice!
Furthermore, this value is useful for more than just the sun. With these angles, it’s possible to tell how what percentage of the sky is visible from each point. I’m using this percentage to reduce the amount of ambient light reaching points that are nestled in valleys or depressions, as visible in the rough mountains near the bottom of the image. Effectively it’s a simple kind of ambient occlusion (a concept which is just what it sounds like — computing the amount of ambient light reaching a point.) AO is a very popular technique in realtime graphics these days, and horizon mapping turns out to be a super cheap way of doing it in realtime on terrain.
Now, there’s an obvious limitation here: since I’m only calculating the eastern and western horizons, this will only produce useful results if the sun stays on a track from directly east to directly west. It’s certainly possible to calculate horizons heading north and south, and even in more directions, and then use that to evaluate the sun’s angle, but the east-west approximation for the sun is something I’m fine with (even though this game — if I ever actually make it — is supposed to be set in Antarctica, so having such a track for the sun would be as wrong as it’s possible to be.) Slightly more seriously, using east-west as a shortcut to determine the sky visibility means points in valleys that actually run east-west will have incorrect ambient occlusion. This will probably make it worth calculating north-south horizons as well and using them solely for a more accurate precalculated occlusion value.
(The original paper suggested some even more cool concepts: namely, you could have some idea of color blending across the entire sky — yellow and orange towards a setting sun and blue far away from it — and use that to have the terrain’s ambient lighting be even more accurate. This wouldn’t be impossible to implement, but I doubt I’ll go that far; changing the overall ambient light color as well as the sun color should probably create “good enough” results even at complex times of the day like sunset.)
A more troublesome issue is that due to me cheating around a vertex blending issue that’s too annoying to explain right now these shadows are faceted. It’s not visible in this screenshot, but if you approach a shadow edge closely you’ll see a precise silhouette of the polygons the shadow falls upon. It might be possible to use a separate value, indicating whether each triangle faces east or west, to break out the precision of the shadow edge from its far end. Further work is going to be required in this area.
But, yeah! Shadows! And one final benefit is that the point normals are completely unused and so I don’t have to calculate or supply them. I might want to use them to rim-light (or rather, rim-darken) the terrain, but we’ll see if that’s even necessary once other issues are resolved.
Notes
-
mayflystudio posted this