# Premultiplied alpha render states question

This topic is 3870 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi all, I've got a small problem with using a .PNG sprite with premultiplied alpha. (I am using DX8/fixed function pipeline.) Previously I was using straight alpha
mD3DDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA
mD3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA
but I realize there are advantages to using premultiplied alpha; in particular it solves the color fringes/interpolation problem nicely. So I switch to using this:
mD3DDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_ONE
mD3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA
which works nicely with premultiplied source images. But, it causes a new problem: I can't fade the sprites in and out using the vertex alpha component like before. Using something like TFACTOR seems inpractical since I can't batch the quads together if they are of varying opacity the way I can now. Is there something simple I'm overlooking here? With the 'straight alpha' method I just place the desired opacity in the DIFFUSE member of each vertex and it works great; I just fill up the dynamic vertex buffer and render in one DrawPrimitive. I'd love to be able to fade my premultiplied sprites in and out on a per-quad basis the same way, but somehow the math of how to incorporate the vertex alpha into the formula is escaping me! I'm not sure if the texture stage states are relevant, but they are set up like this:
mD3DDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE
mD3DDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_MODULATE
mD3DDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE

mD3DDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE
mD3DDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE
mD3DDevice.SetTextureStageState 0, D3DTSS_ALPHAARG2, D3DTA_CURRENT

##### Share on other sites
Just setup another stage where you multiply the color by the diffuse alpha (modulate, current, diffuse|alphareplicate)

##### Share on other sites
Ok, no fair, that worked way too easily. Thanks for the extremely efficient reply! I'm not familiar with the ALPHAREPLICATE flag, I'll have to check that out. Any comments on the math, or was this such a simple question that I should have known better? :)

Another quick question: the game I'm making is for a low end casual market; I'm wondering if using the additional texture stage jeopardizes compatibility with (crappy/integrated) GPUs? Are we talking 'almost all' graphics adapters will do this no problem? Thanks for any insight.

##### Share on other sites
I'm pretty sure 2 stages works on most DX7 cards, like an ATI XPert98. Though I have no idea if they support alpha replicate. You could skip the extra stage and just premultiply it all.

The current setup is like so:

color = texture.rgb * diffuse.rgb
color = color * diffuse.a

which is the same as

color = texture.rgb * (diffuse.rgb * diffuse.a)

So, just pre-scale diffuse.rgb by diffuse.a when making your vertices, then switch back to your single stage setup again... that's probably the best plan, and all your inputs will be nicely premodulated, and it will work on ANYTHING.

##### Share on other sites
Quote:
 Original post by NamethatnobodyelsetookSo, just pre-scale diffuse.rgb by diffuse.a when making your vertices, then switch back to your single stage setup again... that's probably the best plan, and all your inputs will be nicely premodulated, and it will work on ANYTHING.

This is a very good idea, thanks. I un-did your previous suggestion and I got it almost working but still have one small problem.

My sprite class has two different 'color modes' that let me do various color effects, based on whether I want to darken (using diffuse component) or brighten (using specular component) the image. Some small code, which I have been using to get the final vertex colors:
If mlColorMode = COLORMODE_DIFFUSE Then      mlDiffuseColor = D3DColorRGBA(tempRed, tempGreen, tempBlue, msngOpacity)      mlSpecularColor = D3DColorRGBA(0, 0, 0, 0)ElseIf mlColorMode = COLORMODE_SPECULAR Then      mlDiffuseColor = D3DColorRGBA(255, 255, 255, msngOpacity)      mlSpecularColor = D3DColorRGBA(msngRed, msngGreen, msngBlue, msngOpacity)End If

The 'tempRed', 'tempGreen', and 'tempBlue' variables are now scaled by the opacity as suggested, and this first case works great.

It's the second case that's giving me trouble, where I use a specular component to 'brighten' the sprite by a given color value. (For instance, if I want a sprite to glow or flash I will use this effect.) I'm not sure how to set it up so that it only brightens the sprite (as per the alpha channel) instead of the entire quad (which is what this code here does). Obviously brightening the entire quad looks terrible and reveals the quad's edges even where alpha = 0. If I can get past this last hurdle I will be able to update the entire engine to use premultiplied alpha, but I fear this might not be possible without another texture stage...

##### Share on other sites
The more I think about it, the more impossible my request sounds, just because of the way premultiplied alpha works. I assume the difficulty here is that the RGB data has already been modulated by the alpha channel prior to render time, thus making what I want to do rather complicated.

What I don't precisely understand is how the 'specular' component (which is never even set up besides a specularEnabled = true) gets modulated by the alpha channel in my old code! Obviously the specular component is designed for 3D use but it has been working well for my sprite purposes for years. I suppose I never really understood exactly how the specular color factors into the texture blending equations. It is a valuable effect to make things flash and glow different colors without doing any real work.

Is there a simple way to add the specular component to a premultiplied sprite (but restricted by the alpha channel) that DOES use an extra texture stage, or does this not make sense at all?

##### Share on other sites
Previously, the pixel pipeline would run, giving you your diffuse, it would then automagically add specular, then blend the result. Since the old blending was based on SRCALPHA, a lower alpha would cutaway bits of the final color.
(tex * diffuse + spec) * (tex.a * diff.a)

Now alpha is applied earlier, and won't affect the specular at all.
((tex*tex.a) * (diffuse * diff.a)) + spec

The tricky part is getting the texture alpha to modulate the specular. If you try to mix the specular in the with diffuse, it will be affected by the texture color, which isn't what you want. If it's seperate, you'll need another stage... but specular is odd.

D3DRS_SPECULARENABLE does 3 things at once. It enables fixed-pipe specular calculations, enables the 2nd color interpolator, and puts in the magic specular addition after the texture stages. It's that last one that's the pain. Your second stage could mix in specular, but it would still get added in again at the end. If the color is constant, you could use TFACTOR, but as specular I'm not sure what to do.

##### Share on other sites
Quote:
 D3DRS_SPECULARENABLE does 3 things at once. It enables fixed-pipe specular calculations, enables the 2nd color interpolator, and puts in the magic specular addition after the texture stages. It's that last one that's the pain. Your second stage could mix in specular, but it would still get added in again at the end. If the color is constant, you could use TFACTOR, but as specular I'm not sure what to do.

Ok, thanks for the explanation. I figured it was something like that, but I couldn't really find the technical explanation for how specular works; your 'automagically' description is pretty much the best I've seen. TFACTOR isn't really an option since it breaks the batch. I'll just have to have an option to use regular or premultiplied alpha within the sprite, in case I need to use specular. Thanks again for working through this with me!