Basis textures with alpha appearing fully black and white on some iPads and older mobile devices when RGB_PVRTC_4BPPV1_Format is used

I’ve outlined the issue I’m encountering here, but thought it might be an issue on the three.js side of things instead of the basis texture so I’m posting here as well.

This question GLTFLoader with basisLoader renders only alpha map on iOS on this forum might be the issue I’m encountering but the reference images provided are no longer live so I’m not sure.

Thanks in advance for any insight

Hey dude, I think you need to change the min and mag filter type to THREE.linearFilter, the number code is 1006 if I remember, you can also set this in your .gltf code if you search in there for min filter you will find a min and mag value, one of those values is the right one that needs to be pasted into both min and mag filter…

1 Like

Looks like its the material issue, can you show me the material code?

Thanks for the quick responses guys @forerunrun @playbyan1453 . Actually, the texture’s min and mag filter were already set to code 1006, which is THREE.LinearFilter, by default.

Here’s the code that’s loading the basis texture and applying it. The mesh is imported with a MeshBasicMaterial who’s map is a 4x4 white png so I’m just switching out that image with the basis texture.

(texture) => {

    texture.encoding = THREE.sRGBEncoding; = texture;

    if (node.material.userData.basisSrc.includes('alpha')) {
        texture.magFilter = texture.minFilter = THREE.LinearFilter; // just added this in reply to forerunrun but no success
        node.material.transparent = true;

    node.material.needsUpdate = true;




More intel. The texture format is different on each device I’ve tried (see below). Texture is looking correct on laptop and iPhone but not iPad.

// laptop = THREE.RGBA_S3TC_DXT5_Format = 33779;
// iPhone = THREE.RGBA_ASTC_4x4_Format = 37808;
// iPad = THREE.RGB_PVRTC_4BPPV1_Format  = 35840;

I’ve tried changing the format to the RGBA version of PVRTC but that’s not working either.

More updates from the creator of basis here.

He brought up the same point I presented in my previous post:

The only thing that looks iffy is the texture format being used for PVRTC1:
iPad = THREE.RGB_PVRTC_4BPPV1_Format = 35840;

His suggestions to me:

For an alpha texture like this, it may be better to use 4444 or 32-bit instead of PVRTC1 (or perhaps a larger texture).

I’m not very experienced with three.js, so you may want to ask on one of their developer forums. The basis transcoder can supply 4444, 565, or 32-bit texture data (along with all the other GPU tex formats). But I’m unsure if three.js supports it.

Also, you may just want to see if the three.js folks have already fixed the problem, because it appears to be that they aren’t using a RGBA format for PVRTC1.

Still no solution though. Anyone know how to leverage 4444 or 32-bit format? Or have any other ideas?

One other note that seems weird to me, which you’ll find in the thread on basis github:

Ya I noticed that the iPad format was RGB, which would make sense as to why no alpha is appearing. The weird part is it appears to be rendering the alpha channel of the texture instead of the RGB channels, since it’s showing white on the tree, black on fully transparent ground, and grey on the shadow.

Now I’m thinking potential solutions could be updating three.js past v111 or implementing a KTX2Loader. Will report back if I do indeed test those solutions

I would suggest trying to use KTX2Loader to embed .ktx2 textures in the model, yeah. If that isn’t working I can look into it further, but putting .basis files in glTF models is not really officially supported and I don’t think I can investigate that. The Basis compression technology is the same either way, it’s just a different “container” format around that data.

1 Like

@donmccurdy I’m going to attempt to fix this issue using ktx2 since the three.js example with ktx2 and alpha works on my iPad that is breaking with the regular basis texture with alpha.

A couple of questions for you though as I’m sorta confused about ktx2.

  1. I’d like to have the ktx2 texture separate from the glb model and apply it programmatically since that’s the flow I already have with basis textures. I see that’s occurring in the example three.js/webgl_loader_texture_ktx2.html at cf04fca253477f40e04488229cfcaf0f280e448b · mrdoob/three.js · GitHub. But how do I convert to the ktx2 texture either from basis or png?

  2. I’m using three.js v111 and would like to stay with this version for the time being since I’m at the end of this project’s dev cycle. Can I still use the KTX2Loader? I see v111 only has the KTXLoader. Can I add the KTX2 loader code into the npm package in three/examples/jsm and expect it to still work?

Download an installer for KTX-Software from the “Assets” dropdown here: Releases · KhronosGroup/KTX-Software · GitHub. This will install a toktx CLI tool that can convert PNG or JPEG images to .ktx2 files. Converting .basis to anything else would require some custom code.

Can I still use the KTX2Loader [in v111]? I see v111 only has the KTXLoader. Can I add the KTX2 loader code into the npm package in three/examples/jsm and expect it to still work?

I’m not sure… my guess would be that it won’t work correctly because KTX2Loader relies on some more recent changes to three.js, including WebGLExtensions: Add .has( name ) method by donmccurdy · Pull Request #19756 · mrdoob/three.js · GitHub and CompressedTexture: Support ETC2/EAC by donmccurdy · Pull Request #18581 · mrdoob/three.js · GitHub. You might be able to modify KTX2Loader locally to not need those changes.

1 Like

Thanks for such a quick response! I appreciate the help a ton! Hopefully I don’t have anymore questions :crossed_fingers:

@donmccurdy okay so I converted my png file to ktx2 using toktx but the file is 5x larger than the png file. What am I missing here?? Would it be better to just use the png?

I would need to see the textures and the conversion settings used to say why it might be larger, but see my answer below:

If you’re using the UASTC option from toktx, be sure to also enable ZSTD compression.

1 Like

aha, the --uastc or --zcmp options made the .ktx2 file about the same size as the png! Looking at your answer, I never knew that basis wouldn’t always result in smaller size - they’ve always given a reduction of anywhere from 50-90% reduction of my textures so I was surprised to see the file bigger.

Anyways, the solution I have settled on is falling back to png textures on the few devices where this black and white basis issue is occurring and that’ll have to suffice for now. If I get around to implementing ktx2 textures I’ll update this thread with the solution.

Again, thanks so much for your guidance. You’re the man.

1 Like

Circling back around on this as a new but similar issue has come up.

I have ktx2 textures with alpha embedded in a glb and they work great on every device + browser I’ve tried except for Safari on MacOS. The alpha channel is ignored and the texture appears as black and white. It’s because the basis texture’s format becomes RGB_ETC1_Format on Safari + MacOS. To reiterate, safari on my iPhone works fine and Chrome and Firefox work fine on the same Mac I’m having the problem on, because the basis texture’s format becomes something with RGBA.

I wanted to make sure this wasn’t an issue with my project or potentially the toktx version I’m using so I went to the ktx2 example on on safari and the transparency is showing up black there too.

Any ideas on solutions to this problem? My current plan is to check the format of the loaded compressed textures and if it doesn’t contain an alpha channel then I’ll load in a png version of the texture.

Hm, that’s interesting… Could you print the value of kxt2Loader.workerConfig after detectSupport() has been called, on your macOS device? This depends on more than just the browser and OS, but on my macOS device (M1 Pro), I get —

Chrome and Firefox:

astcSupported: false
bptcSupported: false
dxtSupported: true
etc1Supported: false
etc2Supported: false
pvrtcSupported: false


astcSupported: false
bptcSupported: false
dxtSupported: true
etc1Supported: true
etc2Supported: false
pvrtcSupported: false

Currently the transcoder prefers ETC1 over DXT, but maybe that shouldn’t be the case for textures with alpha channels. This could be worth filing a bug for.

I’d also thought that Safari + ANGLE was supposed to support more texture formats on M1 devices, but I’m not seeing it at the moment.


1 Like

My safari output is the same as yours:

astcSupported: false
bptcSupported: false
dxtSupported: true
etc1Supported: true
etc2Supported: false
pvrtcSupported: false

My output is the same as yours on Chrome and Firefox as well.

Is the issue happening on your device on the ktx2 example on three.js?

In regards to the ETC1 being prioritized over DXT, that does seem like the problem for these transparent textures. Is there a way I could edit the transcoder to use DXT instead of ETC1?

Yeah, same issue here (Safari only).

Is there a way I could edit the transcoder to use DXT instead of ETC1?

I think this would work:

loader.detectSuppor( renderer );
loader.workerConfig.etc1Supported = false;

I’d like to figure out what is happening with Safari’s support for ETC2 and other formats, but in the meantime we should probably patch this in KTX2Loader yeah.

1 Like

Okay that worked! I followed your directions and set ktx2Loader.basisLoader.workerConfig.etc1Supported = false.

Now the texture format is RGBA_S3TC_DXT5_Format , which supports the transparency.

Do you think this patch is okay for production? Would be ideal if KTX2Loader would check if the texture has alpha and fallback to an RGBA format instead of RGB. Let me know if you’d like me to file an issue or something on three.js github.

I patched the basis/ktx2 loader by adding if ( opt.if === 'etc1Supported' && hasAlpha ) continue; to getTranscoderFormat() and then I don’t have to fiddle with the workerConfig. Let me know if you think is the best solution @donmccurdy