Volume Roads demo

Humus

Crazy coder
Veteran
It's been a while since I posted my last demo, but now it's time again! :)

This time I demonstrate how to apply roads over terrain using depth buffer information and thus avoiding any clipping or visible parallax issues common on roads in games.

Download the demo from here:
http://www.humus.name/

VolumeRoads.jpg
 
Not related to the technique you're demonstrating, but AA doesn't work for me. And you know what you said about AA :yes:
 
Not related to the technique you're demonstrating, but AA doesn't work for me. And you know what you said about AA :yes:

Well, this is not a game. ;)
I could make it work with AA, but that would bump the requirements to DX10.1, plus add some work, so I took the easy route.
 
Nice to see someone talking about discontinuous gradients in screen space deferred rendering techniques. Not so much has been talked about this issue and how to solve it. Mip mapped shadow maps and mipmapped projector textures (when rendering light geometry to g-buffers) suffer from similar gradient issues. Basically this is the reason why we have always used light projector textures without mipmaps. Fortunately cascaded shadow mapping results in near 1:1 texel mapping everywhere when rendering the main sun light, and this makes it possible to render high quality shadows without mipmapping the shadow map in deferred renderers (and shadow filtering like EVSM also helps of course). It would be enlightening to hear how other developers have solved mip mapping gradient issues in their deferred renderers.

I have always disliked rendering decals every frame on top of the existing geometry. It introduces so much overdraw (especially when rendering decals with full material channels, normals and parallax occlusion mapping). Baking decals directly to the surface textures (during runtime) is more elegant (in both performance and engine simplicity points of view). But unfortunately requires unique mapping for all surfaces, so it's basically limited for games with virtual texturing or with simple game worlds that do not have that many visible objects on the screen at once (2.5d side scrollers like LittleBigPlanet for example or isometric strategy games). For example in Warhammer 40K: Squad Command we rendered the roads and all the other terrain textures / decals to a slightly larger than screen sized texture (512x512 if I remember correctly, PSP screen is 480x272) and projected it to the bumpy terrain (top projection). Because the isometric camera was pretty much locked (only rotate around y-axis and slight tilting was possible) and the viewport terrain area shown was limited, this technique worked really well, and made it possible to update the terrain texture just partially every frame (new area revealed by scrolling). In our newest engine we render all our object and terrain decals (including roads) to virtual texture pages (during page generation in runtime). We were going to render decals as volumetric areas in our newest engine, but when we started to experiment with virtual texturing, it proved to be superior in our analysis (performance was dramatically better on complex decals), and it solved the texture streaming problem at the same time.
 
Last edited by a moderator:
Well, this is not a game. ;)
I could make it work with AA, but that would bump the requirements to DX10.1, plus add some work, so I took the easy route.

Quote this for most developers and see why we don't have AA in so many titles anymore.
 
Cool demo, as always!

It would be enlightening to hear how other developers have solved mip mapping gradient issues in their deferred renderers.
For shadow filtering (as in the demo+source here) we do something similar to this demo; namely we store depth derivatives in the frame buffer, which is equivalent to storing a face normal (2 16-bit floats should be plenty; probably can compress it even more if need be). We use them to reconstruct position derivatives in the deferred pass. Since shadow map texture coordinates/derivatives are a function of these positions/derivatives we simply do a quick forward differencing approximation of the shadow map derivatives. Details in the source code above (see GBuffer.hlsl:ComputeSurfaceDataFromGBuffer(...)). Of course if you had an arbitrary parameterization you'd need to store derivatives for it directly, but many of the techniques that require filtered sampling in the deferred pass are things derived from position.


Quote this for most developers and see why we don't have AA in so many titles anymore.
It'll come back though. Lots of developers talking lately about how reducing aliasing, etc. is their #1 priority on "next generation" games. Consoles are holding that back a little bit right now but I think it's safe to assume that the next generation of consoles will provide a better ability to do some good AA.
 
For shadow filtering (as in the demo+source here) we do something similar to this demo; namely we store depth derivatives in the frame buffer, which is equivalent to storing a face normal (2 16-bit floats should be plenty; probably can compress it even more if need be). We use them to reconstruct position derivatives in the deferred pass.
I just came up with a similar idea :) . Actually you could just save the "mip level" of the base texture in a single 8 bit channel in your g-buffers. The decal uses the same mip level as the base texture (if it has equal texture dimensions). If you save the mip level to g-buffers in global scale (remove texture dimension from the equation), you don't have to know the base texture dimension, and you can just add/decrease the global mip based on the decal texture dimension you are just rendering. 8 bit channel is perfect for bilinear filtering, but doesn't offer perfect trilinear quality (if your max texture size is 65536 you get 16 blending steps between trilinear mips). But 16 step trilinear should be good enough for console titles at least.

Nice technique there, thanks for sharing!
 
