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

Saving and loading two-channel normal maps (ASTC) #346

Open
UltraEngine opened this issue Mar 16, 2023 · 1 comment
Open

Saving and loading two-channel normal maps (ASTC) #346

UltraEngine opened this issue Mar 16, 2023 · 1 comment

Comments

@UltraEngine
Copy link

UltraEngine commented Mar 16, 2023

I'm trying to implement platform-independent compressed normal maps that use Z reconstruction in the shader.

It looks like this is the correct way to save a two-channel image:

m_comp_params.m_swizzle[0] = 0;
m_comp_params.m_swizzle[1] = 0;
m_comp_params.m_swizzle[2] = 0;
m_comp_params.m_swizzle[3] = 1;

I am working on the assumption that a two-channel image should always be transcoded into BC5 format (or its equivalent on mobile). Is there a way to detect either the swizzle states or number of channels when the image is loaded? I can't seem to find anything like this in the basisu_image_info or basisu_file_info structures. If we had this information it would be very easy to select a transcode format:

Channels PC format
1 BC4
2 BC5
3, 4, unknown BC7

I know the KTX2 SDK provides this information, but I prefer to use Basis and DDS for all textures. It is also possible to store hints in the userdata values, but this feels like a hack and I would like something more versatile that will work with any basis file.

@thokra1
Copy link

thokra1 commented Mar 24, 2023

Not sure about how BasisU handles this, but when doing normal map compression with BasisU via libKTX, then the correct swizzle for UASTC compressed normal maps would be

ktxBasisParams basis{};
basis.structSize = sizeof(ktxBasisParams);
basis.inputSwizzle[0] = 'r';
basis.inputSwizzle[1] = 'r';
basis.inputSwizzle[2] = 'r';
basis.inputSwizzle[3] = 'g';

Having swizzles of 0 and 1 would, at least in OpenGL terms, mean that you don't take the actual R and G values, but replace them with 0 in rgb and 1 in a when sampling in a shader.

Nowadays, the only options one needs to really consider is BCn on desktop platforms (Windows, Linux, OSX, ...) and ASTC on mobile and embedded platforms and if for some reason that doesn't work, you have multiple legacy formats to fallback to (PVRTC1/2 and ETC2/EAC on iOS/Android, S3TC on desktops). Let's assume, we only target BCn and ASTC.

When sampling the texture in a shader, the necessary swizzle depends on the platform your running on. When transcoding to ASTC on the target platform, you need to sample with an .ra swizzle, when transcoding to BC5, you'd sample with an .rg swizzle.

To be able to use a single swizzle like .rg everywhere, you'd need to set the appropriate swizzle on the texture, i.e. if you wanted to always use an .rg swizzle in the shader, you'd need to set the appropriate texture swizzle when transcoding to ASTC. However, this is not possible when using WebGL (because one of the main backends is Direct3D, which doesn't support texture swizzles), so the choices u need to make depend on the actual backend and platform.

When not having native texture swizzle support, the only options you have is to either

  • do something based on info stored in a uniform[-buffer], like "0 == take Y from G, 1 == take Y from A" depending on the format or
  • to generate appropriate shader permutations that do the right thing.

This gets even more complicated when supporting both compressed and uncompressed normal maps, which you sometimes want, especially with 8-bit normal maps. Assuming the uncompressed data is also stored as a two-channel RG image, then on an ASTC platform, you'd have two cases to consider, because both sampling from .ra and from .rg would be necessary. In addition to the above mentioned options, you'd then have a third option for the uncompressed normal maps: on ASTC platforms, you'd simply blow up the RG texture into and RGBA texture, swizzled to RRRG again, so you can simply use the static permutations doing .ra swizzling just like for ASTC compressed textures.

I guess going to UASTC->ASTC and UASTC->BC7 and uncompressed RRRG textures would be a viable solution, but when I investigated this avenue, I found some more or less heavy quality issues with UASTC->BC7 transcoding for normal maps.

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