1
\$\begingroup\$

I am using a Deferred Rendering engine from Catalin Zima's tutorial:

His lighting shader returns the color of the light in the rgb channels and the specular component in the alpha channel. Here is how light gets accumulated:

Game.GraphicsDevice.SetRenderTarget(LightRT);

Game.GraphicsDevice.Clear(Color.Transparent);

Game.GraphicsDevice.BlendState = BlendState.AlphaBlend;

// Continuously draw 3d spheres with lighting pixel shader.
...

Game.GraphicsDevice.BlendState = BlendState.Opaque;

MSDN states that AlphaBlend field of the BlendState class uses the next formula for alphablending:

(source × Blend.SourceAlpha) + (destination × Blend.InvSourceAlpha),

where "source" is the color of the pixel returned by the shader and "destination" is the color of the pixel in the rendertarget.

My question is why do my colors are accumulated correctly in the Light rendertarget even when the new pixels' alphas equal zero? As a quick sanity check I ran the following code in the light's pixel shader:

float specularLight = 0;

float4 light4 = attenuation * lightIntensity * float4(diffuseLight.rgb,specularLight);

if (light4.a == 0)
light4 = 0;

return light4;

This prevents lighting from getting accumulated and, subsequently, drawn on the screen. But when I do the following:

float specularLight = 0;

float4 light4 = attenuation * lightIntensity * float4(diffuseLight.rgb,specularLight);

return light4;

The light is accumulated and drawn exactly where it needs to be. What am I missing? According to the formula above:

(source x 0) + (destination x 1) should equal destination, so the "LightRT" rendertarget must not change when I draw light spheres into it!

It feels like the GPU is using the Additive blend instead:

(source × Blend.One) + (destination × Blend.One)

\$\endgroup\$
2
\$\begingroup\$

Ok I figured this stuff out, was not obvious.

The reference MSDN page i talked about does state that "Blend type" "Alpha Blending" uses the following formula in XNA 4:

(source × Blend.SourceAlpha) + (destination × Blend.InvSourceAlpha)

But despite this, BlendState.AlphaBlend field has the following pre-set parameters:

ColorSourceBlend        Blend.One
AlphaSourceBlend        Blend.One
ColorDestinationBlend   Blend.InverseSourceAlpha
AlphaDestinationBlend   Blend.InverseSourceAlpha

So the correct formula the compiler uses is the following:

(source × Blend.One) + (destination × Blend.InvSourceAlpha)

And this is why my colors add up correctly even when SourceAlpha == 0. I guess the MSDN page has been incorrectly updated after they switched to premultiplied alpha in XNA 4.0:

http://blogs.msdn.com/b/shawnhar/archive/2009/11/06/premultiplied-alpha.aspx?Redirected=true

The BlendState.NonPremultiplied that exists in the library does exactly what the first formula suggests.

However, the lighting result under BlendState.AlphaBlend is incorrect for my deferred renderer as whenever any new pixel comes in with high alpha (specular) it would completely nullify all the colors in the destination texture.

Additive blend, despite human logic and despite its explanation has the following fields:

"A built-in state object with settings for additive blend that is adding the destination data to the source data without using alpha."

ColorSourceBlend        Blend.SourceAlpha
AlphaSourceBlend        Blend.SourceAlpha
ColorDestinationBlend   Blend.One
AlphaDestinationBlend   Blend.One

And this is why under this BlendState only those pixels where the alphas (specular) were more than zero were written into my cleared to (0,0,0,0) LightRT.

So in order for Catalin's Deferred Renderer to work properly I need a custom BlendState that would have the following parameters:

ColorSourceBlend        Blend.One
AlphaSourceBlend        Blend.One
ColorDestinationBlend   Blend.One
AlphaDestinationBlend   Blend.One
\$\endgroup\$
0
\$\begingroup\$

In that use case you should be (and are, I guess) using additive blending, not alpha transparency. This mode performs "source = source + destination"

\$\endgroup\$
  • \$\begingroup\$ Mick my system works, I am trying to figure out why it works. I tried using Additive blending - it does not work. The whole screen goes black. \$\endgroup\$ – cubrman Oct 30 '13 at 12:41
  • \$\begingroup\$ I have re-checked. The Additive blending makes the entire rendertarget completely black. Only the specular highlights are stored somehow (not precisely sure accurate or not). \$\endgroup\$ – cubrman Oct 30 '13 at 12:56
  • \$\begingroup\$ Look you described BlendFunction.Add, which is the correct answer for that style of deferred renderer. I suspect you are confusing blendmode with blendfunction. \$\endgroup\$ – MickLH Oct 30 '13 at 14:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.