Monday, September 19, 2011

Notes on Quake Lighting

OK, I tried something with that extra 8 bits and it didn't work out.

Here's how it goes. I don't like the fact that overbright lighting loses 1 bit of precision compared to GLQuake's flattened lighting. I want to get that bit back. Now, it may be the case that it's only one bit, but the difference is there, it's quite subtle, and I can set up a test case that proves it.

In the past I've experimented with other methods (and rejected them for various reasons - lack of widespread hardware support, too slow, too awkward to work with, etc), but today it was using those extra 8 bits to store a "scaling factor". The theory goes something like this. If the blocklights value is less than 32768 bitshift by 7 and give it a scaling factor of 1, if it's less than 65536 bitshift by 8 and give it a scaling factor of 2, and so on. You can even go all the way down the scale to the other end too. In practice because we're storing the scaling factor in a single byte it's more convenient to make the baseline something like 64 (which will translate to roughly 0.25 in the driver) and then multiple it by 4.

So, in the shader when we're reading back the lightmap we have the scaling factor stored in the alpha channel, so all we need to do is multiply scale by RGB and - in theory - out comes the lightmap at the correct scale. (Like I said, we multiply it by an extra 4 too as we're using a baseline of 0.25ish rather than 1.)

That's where the test case that proves the difference lies. What actually happens is that you get some pretty awful banding on lightmaps where they transition between one scaling factor and another. It's very noticeable and very abrupt.

Aside from that it worked beautifully - it was quite breathtaking to see the full dynamic range of a rocket explosion going off.

An alternative that's probably worth pursuing is to use the extra byte as an additive factor instead. The way this would work is - if all of the colour components are above 255 (before clamping) subtract 255 from them and store 255 in the alpha channel. Then recomposit the lightmap in the shader. This could actually go one further again by storing a lower base value in the alpha channel and multiplying it before adding to RGB. Worth experimeting? I think it probably is.

No comments: