Skip navigation

I posted the following on the Card Kingdom development blog.

Background

One of the major updates to Card Kingdom going forward is a complete rewrite of the rendering pipeline. In the original version, the alpha version essentially, we used a”forward rendering” style for our rendering process. In a forward rendering system, all lighting information is calculated when the mesh is rendered to the screen. For scenes with multiple lights, the mesh would have to be drawn for each light and blended together to get the final image. For non-complex, low lighting count scenes, forward rendering is straight forward and works well. As the number of lights and the complexity of the geometry that is being rendered increases, the processing time drastically increases. The complexity can be modeled as O( n * m ), where n is number of triangles, meshes, objects to render, and m is the number of lights in the scene. Rendering times drastically increase when more lights and triangles are drown. This is why the number of supported lights is usually around 8.

In this version of Card Kingdom we’re working on now, beta if you will, the rendering pipeline now uses “deferred rendering.” The “deferred” part comes from moving the lighting calculations until after all of the geometry has been rendered. Diffuse/albedo, normals, specular color and power, depth and anything else that is required are rendered to different render targets, saving the information for compilation and processing later. Then, each light then only operates on what was rendered to the screen. This changes the complexity of rendering to O( n + m ) where n and m are what they were in the previous example. This means that the number of lights can increase far beyond the forward rendering’s 8 to practically infinite (with in reason). Different optimizations can be done on the lights, as point lights only influence a set area and lights that are not visible do not need to be calculated.

Implementation

G-Buffer

The separation of the different components in placed in a structure called a “g-buffer.” The g-buffer for Card Kingdom is structured as such:

  1. The Diffuse/albedo buffer is a 32bit wide, R8G8B8A8 texture that stores the final color and alpha.
  2. The Normal buffer is a 32bit wide, R8G8B8A8 texture that stores normal information. The last component is not currently being used, but could be used for object or materiel IDs.
  3. The Specular buffer is a 32bit wide, R8G8B8A8 texture that stores both the specular color, as RGB, and the specular power, as A.
  4. The Depth buffer is a 32bit wide, single element R32 that stores the depth of the pixel as a single floating point value, R.
  5. The Occlusion buffer is a 32bit wide, single element R32 that stores the occlusion value of the pixel as a single floating point value, R.
  6. The Lighting buffer is a 32bit wide, R8G8B8A8 texture that stores both the accumulated lighting information stored as RGB, and the edge detection value, stored as A.

For Card Kingdom, we are increasing our display size from 960 x 540 to 1920 x 1080. This means that the screen size buffer is 4 times larger in area. Storing all the components as 32bit wide textures saves space and processing throughput of the graphics card.

Rendering Phases

The rendering process is then separated in to multiple phases: geometry phase, lighting phase, composition phase.

  • The geometry phase only affects the diffuse, normals, specular information, and depth buffers by writing geometry information to them.
  • The lighting phase uses the normals, specular and depth information to calculate each light and their contribution to the scene and writes out to the lighting accumulation buffer.
  • The composition phase is where the diffuse from the first phase is composed with the light accumulated from all the lights. Additional post-process effects can easily be added after the scene has been composed.

In Card Kingdom the lighting step also includes a edge detection step that fills in the alpha component of the lighting buffer. This step is interesting because a custom blend state needs to be used in order to set the alpha channel without touching the color channels of the texture. Also during this step, the occlusion factor will be calculated.

P-Buffer

What I call the “p-buffer” is a collection of buffers used in the composition phase of the rendering process.

  • The Full 0 and Full 1 buffers are full sized, R8G8B8A8 textures that are swapped between for each post-process effect
  • The Half buffer and Quarter buffer are a half and quarter sized, R8G8B8A8 textures that are used to scale down the previously rendered frame for different effects, such as bloom.

For each post process effect, the full buffers are swapped between render target and shader resource. Buffers from the g-buffer are also bound during the post processing stage to provide extra information for varying effects.

Results

Here are some of the results of deferred rendering.


First testing of the deferred rendering system. The top left is the diffuse buffer, top right is the world-space normals, bottom left is the depth values (they apear white because the values are greater than 1 and DirectX clamps these values from 0 to 1), and the bottom right is the lighting calculations of two point lights based on the values in the g-buffer.

Rendering 16 point lights, accumulated in the lighting buffer.

Inclusion of an animated mesh.

This is an early test of the alpha version of the game. Again, the top left is the diffuse, top right is world-space normals, bottom left is depth and bottom right is the results of the edge detection.

This is a more recent render of the composition of the diffuse and lighting information, as well as an outline shader based on the edge detection phase. This gives a great preview of what the final game would look like.

This screenshot shows off a depth of field effect that can be simply included as a post process effect using both the p-buffer to accumulate renders (lighting and diffuse combination, edges, etc.) and the g-buffer’s depth buffer to blur pixels that are far from the camera.

There is still more work to be done on the new deferred rendering pipeline for Card Kingdom (optimizing the lights, adding screen-space ambient occlusion, adding shadows, adding proper alpha blending, motion blur and heat shimmer effects, etc.), but in its current form, I am quite please with the results.