About Me

Hi. I'm Josh Ols, aka n00body. Just a wannabe game developer trying to take it seriously.

View Joshua Ols's profile on LinkedIn
Recent Comments
Blog Search
Meta
Powered by Squarespace
Sunday
07Feb2010

Self-Shadowed Bump maps

For those who don't know, Self-Shadowed Bump maps were originally developed by Valve for use with their Radiosity Normal Mapping technology. After I decided to make the switch to a renderer built around pre-computed data, this was one of the first things I decided to try out. If you wish to read more about the technique, the following links have provided me with most of my information on the subject.

Links:

Efficient Self-Shadowed Radiosity Normal Mapping

Surface Detail Maps with Soft Self-Shadowing

Half-Life 2 / Source Shading

Shading in Valve's Source Engine

SSBump Generator (used to make the maps)

 

Overview:

During the course of my experiments, I have verified all the benefits that Valve described in their papers. However, I have also discovered a few drawbacks that they didn't mention. Keeping that in mind, I have pooled and summarized my findings from all the papers and my own experiments.

Benefits:

  1. Directional Occlusion

  2. Correct filtering

  3. Detail mapping

  4. Recovers bent normals

  5. Better use of byte precision

  6. Faster than tangent-space normal maps

    • NOTE: Only for RNM, not for general diffuse lighting

Drawbacks:

  1. Diffuse Lighting expense

    • In order to get diffuse lighting with directional occlusion, each light direction has to be projected onto the basis. This boils down to the same amount of math as a 3x3 matrix multiply, per light direction.

  2. Poor compression

    • DXTC causes artifacts at detail edges, and nasty blocky artifacts in specular/reflection

    • RGB5 is acceptable for diffuse lighting, but produces banding for specular/reflections. Also, this format is not supported by Direct3D 10.

 

Visuals:

height normalMapRH bumpRH ss bumpRH (SS Bump) no DO SS Bump (bumped) (SS Bump) reflection

Figure 1. 1, Height map; 2, Normal map; 3, RNM map; 4, SS Bump map, 5, no DO; 6, DO; 7, reflection


detail map SS Bump, (detailed) (SS Bump) detailed

Figure 2. 1, Detail map; 2, combined with SS Bump; 3, diffuse lighting


(SS Bump) DXT1 (SS Bump) DXT1 diffuse (SS Bump) DXT1 reflection

Figure 3. 1, DXT1 SS Bump map; 2, diffuse lighting; 3, reflection


(SS Bump) RGB5 diffuse (SS Bump) RGB5 reflection

Figure 4. 1, RGB5 diffuse lighting; 2, reflection

 

Usage Tips:

Most tools that generate SS Bump maps will format them for the Source Engine, so they assume that the Y axis is inverted. This will cause issues with any programs that use a right-handed coordinate system. Fortunately, this issue can easily be fixed in a decent image editing program (Photoshop, Gimp, etc).

Tangent-Space normals:

Here, you just need to invert the contents of the green channel to invert the Y-axis.

normalMapLH normalMapRH

Figure 5. 1, normal LH; 2, normal RH

 

Self-Shadowed Bump maps:

Here, you have to swap the contents of the green and blue channels.

SS BumpLH ss bumpRH

Figure 6. 1, SS Bump LH; 2, SS Bump RH

 

Other than that, I thought I'd mention a problem I encountered while writing shader code to use these maps. The order of the bump basis vectors as Valve listed them in the paper can be a bit confusing, and will produce incorrect results that will have you scratching your head for days. To save you that trouble, here is how they should appear:

Code:

static half3 bumpBasis[3] =
{
    half3(sqrt(2.0) / sqrt(3.0),              0.0, 1.0 / sqrt(3.0)),
    half3(     -1.0 / sqrt(6.0),  1.0 / sqrt(2.0), 1.0 / sqrt(3.0)),
    half3(     -1.0 / sqrt(6.0), -1.0 / sqrt(2.0), 1.0 / sqrt(3.0))
};

 

Final Thoughts:

