RGB10 Lightmaps

Post tutorials on how to do certain tasks within game or engine code here.
Post Reply
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

RGB10 Lightmaps

Post by mh »

Here we're going to improve lightmap quality. By default lightmaps will use 8 bits per colour component, but we can do better than that. Using a 32-bit texture we can go up to 10 bits per colour component, meaning that overbright lighting can retain the same precision as regular GLQuake lighting but have a much higher dynamic range (up to 4 times GLQuake's) whereas non-overbright lighting can get 3 extra bits of precision.

This assumes that you already know your way around the relevant parts of the code. Please don't just copy and paste or you risk making a mess and breaking things.

There is a dependency on OpenGL 1.2 or the extensions GL_EXT_texture and GL_EXT_packed_pixels being available. I reckon most prople should be on OpenGL 1.2 or higher so there is no need to check for the extensions if your GL_VERSION meets the requirements. This runs fine on all current Intel, NVIDIA and ATI graphics hardware.

GL_EXT_texture
GL_EXT_packed_pixels
OpenGL 1.2 Specification

Code is loosely based around Fitz 0.85 but it should be obvious what needs to be done with other engines. Like I said: loosely. This ain't copy 'n' paste stuff. ;)

First of all we need to change our texture specification calls to match the new format; these now become:

Code: Select all

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB10_A2, glt->width, glt->height, 0, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV, data);

glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, BLOCK_HEIGHT, GL_BGRA,
				 GL_UNSIGNED_INT_2_10_10_10_REV, lightmaps + i * BLOCK_WIDTH * BLOCK_HEIGHT * LIGHTMAP_BYTES);
And then we need to change how we build the lightmap (in R_BuildLightmap):

Code: Select all

unsigned *rgb10a2 = (unsigned *) dest;
int shift;

if (gl_overbright.value)
	shift = 7;
else shift = 5;

for (i = 0; i < tmax; i++)
{
	for (j = 0; j < smax; j++, bl += 3)
	{
		rgb10a2[j] = 0xC0000000;

		t = bl[0] >> shift; rgb10a2[j] |= ((t > 1023) ? 1023 : t) << 20;
		t = bl[1] >> shift; rgb10a2[j] |= ((t > 1023) ? 1023 : t) << 10;
		t = bl[2] >> shift; rgb10a2[j] |= ((t > 1023) ? 1023 : t);
	}

	rgb10a2 += BLOCK_WIDTH;
}
And the defines needed:

Code: Select all

#define GL_RGB10_A2 0x8059
#define GL_BGRA 0x80E1
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
Some final notes:
  • I'm using GL_RGB10_A2 instead of just GL_RGB10 here. GL_RGB10_A2 is marginally faster because the driver doesn't have to fill in the alpha channel for you before submitting the data to OpenGL. I've supplied a default alpha in case we ever need to do alpha blending with a lightmap texture (otherwise the surface may not be visible!)
  • I'm using a GL_BGRA format too; these are also marginally faster on some platforms. For format GL_RGBA just switch the << 20 to bl[2].
  • The code as supplied requires a 4x GL_MODULATE/GL_RGB_SCALE combine blend; you can switch to 2x and shift down by 6 instead.
  • Lightmap bytes now needs to be 4; I've replaced Fitz's int variable with a #define for the purposes of this example.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
Post Reply