Framework 3 (Last updated: June 26, 2012)Framework 2 (Last updated: October 8, 2006)Framework (Last updated: October 8, 2006)Libraries (Last updated: September 16, 2004)Really old framework (Last updated: September 16, 2004)
Monday, October 4, 2010 | Permalink
Rendering roads is usually done by first smoothing the terrain under the road and then rendering some simple 2D geometry on top of the terrain. This approach commonly causes issues with depth fighting though. A liberal amount of depth bias that's usually needed to solve this unfortunately also usually puts the road in front of the terrain at some places and/or clips through wheels on vehicles and other artifacts. Another solution is to put the road geometry somewhat above the terrain. This unfortunately causes visible parallax against the terrain.
This demo solves these problems by applying a technique similar to the Volume Decals demo. A road segment is rendered as a box encapsulating the terrain. The depth buffer is sampled and back-projected into the local space of the box, and the .xz coordinates are used as texture coordinates. Unlike in the decal case the road texture is tiled in one direction, so we can't just pad it with zero alpha or use the texture border color to handle the "outside the texture" case. This is solved by first cutting out the affected pixels with a stencil pass. When rendering the road the stencil bits are then reset to zero.
Road textures are typically relatively high resolution and needs mipmapping, however, when computing texture coordinates from the depth buffer we get discontinuities wherever there was a geometric edge between a triangle and the background. This causes artifacts in 2x2 pixel blocks as the gradients get messed up. In the Volume Decals demo this was solved by simply not using mipmaps, which works on the fairly low-resolution textures generally used in that technique. That's not going to work with roads though. So for this demo I sample neighbor depths. For pixels belonging to the same primitive the deltas should be constant, so whenever a delta is larger in one direction, it means we crossed over to a background pixel there. So I compare which of left and right pixel has the smallest difference in depth, and compute the texture coordinate for that pixel as well. The same is done vertically. This gives me the two gradients and I can do a SampleGrad() lookup and get the right mipmap even at border pixels. The benefit of computing correct gradients is visible in these screenshots:
With a less uniformly colored texture the artifacts would be even more obvious.
Since this technique uses the depth buffer the roads are smeared on top of the underlying geometry and no biases of any kind are necessary. This means it will work even with not so smooth geometry, as evident in the demo, although in practice you obviously still normally want to smooth the geometry under the road.
The demo allows you to place roads dynamically over the terrain using the left mouse button, and clear all with right button.
This demo should work on Radeon HD 2000 series and GeForce 8000 series and up.