• Create Account

Banner advertising on our site currently available from just \$5!

# layers theory

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

3 replies to this topic

### #1Farfadet  Members   -  Reputation: 175

Like
0Likes
Like

Posted 01 November 2010 - 07:34 PM

I've been messing up some time with rendering a mesh with multiple see-through textures, until I dived into the theory. This is what I came with :

1) rendering multiple layers

We can define the visible colour Vi of layer i, as the colour that would be seen if layers 0 to i were visible, and the remaining layers were invisible. Vi is obtained by computing, from bottom (layer 0) to top (layer i), the successive values of Vi with the formula :

Vi = Ci. αi + Vi-1.(1 - αi)

with Ci the colour stored in layer i, and αi the alpha value stored in layer i.

This is the classical blending formula, but the destination colour is not the colour of the underlying layer, it is its visible colour. The lowest layer is the background, its alpha is by definition 1, so V0 = C0.

This is easy to implement in a pixel shader: we need just to compute the successive values of Vi, from layer 0 upwards, possibly skipping a hidden layer. This is well known stuff.

2) merging layers

Sometimes we want to merge two layers together, or better said replace two textures with their blended colours. This is the case, for example, when we paint with a textured brush on one layer. At first sight, all we have to do is use the classical blending formula (αdest, 1-αdest). Doing this we get a "halo" around the brush (where the alphas are somewhere between 0 and 1). The reason is that we ignore the source alpha, and we shouln't.

Let us see how to correctly merge two successive layers together. What we want is to replace the two layers with one, and get the same visual result than when they are layered. With the two layers, say layers 3 and 4, the visible colour as defined above is given by:

V3 = C3. α3 + V2.(1-α3)
V4 = C4. α4 + V3.(1-α4)

and we want to replace this by layer 3’ (merge of layers 3 and 4) :

V’3 = C’3. α’3 + V2.(1-α’3)

so that V’3 = V4

Therefore we can write the identity :

C’3. α’3 + V2.(1-α’3) = C4. α4 + V3.(1-α4)
= C4. α4 + (C3. α3 + V2.(1-α3)).(1-α4)
= C4. α4 + C3. α3. (1-α4) + V2.(1-α3).(1-α4)

Since V2 is an invariant, this can only be met if two conditions are satisfied :

C’3. α’3 = C4. α4 + C3. α3. (1-α4)
1-α’3 = (1-α3).(1-α4)

α is the inverse of the opacity : the transparency, so the merged layer’s transparency is the product of the two layer’s transparencies, or :

α’3 = 1- (1-α3).(1-α4)

And the merged color :

C’3 = (C4. α4 + C3. α3. (1-α4)) / α’3

So far so good. I'd welcome comments on this by more experienced people.

My problem is to implement this formula in glsl. I cannot use the existing openGL blending formulas, so I need to do it with GLSL. The problem is that I need to write the result of the merge in texture 3 corresponding to layer 3, but I also need a lookup in texture 3. Can I do simultaneous read and write on the same texture ? I guess not. Another solution would be to write in a temporary texture, and replace texture 3 by this texture when the blending is done. Considering my brush application, I need this to be quick (of course). Is there another option ? What's the best way to do this ? Any help/advice welcome.

### #2Krohm  Crossbones+   -  Reputation: 3619

Like
0Likes
Like

Posted 01 November 2010 - 11:04 PM

Quote:
 Original post by FarfadetAnother solution would be to write in a temporary texture, and replace texture 3 by this texture when the blending is done. Considering my brush application, I need this to be quick (of course). Is there another option ? What's the best way to do this ?
Just Render-texture the layers. You get full access to full HLSL programmability and blending options. It could eventually be on the same performance grade. Additionally, if you have "layer groups" then you can cache the whole group. Recall that HLSL has at least 16 texture image units so if you somehow manage to put the layers in a coherent "image space" you could merge them all in a single pass. I bet this could even be faster than using the blend unit because of the less recurring state changes.

### #3Mathucub  Members   -  Reputation: 160

Like
0Likes
Like

Posted 02 November 2010 - 08:28 AM

Quote:
Original post by Krohm
Quote:
 Original post by FarfadetAnother solution would be to write in a temporary texture, and replace texture 3 by this texture when the blending is done. Considering my brush application, I need this to be quick (of course). Is there another option ? What's the best way to do this ?
Just Render-texture the layers. You get full access to full HLSL programmability and blending options. It could eventually be on the same performance grade. Additionally, if you have "layer groups" then you can cache the whole group. Recall that HLSL has at least 16 texture image units so if you somehow manage to put the layers in a coherent "image space" you could merge them all in a single pass. I bet this could even be faster than using the blend unit because of the less recurring state changes.

I think your equation addressed this; however, just quoting from experience you must be really careful with rendering to textures and then trying to blend that resulting texture over another something else.

When doing "normal" blending, one expects the data to come in "unshaped".
Say you are reading a pixel that is red but 50% transparent from a png with transparency. It will come in as a float4 = {1.0f, 0.0f, 0.0f, 0.5f}

Say for example, you are blending over black [such as when using a render texture]; When you do your Alpha/Inv. Src alpha standard blend--the RT pixel will be written as {0.5f, 0.0f, 0.0f, 0.5f} since the blending equation triggered a multiply.

This multiply is called shaping [or matting, depending on what school of graphics your from]. This is fine for drawing on screen--but can reek havoc when working in any context where you have to blend again. [Layers or when handing signals down to a downstream switcher for TV broadcast]

Now in your next step--you need to reread your render target texture. If you go to that same pixel: he will now have the {0.5f, 0.0f, 0.0f, 0.5f} value--this is wrong for blending. The color channel has been polluted by the alpha.

Whenever you read back from a render texture, if you were doing Alpha/Inv. Src; you need to divide the color channels by the alpha so that you can do the blend properly:

{0.5f/0.5f, 0.0f/0.5f, 0.0f/0.5f, 0.5f} = {1.0f, 0.0f, 0.0f, 0.5f}

This will give the result so that the next Alpha/Inv. Src stage works correctly.

If you don't do this: you graphics will always fade to black since they will have
gotten multiplied by their alpha twice.

Remember: graphics go to dark = double shaping/matting
graphics go to white = double unshaping/unmatting

Hope that helps.

### #4Farfadet  Members   -  Reputation: 175

Like
0Likes
Like

Posted 08 November 2010 - 07:46 PM

First, sorry it took me so long to answer.

Well, I'm using openGL and GLSL, and I render to texture. Putting aside the render to texture technology (I guess there's not much difference btw HLSL and GLSL), I still have a problem with the standard blending formula Cs*aS + Cd (1 - ad).

Consider the case :
Cs = 1 0 0 0.5
Cd = 0 0 1 0
i.e. the source is partly transparent, and the destination is fully transparent at that pixel.
The standard formula gives :
C = 0.5 0 0.5 0.25 (a = as*as + ad*(1-as) = 0.5*0.5 + 0*(1-0.5) = 0.25)

The resulting alpha is already strange. I can go around this by using :
glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ONE). This will add up the alphas, which makes sense for a brush adding paint to a layer. But there is another problem : the resulting color I get is a dark purple because of the blue contribution. But there should be NO blue contribution, since the destination layer is fully transparent. My conclusion : standard blending is valid only for a fully opaque back layer. It assumes an alpha of 1 in the destination layer. Proof of that is that the destination alpha doesn't appear at all in the formula. Dividing by alpha will still not give me a good result : the blue will always be there.

My merging formula is alright to merge two layers without changing the visible result, but I'm still not sure it's OK for painting with a brush with an alpha component.

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS