¿Cómo funciona AlphaBlend Blendstate en XNA 4 al acumular luz en un RenderTarget?

Estoy usando un motor de representación diferida del tutorial de Catalin Zima:

Su sombreador de iluminación devuelve el color de la luz en los canales rgb y el componente especular en el canal alfa. Aquí es cómo se acumula la luz:

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 indica que el campo AlphaBlend de la class BlendState usa la siguiente fórmula para alphablending:

(fuente × Blend.SourceAlpha) + (destino × Blend.InvSourceAlpha),

donde "fuente" es el color del píxel que devuelve el sombreador y "destino" es el color del píxel en el rendertarget.

Mi pregunta es: ¿por qué mis colors se acumulan correctamente en el rendertarget de Luz incluso cuando los alphas de los nuevos píxeles son iguales a cero? Como un rápido control de cordura, ejecuté el siguiente código en el sombreador de píxeles de la luz:

 float specularLight = 0; float4 light4 = attenuation * lightIntensity * float4(diffuseLight.rgb,specularLight); if (light4.a == 0) light4 = 0; return light4; 

Esto evita que la iluminación se acumule y, posteriormente, se dibuje en la pantalla. Pero cuando hago lo siguiente:

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

La luz se acumula y dibuja exactamente donde debe estar. ¿Qué me estoy perdiendo? De acuerdo con la fórmula anterior:

(origen x 0) + (destino x 1) debe ser igual a destino, por lo que el rendertarget "LightRT" no debe cambiar cuando dibuje esferas de luz en él.

Parece que la GPU está utilizando la mezcla de aditivos en su lugar:

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

Ok, me di count de esto, no era obvio.

La página MSDN de reference de la que hablé afirma que "Blend type" "Alpha Blending" utiliza la siguiente fórmula en XNA 4:

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

Pero a pesar de esto, el campo BlendState.AlphaBlend tiene los siguientes parameters preestablecidos:

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

Entonces, la fórmula correcta que usa el comstackdor es la siguiente:

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

Y esta es la razón por la que mis colors se sumn correctamente incluso cuando SourceAlpha == 0. Creo que la página de MSDN se ha actualizado incorrectamente después de cambiar a alfa premultiplicada en XNA 4.0:

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

El BlendState.NonPremultiplied que existe en la biblioteca hace exactamente lo que sugiere la primera fórmula.

Sin embargo, el resultado de la iluminación en BlendState.AlphaBlend es incorrecto para mi renderizador diferido, ya que cada vez que un nuevo píxel aparece con alfa (especular) alto, anula por completo todos los colors en la textura de destino.

Mezcla aditiva, a pesar de la lógica humana y a pesar de su explicación tiene los siguientes campos:

"Un object de estado incorporado con configuraciones para mezcla aditiva que agrega los datos de destino a los datos de origen sin usar alfa".

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

Y esta es la razón por la cual bajo este BlendState solo se escribieron en mi borrado los píxeles donde los alfas (especular) eran más que cero (0,0,0,0) LightRT.

Entonces, para que el Renderizador diferido de Catalin funcione correctamente, necesito un BlendState personalizado que tenga los siguientes parameters:

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

En ese caso de uso, debería ser (y lo es, supongo) utilizando mezcla aditiva, no transparencia alfa. Este modo realiza "fuente = fuente + destino"