**Introduction**

When we implement
the pipeline of Tone Mapping, we always need to calculate the average luminance
of a rendering texture in which float format mostly. Generally, there are two
algorithms to calculate the average luminance: geometric mean and arithmetic
mean.

The arithmetic mean A is defined by the formula

We assume thedenote ith texel of texture, Apparently,
the arithmetic mean could be implemented easily, how about the geometric mean? Yes,
you know it, we employ the so-called log average to get the geometric mean
value.

The process is
like that:

Hence, we could
calculate the geometric mean in the same way of calculation of arithmetic mean
except to append a log and exp operations.

**Implementation**

First we explore the calculation arithmetic
mean of texture, the difference between geometric mean and arithmetic mean is
just to add a log and exp operation in the shader.

1. Based on
DX11

There are three methods to
calculate the average luminance:

(1)
D3D API:
with the function of “GenerateMips”, you should first translate the texel(RGB) to
luminance and call the function “GenerateMips”, then we can get the average
luminance from the mip 1x1 level. It works based on the algorithm of generating
mipmap with box filter, as long as now, it’ ok for DX.

(2)
Computer
shader: with the new shader stage of DX11, computer shader, we can write a
computer shader to do GPGPU operation, for calculating the average luminance,
it easy to implement just a bit of more works compared to use function “GenerateMips”.

(3)
Rendering
multi-pass: actually it’s primary adapt to DX9, we explore it in DX9.

2. Based
on DX9

As far as I know, we only have one
approach to do it, rendering multi-pass

(1)
Rendering
multi-pass: with DX9, we don’t have the “GenerateMips” or computer shader, so
we need to calculate the average luminance by repeated rendering pass. We benefit
from GPU’s property of linear sampling to do down sample pass by pass. Is a
simple pixel shader is enough? Yes, but you should be cautious of sampling. Suppose
we have a original rendering texture with size of 1024x1024, usually the queue
of down-sample rendering pass would be 1024x1024->256x256->64x64->16x16->4x4->1x1,
a quarter was choose because we can down to 1x1 quickly, the only extra works
is that you should do pcf youself in the shader. For instance,
1024x1024->256x256, a texel of 256x256 just the average of 16 texels which
illustrated as follow:

Figure 1: down sampling

Where the GPU’s linear sampling is
like

Figure 2: linear sampling

Hence, in the shader we do some
things as follow

float2 offset[4] =

{

0.0f,
0.0f,

1.0f,
0.0f,

0.0f,
1.0f,

1.0f,
1.0f

};

for(int
i=0; i<4; ++i)

{

oColor += tex2D(baseTex, texCoord.xy + (offset[i] +
0.5f) * invTexSize.xy);

}

oColor *= 0.25f;

**Note:**we need half pixel offset for DX9 render texture, but on DX11 not.