# How to adapt diffuse/GI light mapper to directional light maps?

I have a working light mapper that captures global illumination. I evaluate a hemisphere when collecting the data and store the result in the texel. However, I want to support directional lightmaps so that I can more realistically light normal maps at runtime. What strategies are there to adapt my light mapper's final gather operation to capture this data? Is it as simple as performing three hemisphere samples per texel in orthogonal directions? Or should I uniformly sample the hemisphere once, and project the results onto the basis vectors? Or do I sample the hemisphere and project each sample on to the basis.

I've done some tests with the latter - projecting the each sample on the hemisphere onto the three basis vectors - and this works mostly, but I'm not sure about whether the projected samples should be clamped when their dot product is facing away from the basis or not.

When reading all of Valve's HL2 Radiosity Normal Mapping articles, the need to generate three lightmaps for the bases vectors is clear, as are the coordinates for the bases vectors, as is the reconstruction in a shader of the data - this is straight forward enough. See Valve SIGGRAPH06, Valve Tutorial and Peter Houska on Directional Lightmaps).

The complication comes from how to generate those three samples:

For example - from a Valve GDC talk:

• Traditionally, when computing light map values using a radiosity preprocessor, a single color value is calculated

• In Radiosity Normal Mapping, we transform our basis into tangent space and compute light values for each vector

Given that I am currently sampling on a hemisphere, do I simply project each sample onto the bases and store those accumulations? If I do that, how do I handle the fact that data within my original hemisphere, when projected onto the bases, could create a projection that actually subtracts energy, given that a sample ray pointing away from the bases gives a negative dot product.

Take a look at Valve's paper on combining Radiosity and Normal Mapping. It had a lot of useful insights.

If I'm not mistaken, it looks to me like you're trying to compute normal mapping using the energy value at the texel that is a result of Radiosity - e.g. it came from all directions through almost-endless bumping around the room across thousands of surfaces.

But your point light, for the normal mapping, has a very specific position, direction, distance and intensity. Meaning, you're combining apples and goats.

What I believe you should do is: - compute the normal mapping separately for those surfaces - blend the GI solution with normal mapping - experiment with the blending factor till it looks 'good enough' for you

• Thanks for the suggestions. I've read Valve's paper on Radiosity and Normal Mapping. They describe projecting the results of the radiosity onto the three basis vectors. They don't distinguish between direct and indirect lighting. I don't think you want to consider normal mapping in the baker - my understandings is that the point of the three basis is so that the normals can access the full hemisphere of radiosity at runtime when performing bump mapping. – Steven Feb 15 '17 at 4:54
• Of course. The whole point of Radiosity is that it is indirect, so Normal mapping isn't really part of the total energy distribution solution. Of course, you could subdivide the surface even further to the level of individual bumps (in the normal map), but at that point you're at a subpixel level and that brings whole lot of other issues. Then again, if you had a CUDA Radiosity shader, it should still be doable. – RubyNoob Feb 15 '17 at 16:05
• Wait - what? Light maps are almost always lower resolution than the diffuse and bp maps. That's why i am trying to generate directional lightmaps to encode the static lighting so i can "light" the normal maps at runtime. – Steven Feb 15 '17 at 16:22
• Yeah, well that's the decision that only you can make, as only you know how high/low frequency the diffuse maps are and what is the actual visual style you are aiming for. But with Radiosity, you really don't want to use low-resolution lightmaps. I mean, the shadow boundary that Radiosity produces is one of the most beautiful things in computer graphics. You don't want to loose that to save some memory, now do you ? Do you happen to have any screenshots you are willing to share so we know what exactly are we talking about here ? – RubyNoob Feb 15 '17 at 16:26

In the end, what I gleaned from the Valve paper, and what I found in an existing light mapper implementation, led me to the following conclusion:

When performing my final global illumination integration, instead of using a cosine-weighted hemisphere of samples, use a uniform hemisphere of samples. For each sample, project the sample's direction on each of the three basis vectors, scaling the sample colour by the resulting projection. The three sums are accumulated, and then divided by the number of samples. At runtime, the normal map is projected onto the basis and the contributing lightmap colour for each basis is scaled by the projection prior to accumulating.

Color3 basisSum[3] = { 0, 0, 0 };

for ( each sample in hemisphere... )
{
Color3 colour = trace_sample(sample);

for ( int b = 0; b < 3; b++ )
{
float dot = Dot( sample.directionTS, basisTS[b] ); // both in tangent space
basisSum[b] += colour * dot;
}
}
for ( int b = 0; b < 3; b++ )
{
basisSum[b] /= numSamples;
}


I made no special case handling for clamping the back-side of the basis in the tool - my experiments and research lead me to conclude that the entire hemisphere should be represented for each basis, even if intuitively the backwards-facing samples seem like they would 'subtract' colour because of a negative dot product.