BinomialLLC / basis_universal

Basis Universal GPU Texture Codec
Apache License 2.0
2.72k stars 267 forks source link

Difference in RGB channels when compressing normal map with UASTC #248

Open thokra1 opened 3 years ago

thokra1 commented 3 years ago

We're currently integrating BasisU into our asset pipeline and renderer and it has been a great experience so far! While checking the quality of some normal maps of varying complexity, I noticed that all maps contain, to a certain degree, differences between the R and the GB-channels. As far as I can tell, the G- and B-channels are always identical, but the R-channel sometime differs considerably. The following shot from PVRTexTool highlights this:

image

Cmd line: basisu.exe -ktx2 -ktx2_no_zstandard -uastc -uastc_level 0 .\warning_normal.png -swizzle rrrg -normal_map -output_file warning_uastc.ktx2

Please note that this becomes more noticeable with reduced UASTC quality levels. Still, even with -uastc_level 4 the result show some of these differences:

image

For comparison and reference, I also compressed with the latest version of astcenc.

image

Cmd line: astcenc-sse4.1.exe -cl .\warning_normal.png .\warning_astc_4x4.ktx 4x4 -thorough -normal

It doesn't seem to be influences by SSE, threading or swizzling, as this also happens with pre-swizzled images. It also never happens with ETC1S!

From what I can tell, the R-channel is closest to what astcenc produces across all tested source images, so I assume the unintentional deviation happens in the GB-channels. Depending on the swizzle used when sampling the textures in a shader, the result might or might not negatively impact shading, but it still seems to be a bug in the UASTC compressor.

richgel999 commented 3 years ago

Hi, Which format are your transcoding UASTC to? UASTC->ASTC is lossless, but UASTC->anything else (even BC7) does incur some loss.

Can you upload a test normal map (the original as .PNG) here, so we can look at exactly what you're seeing? The UASTC compressor tries to balance the error that will be introduced into UASTC->ASTC and UASTC->BC7, and it's possible you are seeing this in action. I strongly suspect this isn't an error per se, but an artifact due to how the compressor weights different encodings.

thokra1 commented 3 years ago

Hi Rich,

I have attached an archive with a particularly strange case: grate.zip

I did the comparison directly in PVRTexTool, which transcodes UASTC to ASTC and then decompresses to RGBA for display. I checked this by comparing the transcoded result in the tool with unpacked ASTC RGBA output from the CLI.

What's curious in this case, is that transcoding to BC5 yields the best overall shading. I compared images from our renderer to UE4 as a reference, and the apparent loss in quality is very minor. Here's the diff for BC5:

diff_rg_bc5

Transcodig to ASTC shows "streaks" of pixels that noticeably interfere with shading, but overall it's still okay:

diff_rrrg_astc

Now, transcoding to BC7 is the real deal. This introduces some hefty atifacts which makes is basically unusable. This introduces more streaks and a lot of blocky artifacts on top:

diff_rrrg_bc7

For now, we'll go with BC5 (if supported) since we cannot estimate how much error is introduced by transcoding to BC7. While BC5 maybe slightly inferior in many cases, as it stands, it may also be vastly superior in others. On mobile devices, ASTC is our go-to format.

The overall diff of all transcodings highlights this pretty well:

diff_overall

You can also see, that the channel differences are much, much more pronounced in the BC7 transcoding.

Encoding was done with basisu.exe -ktx2 -ktx2_no_zstandard -uastc -uastc_level 3 -swizzle rrrg -normal_map grate.png and all decoding has been done with CLI as well. Source images in RRRG and RG (gray/alpha) formats have been created with GIMP.

richgel999 commented 3 years ago

UASTC->ASTC is lossless. It's the highest quality UASTC can give you. UASTC->BC5 should be very low loss (relative to ASTC) because there are two independent channels for the encoder to utilize. UASTC->BC7 will involve some loss, but it's very low.

I'll try encoding your texture and see what happens. It's possible a few tweaks of the parameters or the error distance function (which is currently generic for RGBA content) could make a big difference.

thokra1 commented 3 years ago

Awesome, thank you!

richgel999 commented 2 years ago

I'm leaving this up for further investigation.