Deferred rendering with skyboxes

XNA Dude has only one animation: "haters gonna hate"

There is one simple but important rendering feature that helps make any game world more immersive- skyboxes. I have planned to add it for a while, but for a deferred renderer the solution didn’t come to me immediately.

You mainly deal with rendered scenes that later get “flattened” to textures projected onto the viewport, so adding backdrops can’t be done as it is with forward rendering. Skyboxes have the problem of not needing to be treated with the same lighting effects as other objects. Also, they cannot interfere with the scene’s 3D space. I’ve read a few topics on the App Hub forums on how people handle backdrops with deferred renderers, and there was a lot of conflicting information. Should I use one depth clearing effect, or should I treat the skybox like a light? Or can two approaches be combined for more flexible usage? Fortunately the solution to was simpler than I expected.

No special skybox class is used- it’s just another InstancedModel object which is like any other one, except that it’s not placed in the culling list.  So actually, we can just assume that it can be anything, either a box or a sphere or something in between. There’s no need to cull a skybox most of the time, and if drawn after everything else, we can reduce the fillrate since a lot of the screen will already be covered with the scene objects. The skybox can be scaled, positioned and rotated manually so that it will look right with the rest of the scene.

What did come to me more naturally was the concept of viewport depth. By splitting up the viewport’s depth range, the skybox can be rendered separately from the rest of the scene without having it interfere or block other objects. With this in mind, a separate rendering method was used for only the skybox, which restricts the viewport to a very far distance. No big depth sorting tricks are necessary, we can just keep the GraphicsDevice.DepthStencilState to Default. We would use these steps to draw the skybox with everything else:

  • Clear the GBuffer
  • Set viewport depth range from 0 to n (some value very close to 1)
    If using deferred rendering:

    • Render the scene to color, normals, and depth in the GBuffer
    • Set viewport depth range from n to 1.0
    • Render the skybox with face culling disabled, only to the color of the GBuffer
    • Accumulate all lighting

    If using light pre-pass rendering:

    • Render the scene to normals and depth in the GBuffer
    • Accumulate all lighting
    • Clear the viewport, render the scene with just the color
    • Set viewport depth range from n to 1.0
    • Render the skybox’s color with face culling disabled
  • Combine color with lighting to draw the final image

Everything looks right, except that the skybox is unlit, so nothing would still show up. We could increase the ambient lighting a fair amount, but that will make everything else too bright and washed-out. So instead a special case is made for depths that are greater than n to set the ambient lighting bright enough for the skybox to show correctly. This was simple to add into one of the lighting shaders, where depth can be sampled. To keep the skybox from moving, we just translate its position along with the camera. We also need to clamp the textures in the skybox so that no visible seams appear.


Past and present of the rendering engine

As I mentioned before, I have been working on a rendering engine for XNA called the Meteor Rendering Engine. I’ve only been working on it for a little over a month but already it’s come a long way. Even though it started out as simply a tutorial project from Catalin Zima-Zegreanu, I made enough modifications to it to call it my own.

Continue reading