Epic's GPUlightmass

So, this is a case where lightmap encoding doesn’t play well with texture compression.
UE has 2 encoding scheme, HQ (high quality) and LQ (low quality). Hight quality uses a variation of the popular RGBM (see Lightmap Compression in The Witness – Ignacio Castaño) encoding, RGBLogL, where R:G:B:LogL = 8:8:8:8 (BGRA8). For LQ, RGB with luminance quantized is used, which is 8:8:8. When no compression is used, the LQ encoding is actually not that ‘low quality’, see the comparison of HQ vs LQ uncompressed (please ignore the dark edges as I’m experimenting with something else):


However, when texture compression is used (BC1/3, dependent on whether there is an alpha channel, which HQ has. The RGB part is the same), we can tell the 4x4 compression tile pattern in both HQ an LQ:


LQ compression suffers more as it has less bits (I guess), yet the HQ one is not really much better. Now, the easy ‘solution’ for you is to disable lightmap compression which will result in 4x size. Or, if you can accept the quality of compressed HQ lightmaps and you are working with a programmer, try to hack HQ lightmaps on for the devices you are targeting. I don’t have experience of working with iOS so I could not help you on this part.

@Luoshuang since LQ is exclusively used on mobile, why not to compress lightmaps with ASTC ? Less artifacts and more compression. Win/Win for everyone.

Well, they do, after digging into the code I see:
Android:

	const static FName ASTCRemap[][2] =
	{
		// Default format:		ASTC format:
		{ NameDXT1,			FName(TEXT("ASTC_RGB"))		},
		{ NameDXT5,			FName(TEXT("ASTC_RGBA"))	},

iOS:

// we remap some of the defaults
static const FName FormatRemap[] =
{
	// original				ASTC
	FName(TEXT("AutoDXT")),	FName(TEXT("ASTC_RGBAuto")),
	FName(TEXT("DXT1")),	FName(TEXT("ASTC_RGB")),
	FName(TEXT("DXT5")),	FName(TEXT("ASTC_RGBA")),

So I believe this will result in some divergence between the mobile preview you see in the editor and packaged games. Try to package and see if things improve.

After my conducting comparative tests yesterday, it seems that the influence of image compression is more significant. Additionally, there is a residual issue where GPULightmass does not affect whether “Compress Lightmaps” is enabled in the world settings. Both CPULightmass and LMCPULightmass function properly.
Additionally, do we have a better Lightmaps compression algorithm? The current quality is indeed very poor.

The LQ Lightmap generated after baking uses BC1, but the cooked assets are in ASTC format.Due to issues with the compression of the original LQ assets, the cooked ASTC assets also have problems.

1 Like

I tested it, and the effect on the mobile device is the same as in the PC Mobile Previewer. I suspect that due to issues with the compression of the original LQ assets, the cooked ASTC assets also have problems.

Right now we don’t - though if your primary concern is about on disk size then Oodle handles that, yet in GPU memory size and texture fetch performance are indeed affected. As I’ve said before, maybe you can try hacking HQ lightmaps on and see if you have any luck

After a closer look into the code, I forced the lightmaps to be grayscale to inspect the luminance (Y) component.


So it’s fair to say the tile-like artifacts come from the luminance component.
Then I noticed this in the HQ encoding scheme:

float Residual = LogL * 255.0f - FMath::RoundToFloat(LogL * 255.0f) + 0.5f;
...
DestCoefficients.Coefficients[1][3] = (uint8)FMath::Clamp<int32>(FMath::RoundToInt(Residual * 255.0f), 0, 255);

This effectively means HQ lightmaps have an extra 8-bit input into the texture compressor (16 bits) in total, and since this residual is stored on a second texture (the spherical harmonics texture’s A channel), it ultimately has more bits available after texture compression. I think this is the answer to why LQ encoding looks bad with texture compression, but I don’t have any good advice. Trying to represent HDR data (lightmaps are essentially HDR) in 8 bits (before compression) and ~3 bits (after compression) is just way too hard. If you want to take a look at the best GPU supported compressed format, see BC6H Format - Win32 apps | Microsoft Learn

1 Like

Is there a way to switch that to BC7 ?

Btw, I don’t see any issues like described above in my project. Maybe it’s only noticeable on light-gray surfaces ?

Yes, it will be quite noticeable on light-colored textures. Another factor that affects this is the lightmap denisity. If it’s very hight. the impact will be reduced. And I haven’t found an option to set it to BC7 format.

In my test case, when I enabled compression, the lightmap was 9MB, but after disabling compression, it increased to 90MB. This difference is too large. It also seems that the lightmap doesn’t use Oodle when compression is off. I need to check why there’s such a significant difference.

1 Like

Yep. Non-compressed maps can be brutally huge. Gigabytes for a single home.

Once, I proposed something like an option to select/adjust the level of compresion in someway, but I don’t know if it’s even possible.

1 Like

I’m not an expert in this either.

I must say that I DO think Oodle compression does affect the size of lightmaps that are baked with compression disabled, at least it seemed that way based on our project.

That being said, I absolutely agree. I think it would be great if there was at least a lossless (or near lossless) compression option for lightmaps that saved disk space but kept the full quality.

It’s not #1 on my list though right now, there are plenty of improvements to be made with the actual baking quality itself.

Does anyone know why do I get black ceiling? UE 5.3.2
I got sunlight (directional), skylight, a few square lights in the windows/door. All static.

finally saw the issue myself:


But, it’s only with point light. Didn’t have this with rectangular light.

1 Like

I tested it, and there are still issues when using only a rectangular light source. Do you have an example that’s slightly dimmer?

I modified the source code to set the default compression format of the generated light maps to BC7, but there isn’t much difference compared to before. :rofl:

I thoroughly reviewed the UE code and identified a crucial aspect. When Lightmap compression is enabled, UE applies compression at the final stage of Lightmap Encoding using Oodle, specifically through the FTextureFormatOodle class. When compression is disabled, it uses the FTextureFormatUncompressed class. I also referred to the documentation at Oodle Compression, which indicates that the compression quality shouldn’t be this poor. There might be an issue with the Oodle compression of the Lightmap, but I haven’t pinpointed it yet. The compression method in question is FTextureFormatOodle’s CompressImageEx method. The issue is likely in this part of the code.

static bool CompressMipChain(
	const ITextureFormat* TextureFormat,
	TArray<FImage>& MipChain,
	const FTextureBuildSettings& Settings,
	const bool bImageHasAlphaChannel,
	FStringView DebugTexturePathName,
	TArray<FCompressedImage2D>& OutMips,
	uint32& OutNumMipsInTail,
	uint32& OutExtData)

However, I lack relevant experience, making it very challenging to read the code.

1 Like

I actually was messing a lot with this last night and I did get artifacts with rect lights too. However, once I got more or less proper normal map setup (I am combining baked normal map with tiling detail normal maps), artifacts went away. Could be that they became a lot less noticeable.

Btw, I don’t think I saw any artifacts when checking the scene in Lighting Only and Detailed Lighting mode.

I think these issues have to do with normal maps instead of compression. Have you tried disabling normal map contribution to baked lighting ?