Hey everyone!
I’m trying to compress the shadow map of my shadow casting directional light offline using KTX2 compression. Reason for this is that I’m fine with static shadows, but the scene is pretty big and one 4k shadow map is already hitting the limit in acceptable quality, while simultaneously causing me to hit VRAM limits on mobile.
To do this I’m saving the render target of my directional light into a texture and compressing it offline. Here is a comparison of the shadow maps when put on a mesh (using the fantastic new gltf optimizer from the Babylon community – Link). On the left is the original shadow map and on the right is the KTX2 compressed version (the red dots indicate pixel differences).
When loading the texture back and assigning it to the shadow map render target, the result is looking good in terms of shadow quality, except for the fact that… the shadows are just in the wrong place (see below). The pixel difference seems to be too low that it should be a precision issue. It seems more likely that the textures are flipped or something?
I tried flipping both images (pre ktx conversion) on the x-axis, y-axis and xy-axis but none produced any more sensible results. At this point I’m running a bit low on ideas. I’m getting the same result (just with different quality) when using a regular png file and ImageBitMapLoader
btw, so it doesn’t seem to be caused by the involvement of CompressedTexture
.
Would love some ideas on what the reason could be / what I’m missing
This is roughly what I’m doing in my code:
// create shadow casting directional light
const dirLight = new DirectionalLight();
// shadow settings...
// -------------------------------------------------------------------
// only once in some pre-production step we use this
// light to create a shadow map, save its render target
// to a texture and process that offline via KTX2 tools
const rt = dirLight.shadow.map;
const buffer = new Uint8Array( 4 * rt.width * rt.height );
await renderer.readRenderTargetPixelsAsync(rt, 0, 0, rt.width, rt.height, buffer);
// download texture ...
// -------------------------------------------------------------------
// at actual app runtime:
// pre-generate render target (sunLight.shadow.map) to make sure
// all settings are the same
dirLight.shadow.needsUpdate = true;
// after shadow render target has been created,
// dispose shadow map texture to free VRAM
scene.remove(dirLight);
dirLight.dispose();
// then assign the compressed texture to the still existing render target
const compressedShadowMap = await ktx2Loader.loadAsync("/static_shadows.ktx2");
dirLight.shadow.map.texture = compressedShadowMap;
// add back to scene
scene.add(dirLight);