I have set up an 'Order Independent Transparency' method for drawing my transparent objects.
The algorithm can be simplified as follows:
DrawOpaqueObjects() DrawTransparentObjects()
Drawing the transparent objects uses the following render target setup:
colourAccumulation = new RenderTarget2D(
game.GraphicsDevice,
game.GraphicsDevice.PresentationParameters.BackBufferWidth,
game.GraphicsDevice.PresentationParameters.BackBufferHeight,
false,
SurfaceFormat.Rgba64, // 16 bit floating point texture (required for alpha blending caculations)
DepthFormat.None
);
depthComplexity = new RenderTarget2D(
game.GraphicsDevice,
game.GraphicsDevice.PresentationParameters.BackBufferWidth,
game.GraphicsDevice.PresentationParameters.BackBufferHeight,
false,
SurfaceFormat.Rgba64, // 16 bit floating point texture (required for alpha blending caculations)
DepthFormat.None
);
The transparent objects are combined with another render target texture that all the opaque objects are drawn to before hand:
scene = new RenderTarget2D(
graphicsDevice,
graphicsDevice.PresentationParameters.BackBufferWidth,
graphicsDevice.PresentationParameters.BackBufferHeight,
false,
SurfaceFormat.Color,
DepthFormat.Depth16, // Requires a depth format for objects to be drawn correctly (e.g. wireframe model surrounding model)
0,
RenderTargetUsage.PreserveContents
);
They are combined as follows:
PixelShaderOutput PixelShaderFunction(VertexShaderOutput input)
{
PixelShaderOutput output;
// Background colour 'Cbg'
float4 BackgroundColour = tex2D(BackgroundSampler, input.TextureCoordinates.xy);
float4 SumColour = tex2D(Texture0Sampler, input.TextureCoordinates.xy);
// [Depth complexity]
// • Each time a pixel is written by the transparent object it adds 1 to the value [float4(1, 1, 1, 1)]
// • This is why a render target with 16-bits per channel is required (SurfaceFormat.Rgba64)
float n = tex2D(Texture1Sampler, input.TextureCoordinates.xy).r;
// If no transparent pixel has been drawn at this location, return the background colour
if (n == 0.0)
{
output.Colour = BackgroundColour;
return output;
}
SumColour = Upscale(SumColour);
n = Upscale(n);
// Average colour 'C'
float3 AvgColour = SumColour.rgb / SumColour.a;
// Average alpha based on depth complexity 'A'
float AvgAlpha = SumColour.a / n;
// T = (1 - A) ^ n
float T = pow(abs(1.0 - AvgAlpha), n);
// [Weighted Average Formula]
// • Cdst = (C * A * (1 - (1 - A)^n) / A) + Cbg * (1 - A)^n
// • Cdst = (C * A * (1 - T) / A) + Cbg * T
// • Cdst = C * (1 - T) + Cbg * T;
output.Colour.rgb = AvgColour * (1 - T);
output.Colour.rgb += BackgroundColour.rgb * T;
output.Colour.a = 1;
return output;
}
The problem is that all transparent objects are never obscured by the opaque objects drawn before hand as denoted by the yellow circle in the following picture:

The depth test has to be disabled for the transparent objects but it is enabled for the opaque.
Why is this incorrect effect occurring?