Despite the added storage and lighting cost, the benefits from using this technique are too enticing for me to pass up. So I have decided to integrate this technology into my new renderer for handling static objects. Dynamic objects, or anything that needs detail normal mapping, will still use Partial Derivative Normal maps for efficiency & flexibility. Between these two solutions, I should have a sufficiently versatile system for generating pleasing visuals.


Monday
18Jan2010

Cheap specular lighting

Just the other day I read an interesting presentation from Midway, documenting their work on "Mortal Kombat vs. DC Universe". In it, they describe the various rendering tricks and engine changes they used to get Unreal Engine 3 running at 60Hz. One such trick that peaked my interest was how they approximate specular lighting.

Basically, they just treat the eye vector like a directional light, and use it to calculate lighting like normal. Then, they multiply the result by the current ambient color/intensity. This approach gives them specular highlights that are cheap, always visible, and look like they are influenced by the environment.

This is especially convenient because the equation reuses calculations from other parts of the shader. It uses NdotE that was calculated for rim-lighting, and ambient lighting calculated from SH. So it produces a passable approximation to specular lighting that fits in well with environment lighting, and doesn't require having actual lights present.

Sunday
10Jan2010

OGL -> D3D

For my project, I wanted my renderer to use OpenGL only, so I'd have a mostly unified codebase for all the potential PC platforms (Win, Mac, & Linux). Sadly, I'm finding that things just aren't that simple in practice when it comes to getting the best performance. For Windows 7, in particular, I'm just not going to get the best performance unless I use Direct3D.

So I've decided to develop my codebase like I would for a cross-platform console game. I will make high-level platform-neutral abstraction, with low-level platfrom-specific code. That means using the APIs that provide the best performance and feature access for the platform. Thus I will use DirectX on all things Windows, and OpenGL/OpenAL/etc everywhere else (barring console development).

Other than that, aside from platform-specifics I'm only going to use APIs that don't have a licensing fee attached to them.

Wednesday
06Jan2010

Computer issues...

Well, my computer of almost 6 years decided to kick the bucket this past week. Right now, I'm trying to get a new one set up, and will hopefully have something new to write about soon.

Cheers!

Sunday
20Dec2009

AA + HDR encoding

Okay, first bit of progress with my new rendering approach. I started by testing out NAO32/RGBM for encoding HDR values in an MSAA/CSAA RGBA8 buffer. Since these are two of the major features that made me decide to make the switch, it seemed appropriate to test them first. So without further ado, I present the gruesome results!

First I will show the results of each sample setting by itself, then its difference from no AA. Please note, I found no appreciable visual difference between 8x MSAA and 8x/8xQ CSAA, so I will not be posting them.

 

NAO32:

NAO32 MSAA 1x NAO32 MSAA 2x NAO32 MSAA 4x NAO32 MSAA 8x

Figure 1. 1. No MSAA, 2. 2xMSAA , 3. 4xMSAA, 4. 8xMSAA

 

NAO32 MSAA 2x (difference) NAO32 MSAA 4x (difference) NAO32 MSAA 8x (difference)

Figure 2. 1. 2xMSAA , 2. 4xMSAA, 3. 8xMSAA

 

RGBM:

RGBM MSAA 1x RGBM MSAA 2x RGBM MSAA 4x RGBM MSAA 8x

Figure 3. 1. No MSAA, 2. 2xMSAA , 3. 4xMSAA, 4. 8xMSAA

 

RGBM MSAA 2x (difference) RGBM MSAA 4x (difference) RGBM MSAA 8x (difference)

Figure 4. 1. 2xMSAA , 2. 4xMSAA, 3. 8xMSAA

 

Conclusions:

As you can see from the comparison shots, both schemes produce acceptable results for 2x/4x MSAA, but start to fall apart for 8x MSAA and equivalent quality CSAA modes. The edge quality improves, but color artifacts start appearing around the scene. So, there goes CSAA, and any MSAA modes above 4x. Still quite useable, but a bit of a bummer nonetheless.