PDA

View Full Version : Translucency and multi-pass lighting



christl
11-26-2006, 02:43 PM
Hi!

I want to render some meshes with translucency and multiple lighting passes. I compute the contribution of each light with a relatively big GLSL shader, and blend it with what already exists in the framebuffer. For fully opaque meshes, this requires rendering a black pass first, then blending with GL_ONE,GL_ONE. It works, and quite well.

But when rendering translucent meshes, there is a problem. For each such mesh, I first render it with color writes disabled and alpha writes enabled, to get the desired alpha value into the framebuffer. Then I enable blending with GL_DST_ALPHA,GL_ONE_MINUS_DST_ALPHA, enable color writes, disable alpha writes, and render the mesh N more times for N lights.

Let C_n be the color computed by lighting pass N, let C_b be the color in the framebuffer before blending, and let A be the destination alpha value. For one light, the final color is

C_1*A+C_b*(1-A)

This is correct. But for two lights, it is

C_2*A+(C_1*A+C_b*(1-A))*(1-A)

which is not correct, because this blends C_1 with the factor (1-A)*A, not A, and it blends C_b with (1-A)^2, not (1-A). It should be

(C_1+C_2)*A+C_b*(1-A)

instead. The two lighting passes must be summed up BEFORE blending them with the framebuffer. I also looked at other blend modes, but so far I couldn't find any combination that fits.

And here lies the problem: I can't sum them up in the framebuffer like I do with opaque meshes, because colors in the framebuffer need to be preserved for blending. I see that current games have translucent polygons and they are correctly lighted (and shadowed), so there must be a way to solve this. But how?

If anybody knows how to do this, and would care to enlighten me, I would be really, really thankful!

Komat
11-26-2006, 03:04 PM
The key is to use different blending mode for the first pass than for the additional passes. For example when you have four lights you wish to calculate following equation:

((C_4+C_3+C_2+C_1)*A)+(C_b*(1-A))

That equation can be rewritten as:

C_4*A + C_3*A + C_2*A + (C_1*A + C_b*(1-A))

So what you need to do is:
Draw light 1 with: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
Draw rest of lights with: GL_SRC_ALPHA, GL_ONE

Of course there is still problem if the mesh is concave and one visible triangle overlaps different visible triangle. In that case this will not work correctly even if your mesh has triangles sorted in such way that they render in correct order.

christl
11-26-2006, 03:26 PM
Arrgh! Forget my blabbering. Just ONE minute after I disconnected from the internet, I suddenly saw it. The solution.

I wanted to have this:

(C_1+C_2)*A+C_b*(1-A)

This is equivalent to

C_1*A+C_2*A+C_b*(1-A)

Writing it a bit different:

C_1*A + C_2*A+C_b*(1-A)

I realized that the part after the first + is nothing else than a blend of C_2 and C_B with the blend mode GL_DST_ALPHA,GL_ONE_MINUS_DST_ALPHA. Adding C_1*A to this term is also easy. If C_2*A+C_b*(1-A) is abbreviated X,

C_1*A + X*1

is just a blend of C_1 and X with the blend mode GL_DST_ALPHA,GL_ONE. Repeat for more lights. I implemented it, tested it, and it works flawlessly.

Now, the thanks have to go to myself for this time :-)

ebray99
11-27-2006, 06:03 AM
Out of curiosity, are you doing A:


for each light
for each objector B:


for each object
for each lightIt seems to me that while it would blend the top object, the object behind it wouldn't get shaded in subsequent passes if you're doing option A. This is the problem I had hit and have since resorted to a "psuedo-sortless" technique.

Kevin B

christl
11-28-2006, 01:07 PM
I'm doing B. I dropped considering A really soon after it ocured to me that it would not only be incorrect but also slower: material state changes are more expensive than light state changes, I would not want them in the inner loop. Also, building a list of lights per objects is easier (=faster) in my renderer than the reverse.

ebray99
11-28-2006, 01:28 PM
I'm curious why you think A is incorrect? I'm guessing you misunderstood what I said? To be clear, I'm referring to how you treat all objects, not just translucent ones.

Kevin B

christl
11-28-2006, 01:31 PM
Well, you said that behind objects won't get shaded. This is what I would call incorrect.

Vexator
11-29-2006, 10:44 AM
i'm having the same problem but i'm not sure what exactly i have to do now. this is how my renderer works:

- pass 0 renders depth and ambient light
- glBlendFunc( GL_ONE, GL_ONE )
- passes 1 to x render all geometry for each light source
- glDisable( GL_BLEND )

what exactly do i have to do now? do i need to enable blending for pass 0 as well, which modes do i need for passes 1 to x?

thanks in advance..

christl
11-29-2006, 02:08 PM
Vexator, do you mean for opaque or translucent materials? For the opaque case what you say should be sufficient. For the translucent case, I render the geometry with glColorMask(0,0,0,1) in pass 0, so that the right alpha is written into framebuffer but the color is not touched. This obviously requires a framebuffer with an RGBA pixelformat. Then I set the blend mode to GL_DST_ALPHA,GL_ONE_MINUS_DST_ALPHA for the first lighting pass, and to GL_DST_ALPHA,GL_ONE for the remaining lighting passes.

Vexator
11-30-2006, 07:48 AM
i tried what you suggested, and the desired mesh IS drawn translucent now - but all that can be seen through it is the clear color.. not the geometry behind :p

i set the clear color to grey and took a screenshot: www.vexator.net/blending_prob.jpg (http://www.vexator.net/blending_prob.jpg)

(the muzzle flash is mesh with the alpha mask.) thanks!

ebray99
11-30-2006, 10:07 AM
This looks like a classic case of bad sorting. It looks like you're rendering your transparent objects first. Thus, when the objects behind them go to render, the fragments are being z-tested out and discarded.

Kevin B

Vexator
11-30-2006, 10:40 AM
i've checked that already, the muzzle flash is last in the queue. :p