Tuesday, July 29, 2008

Light Pre-Pass - First Blood :-)

I was looking for a simple way to deal with different specular values coming from different materials. It seems that one of the most obvious ways is the most efficient way to deal with this. If you are used to start with a math equation first -as I do- it is not easy to see this solution.
To recap: what ends up in the four channels of the light buffer for a point light is the following:

Diffuse.r * N.L * Att | Diffuse.g * N.L * Att | Diffuse.b * N.L * Att | N.H^n * N.L * Att

So n represents the shininess value of the light source. My original idea to apply now different specular values in the forward rendering pass later was to divide by N.L * Att like this:

(N.H^n * N.L * Att) \ (N.L * Att)

This way I would have re-constructed the N.H^n term and I could easily do something like this:


where mn represents the material specular. Unfortunately this requires to store the N.L * Att term in a separate render target channel. The more obvious way to deal with it is to just do this:

(N.H^n * N.L * Att)^mn

... maybe not quite right but it looks good enough for what I would want to achieve.


Damian said...

I did mention this in the original post - but I still don't understand how this handles overlapping lights.

If k = (N.H^n * N.L * Att) from one light

one light on a pixel:

k1^mn - hacky but OK

two lights on a pixel:
(k1 + k2)^mn != k1^mn + k2^mn

three lights on a pixel:
(k1 + k2 + k3)^mn != k1^mn + k2^mn + k3^mn

Of course it may look OK in practice, but without seeing it I have my doubts. (Light overlap areas may look weird?)

The only way I can think of having a per surface shininess value is to perhaps output it into the alpha of the normal in the pre-pass.

Wolfgang Engel said...

Hi Damian,
there are two different shininess values. One shinines value describes the light source shininess and the other the material shininess. A material reflects each light source with the same shininess value because its reflection properties are always the same. This is different from the shininess of each light source that is different per light.
The only way I can think of having a per surface shininess value is to perhaps output it into the alpha of the normal in the pre-pass.
This would be a serious restriction ... one of the main disadvantages of a deferred renderer.

Wolfgang Engel said...

Let me experiment a bit more with what I have and I give you feedback how well it works out. In case this is not working out I can always claim that I made a mistake and go back and put the specular power value in the normal map render target :-)

Damian said...

Light sources have shininess values? I have never seen that before.

My point was that exponential operation is not distributive over addition. (a+b)^n != (a^n + b^n)

Wolfgang Engel said...

Oh I thought the ^n term is also called shininess value .. so ok let's just talk about the exponential operation you mention.

You could argue that the way it is handled by most games is wrong. This exponential operation is a property of the light source and the material and not just a property of one of them.
Let me look at it in the game for a while ... I get back to you regarding this.

Phil said...

One could argue that, but the position of the light and the material have by far more of an impact on the shape of the specular highlight than the spectrum of the light does, so I think the criticism is still quite valid.

I believe this is also why the guys at Naughty Dog write out the specular power when they generate the geometry (position/normal) information. And it is also why they output two 3-component light values from their light shaders - a diffuse and a specular texture.

This still allows them the nice middle ground between a fully deferred renderer and a forward renderer.