Per pixel lighting and custom shaders

Hi!

For per pixel lighting, I’m storing the light intensity in the alpha buffer. After that, I’m multilpying the alpha buffer with my diffuse texture map and write the result to the frame buffer:

frameBuffer = alphaBuffer * (diffuseMap)

This is simple, if the diffuseMap is a single texture map. But if the diffuse map is not a single texture map but a shader like in quake3 that has several stages and thus requires several passes, one is running into problems. If you have only up to 4 stages for your shader, you could use the 4 texture units and register combiners to compose the diffuse map and multiply it with the alpha buffer. But what if you have more stages? You would need several passes but the problem is that there is no buffer like the alpha buffer where you could store temporary RGB results.

Any ideas how to do that? Could pbuffers be helpful here?

Thanks in advance
LaBasX2

If you want to end up with something like

frameBuffer = alphaBuffer * (pass1 + pass2 + pass3)

then remember that you can distribute “alphaBuffer” to each of the 3 terms inside the parenthesis. Thus it becomes:

frameBuffer = alphaBufferpass1 + alphaBufferpass2 + alphaBuffer*pass3

If this is not possible for some reason you could try drawing a full screen quad after everything else and using that pass to calculate frameBuffer = alphaBuffer * frameBuffer.

I don’t think the arithmetic works like thak lordK, you end up with multiple modulations because it’s not just pass1+pass2+pass3, might might be pass1*pass2+pass3, or various combinations. In addition to the broken arithmetic the blendfunction is busy doing the shader stuff, and isn’t available for the alpha modulation.

LabasX2, You can move your light map last and modulate destination color with source fragment (alpha or color) it will solve your problem. If that doesn’t work for you and you need to keep destination alpha as a persistent image in the framebuffer then there is another way.

Don’t modulate any of your passes, then after everythinng is drawn draw a single quad over the whole screen and modulate destination color by destination alpha, set the source term to GL_ZERO on this pass.

You might be able to improve on this for a few shader cases, but not all.

[This message has been edited by dorbie (edited 05-13-2002).]

Originally posted by dorbie:
I don’t think the arithmetic works like thak lordK, you end up with multiple modulations because it’s not just pass1+pass2+pass3, might might be pass1*pass2+pass3, or various combinations. In addition to the broken arithmetic the blendfunction is busy doing the shader stuff, and isn’t available for the alpha modulation.

Which is why I said “If this is not possible for some reason you could try drawing a full screen quad…”.

Thanks for your answers.

Hmm, I don’t know if I’m understanding the trick with the quad correctly but I doubt that it will work with multiple lights.
My lighting model looks like that:

For every light do:
frameBuffer += diffuseIntensity * diffuseMap + specular contribution

So there is no global lightmap that is generated from all lights and could be stored in the alpha buffer for the whole frame but there is one lightmap for every light source and so the alpha buffer is overwritten for every light.

Is the way I’m doing the lighting calculations wrong or am I missing something else here?

Thanks!

The single quad is just a way of multiplying the color in the framebuffer by the alpha in the framebuffer after building up your multipass shader result in the rgb.

It wasn’t clear how destination alpha was used initially for multiple lights.

There are a few tricks which might help, you can also create an additional color buffer using render to texture these days which could help.

One thing which might help is to use the rgb buffer to accumulate all light sources. Maybe also accumulate specular to alpha. Just do a pass for each light and keep adding without a clear. Finally modulate with your diffuse term and multitexture. And do a single quad pass to convert the alpha to fragment color.

There is no good way to do multiple diffuse lights and multipass shaders, for color lights, you need more color buffers (hence the render to texture suggestion). For monochrome lights there is a way, add all your lights to alpha WITHOUT clearing, for the diffuse terms only, then draw the diffuse term multipass to the framebuffer, then modulate the framebuffer by alpha using a single quad drawn over the viewport. Then you can add your specular tesms to the framebuffer for all lights.

That’s a lot of passes though.

The biggers win is if you can do your shader using multitexture in a single pass. then you can support colored lights and use destination alpha to accumulate specular in the same light pass.

[This message has been edited by dorbie (edited 05-13-2002).]

Hi,

Putting aside the specular component (the addition doesn’t bother us) we left with somthing like that:

frameBuffer += diffuseIntensity * diffuseMap

for every light. let’s say you want to calculate the shader:

diffuseMap = Pass1*Pass2 + Pass3

assume all passes are RGB . you get for one light:

frmaeBuffer = diffuseIntensity*(Pass1Pass2 + Pass3) = Pass1Pass2diffuseIntensity + Pass3diffuseIntensity

now that is possible to be calculated for one light, but impossible for multiple lights:
frameBuffer = Pass1Pass2L0_diffuseIntensity + Pass3L0_diffuseIntensity + Pass1*Pass2L1_diffuseIntensity + Pass3L1_diffuseIntensity = (Pass1Pass2 + Pass3)*(L0_diffuseIntensity +L1_diffuseIntensity)

you can’t do that because the L1_diffuseIntensity contains multiplications.

I can’t see how you can do that for multiple lights with out using a temporary RGB buffer, or another alpha buffer.

Shlomi.

[This message has been edited by Quaternion (edited 05-13-2002).]

[This message has been edited by Quaternion (edited 05-13-2002).]

Thanks for your detailed help, dorbie.
You’re ideas are really great but unfortunately there’s always a small thing in my engine that hinders me from applying them, e.g. I’ve got colored specular.

Quaternion is right, the best solution would be to have a second color buffer but I guess there’s nothing available in gl at the moment that could do the job.
So I’ll probably really have to limit the number of stages to the number of texture units, thus 4 for most modern cards and only 2 for the MX series, what will mess up some advanced effects on these MX cards…

Thanks again!
LaBasX2

Originally posted by LaBasX2:
[b]Thanks for your detailed help, dorbie.
You’re ideas are really great but unfortunately there’s always a small thing in my engine that hinders me from applying them, e.g. I’ve got colored specular.

Quaternion is right, the best solution would be to have a second color buffer but I guess there’s nothing available in gl at the moment that could do the job.
So I’ll probably really have to limit the number of stages to the number of texture units, thus 4 for most modern cards and only 2 for the MX series, what will mess up some advanced effects on these MX cards…

Thanks again!
LaBasX2[/b]

No but there is something!! And they said it. Render to texture.

say you have diffuse*(pass1+pass2).

and pass1 = t1 + t2

You can render
t1 + t2 to a texture, giving you a texture for pass1.

You do the same for pass2 and then original equation is quite simple!

Labas, I agree quaternion is right about the second color buffer. That is why I suggested it in my earlier post and advised you to implement it using render to texture.

Dorbie, I was just realizing that I have had probably all the time a wrong imagination of what you mean by rendering to a texture. I thought that you just wanted to draw a quad covering the complete texture with stage1 and blend the same quad with stage2 onto it and so on to get one texture map. But this wouldn’t allow dynamic texture coordinate generation and such things of course.

But now I’m quite sure that this isn’t what you were thinking of. Probably you suggested to render the complete geometry with the shader stages to a texture and to blend that texture over the framebuffer using a fullscreen-quad…
Is that what you are thinking of? Or do you still have another idea?

Thanks.
LaBasX2

how about drawing the ‘light intensity in the alpha buffer’ last.
ie after youve done all the passes.

though i do recommend doing lighting first (more natural)