Actually you could just save the "mip level" of the base texture in a single 8 bit channel in your g-buffers.
Yup definitely if you don't need anisotropic filtering you can just compute the mip level and use SampleLevel (or equivalent) directly and save yourself some math :) Simple linear scales and biases can be done in the deferred pass as you note. In our case we needed aniso filtering, which we were happy to fit into only 2 extra values of storage.

Nice technique there, thanks for sharing!
My pleasure!
 
Quote this for most developers and see why we don't have AA in so many titles anymore.

Well, it's a bit different how I approach things at work and how I do when I do demos for illustrating a particular technique. At work I rather spend an extra few days on getting things done right and try to make sure design decisions are right. For a demo it's not nearly as important. Right now my priorities are a bit different since I'm getting married in two weeks, and that's taking all my time, so it's a bit of a miracle I managed to get this out of the door at all.

For shadow filtering (as in the demo+source here) we do something similar to this demo; namely we store depth derivatives in the frame buffer, which is equivalent to storing a face normal (2 16-bit floats should be plenty; probably can compress it even more if need be).

Interesting. Your approach will be more accurate, but at the expense of needing additional storage space. My approach may result in incorrect gradients on corner pixels of triangles, but I'm not sure if this will be visible in practice even for the eagle eyed. At least in my demo I don't see any such problems. Would be interesting to compare performance of the two approaches.

I just came up with a similar idea :) . Actually you could just save the "mip level" of the base texture in a single 8 bit channel in your g-buffers.

This would work and probably be preferable as long as you don't need anisotropic filtering. If you need aniso you're going to need gradients.
 
Interesting. Your approach will be more accurate, but at the expense of needing additional storage space.
Indeed - the approach we used is basically indistinguishable from hardware derivatives. In fact you can avoid the additional storage by using the shading normal to approximate the plane instead and working out the ray intersection with that plane for the adjacent pixels. This actually works pretty well in most cases although since shading normals are imperfect you do have to handle edge cases (inf, etc). We chose to stick to the "extra storage" method (basically storing a normal *and* a face normal equivalent) so the filtering would be above reproach, but in a real game I'd heavily consider just reusing the face normal.

My approach may result in incorrect gradients on corner pixels of triangles, but I'm not sure if this will be visible in practice even for the eagle eyed.
Yeah if you're doing a normal texture it shouldn't be a problem (your method is conceptually similar to bilateral filtering). In our case it was because big derivatives => huge filter regions => inaccurate approximation of shadowing (very bad upper bound - goes to no shadow). Thus at some edges of silhouettes in shadowed regions you'd get a 1-pixel error where they were lit which was very obvious. Maybe in these cases it would be sufficient to just set the derivatives to zero (i.e. highest mip level) since you're probably only talking about one pixel, but we didn't experiment a lot as the solution we implemented was relatively inexpensive.

Again though in practice you could probably just clamp the max mip level, but we wanted to do it the "right" way at least to see how well it worked :) And of course if it was just a blurrier texture on the edge I severely doubt anyone would notice since derivatives often get a bit wonky on some silhouettes anyways. I certainly saw no problems what-so-ever in your demo.
 
Last edited by a moderator:
I did a quick experiment and found that my method is a bit faster, at least for this demo. At 1600x1200 with an HD5870 there was a 0.1ms increase in the terrain pass for writing out the the gradients and a 0.03ms lower cost for the road pass for fewer samples to read and less math needed. Framerate dropped from 712fps to 665fps.
 
Last edited by a moderator:
I did a quick experiment and found that my method is a bit faster, at least for this demo. At 1600x1200 with an HD5870 there was a 0.1ms increase in the terrain pass for writing out the the gradients and a 0.03ms lower cost for the road pass for fewer samples to read and slightly fewer samples needed. Framerate dropped from 712fps to 665fps.
Did you add an extra render target (2 x 16 bit float) for the gradients? If you haven't used the alpha channel of your color buffer, you can just store the mip level there. It should be basically free, and should also boost up the decal pass (one less texture fetch).
 
Cool good info. It would definitely be faster to do the approximation using the shading normal as well (pretty simple math - I think the math is actually in the demo I linked but not currently called) since it doesn't require any additional storage or bandwidth. No doubt you're solution is probably the fastest though Humus!
 
Did you add an extra render target (2 x 16 bit float) for the gradients? If you haven't used the alpha channel of your color buffer, you can just store the mip level there. It should be basically free, and should also boost up the decal pass (one less texture fetch).

Yes, I did. I do a semi-deferred rendering and put the lighting into alpha, so there's no free space anywhere. But since I use anisotropic filter (which I would say is absolutely necessary for roads) a mipmap level alone is not enough, I need gradients. For my Volume Decals demo though, storing the mipmap level would have been ideal.
 
Humus, I have a question regarding this method: would that also work with MSAA enabled ?

I haven't had a deep look in the source code yet, before I spend some time doing this, I would prefer knowing if it is worse it or not...

In any case this method looks very nice, so I hope your answer will be 'yes' :)

thanks !
 
It can work with MSAA, but this is kind of a "deferred" approach, so it has the same complexities as deferred shading for lighting with MSAA.
 
Back
Top