Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Poor normal map compression quality #99

Closed
slimbuck opened this issue Jan 3, 2020 · 7 comments
Closed

Poor normal map compression quality #99

slimbuck opened this issue Jan 3, 2020 · 7 comments

Comments

@slimbuck
Copy link

slimbuck commented Jan 3, 2020

Hello!

We are busy integrating basis into the Playcanvas asset pipeline and find the resulting quality of tangent space normal maps particularly poor, so poor as to make them unusable. We understand there are superior approaches to normal map compression in general (as explained in the basis README), however we have many existing assets that would benefit from basis compression. Please see an example side-by-side comparison below. It appears basis nearly filters out the diagonals running top left to bottom right:

comp

The DXT image was created using PVRTexTool.
The basis image was created using flags: -y_flip -mipmap -comp_level 5 -normal_map

Full versions of the test normal maps

Original:
original
Basis compressed:
basis
DXT:
dxt

Is this drop in quality expected under basis? Is there anything we can do to improve quality?

Thanks!

@richgel999
Copy link
Contributor

Hi - Looking at your command line, you aren't using the -q option to increase the quality level. (-comp_level doesn't increase quality much, and is only intended for video compression.) So definitely try "-q 255". The default quality level is -q 128. You can also boost quality even higher than -q 255 - see below.

Basis Universal doesn't handle 3D normal maps encoded into RGB well because the internal encoding is ETC1S (a subset of ETC1). However, if you use the "-seperate_rg_to_color_alpha" command line option, that'll put X into RGB and Y into Alpha, then you can recover Z in your pixel shader using z=sqrt(1-xx-yy). Quality with this encoding is much higher for tangent space normal maps.

We are working on a new much higher quality universal format due out within a couple months.

Also there's this info in the docs:"

"For high quality tangent space normal maps, here's one suggested solution that should work well today:
Compress with the -normal_map flag, which disables a lot of stuff that has interfered with normal maps in the past. Also compress with -comp_level 2-4, which creates the highest quality codebooks. Use larger codebooks (use the -max_endpoints and -max_selectors options directly, with larger values).

Start with 2 component normalized XY tangent space normal maps (where XY range from [-1,1]) and encode them into two 8-bit channels (where XY is packed into [0,255]). Now put X in color, and Y in alpha, and compress that 32-bit PNG using basisu. The command line tool and encoder class support the option "-seperate_rg_to_color_alpha" that swizzles 2 component RG normal maps to RRRG before compression, aiding this process."

@richgel999
Copy link
Contributor

I compressed your normal map using "-seperate_rg_to_color_alpha", and the quality looks pretty good. (It could be even higher if I used -max_selectors 16128 and -max_endpoints 16128 instead of -q 255.) We're getting 38.8 dB on RGB and 39.1 dB on Alpha. I've included the unpacked X and Y images below.

basisu -file c:\dev\nm.png -seperate_rg_to_color_alpha -normal_map -q 255 -stats

Slice: 0
.basis ETC1S 709 Luma: Max: 51 Mean: 1.937 RMS: 2.941 PSNR: 38.761 dB
.basis ETC1S 601 Luma: Max: 51 Mean: 1.937 RMS: 2.941 PSNR: 38.761 dB
.basis ETC1S RGB Avg: Max: 51 Mean: 1.937 RMS: 2.941 PSNR: 38.761 dB
.basis BC1 709 Luma: Max: 50 Mean: 2.011 RMS: 3.033 PSNR: 38.494 dB
.basis BC1 601 Luma: Max: 50 Mean: 2.011 RMS: 3.033 PSNR: 38.494 dB
.basis BC1 RGB Avg: Max: 50 Mean: 2.099 RMS: 3.126 PSNR: 38.231 dB
Unquantized ETC1S 709 Luma: Max: 41 Mean: 1.510 RMS: 2.254 PSNR: 41.071 dB
Unquantized ETC1S 601 Luma: Max: 41 Mean: 1.510 RMS: 2.254 PSNR: 41.071 dB
Unquantized ETC1S RGB Avg: Max: 41 Mean: 1.510 RMS: 2.254 PSNR: 41.071 dB
Slice: 1
.basis ETC1S 709 Luma: Max: 65 Mean: 1.812 RMS: 2.833 PSNR: 39.084 dB
.basis ETC1S 601 Luma: Max: 65 Mean: 1.812 RMS: 2.833 PSNR: 39.084 dB
.basis ETC1S RGB Avg: Max: 65 Mean: 1.812 RMS: 2.833 PSNR: 39.084 dB
.basis BC1 709 Luma: Max: 63 Mean: 1.863 RMS: 2.900 PSNR: 38.881 dB
.basis BC1 601 Luma: Max: 63 Mean: 1.863 RMS: 2.900 PSNR: 38.881 dB
.basis BC1 RGB Avg: Max: 63 Mean: 1.960 RMS: 3.001 PSNR: 38.585 dB
Unquantized ETC1S 709 Luma: Max: 41 Mean: 1.418 RMS: 2.166 PSNR: 41.417 dB
Unquantized ETC1S 601 Luma: Max: 41 Mean: 1.418 RMS: 2.166 PSNR: 41.417 dB
Unquantized ETC1S RGB Avg: Max: 41 Mean: 1.418 RMS: 2.166 PSNR: 41.417 dB

Color ETC2 (X):
nm_unpacked_rgb_ETC1_RGB_0000

Alpha ETC2 (Y):
nm_unpacked_a_ETC2_RGBA_0000

@richgel999
Copy link
Contributor

As quality with 3D XYZ normal maps encoded into RGB textures is a known issue (it was an explicit tradeoff we decided to make), this is not a bug. The system has really strong support for XXXY normal maps.

@richgel999
Copy link
Contributor

Note that the transcoder has full support for XXXY encoded normal maps. You can transcode them to two ETC1 textures, two PVRTC1 textures, or a single ETC2, BC3, BC5, ASTC, BC7, PVRTC2, etc. texture. (You could try a single PVRTC1, but I doubt it would look acceptable.) So these normal maps should work well on all devices, if you're willing to do the legwork.

@slimbuck
Copy link
Author

slimbuck commented Jan 4, 2020

Hi Rich,

Thank you for the info and for clarifying the -q option. Also, I look forward to trying out your new universal format when it's ready!

Storing a normal map in two textures (and so requiring two texture reads) on devices that only support etc1 (i.e. quite often older slower devices) is bit of a performance and resource double whammy. It also complicates the engine and shader paths. It may be better in this case to transcode to an uncompressed rgb565.

I would like to add that we are very excited to get basis working everywhere for everything in our pipeline, because it simplifies significant parts of our tooling and asset delivery and packaging.

Thanks again!

@slimbuck
Copy link
Author

slimbuck commented Apr 9, 2020

Hi @richgel999 ,

We swizzle normal maps to GGGR and reconstruct Z in shader as is normal/suggested. However we have found up to now that the quality of the GGG channel on iOS is so poor we transcode to RGBA8 instead of PVRTC. (The alpha R channel is fine).

We were hoping uastc would improve on this, but from a quick test on one of our difficult cases I don't see substantial improvement. In fact the resulting GGG data for both etc1 and uastc are very similar.

Am I right to conclude that in general switching to uastc won't improve quality of PVRTC?

Thanks!

@slimbuck
Copy link
Author

slimbuck commented Apr 9, 2020

Sorry and one more thing, am I right in saying that basis etc1 internal format will have higher quality than uastc when transcoded to etc1?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants