dds Alpha channel not working on mesh

Started by
16 comments, last by Evil Steve 12 years, 2 months ago
Does anyone have any idea why my alphas are getting darker the further away the mesh is from the camera?
Advertisement
Finally I have traced the problem down to

SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);


Turning this off makes everything fine. But I want to know what it messed it up. Why would a DXT3 dds manually loaded mess this up?


Here is the loading code, I tried with software vertex processing also but nothing good came out of it:

D3DLOCKED_RECT rc;
if( *(DWORD*)(pTextureData) == 'DXT3' ){
hr = pGraphicsDevice->GetDevice()->CreateTexture(width, height, NULL, NULL, D3DFMT_DXT3, D3DPOOL_MANAGED, &pTex, NULL);
if( hr!=D3D_OK ) return NULL;
hr = pTex->LockRect(0,&rc,NULL,0);
if(hr==D3D_OK){
CopyMemory(rc.pBits,pTextureData+4,(width/4) * (height/4) * 16 );
pTex->UnlockRect(0);
}
pTex->GenerateMipSubLevels(); //on or off makes no diff
return pTex;
}
The 3rd parameter to CreateTexture() is the number of mip levels to generate, with 0 meaning "All, down to 1x1". NULL evaluates to 0, so you're generating a texture with a full mip chain. I'm not sure what the texture contains when it's created, but I'd assume that it's either undefined or all 0 bytes. If you have 0 bytes, then you'll end up with the alpha channel being set to 0 in the lower mips. If you generate the lower mips with a call to GenerateMipSubLevels, then you may end up with the texture getting blurred and the alpha channel messed up - especially if the texture has a lot of alpha in it.

In either case, you ned up with a texture that has a lot of alpha in the lower mips, and the lower mips are displayed when the texture is further away. Setting the mip filter to linear means the two closest mip levels are linearly interpolated to give the output colour, and not setting it means it stays at point (I.e. no mip filtering).
I can only assume that your particular setup makes the linear interpolation go screwy somehow.

What happens if you use D3DXSaveTextureToFile, save as .dds and then use the DirectX texture tool to look at the lower mip levels?

Also, slightly off topic, your code won't work correctly if the texture is not a multiple of 4 in width and height. It should be:
CopyMemory(rc.pBits, pTextureData+4, ((width+3)/4) * ((height+3)/4) * 16);
I suspect GenerateMipSubLevels() is failing - check the return code. You probably won't be able to get it to work either because I don't know of any cards which support that function for DXT compressed textures as that would require them to decompress and then recompress the texture. It's intended for use on render targets.

I'm guessing you probably want to load the mipmaps from the texture file. The simplest option is to create a .dds file with the texture tool that comes with the SDK and load that with D3DXCreateTextureFromFile().
Thanks a lot for the comments. I think Evil Steve is right, the mipmap alphas are getting messed up by the filter. But without the filter are the mipmaps even made? Does the pTex->UnlockRect(0); generate them? Is the filter just making them "worse" afterwards? Maybe pTex->GenerateMipSubLevels(); is called in UnLockRect().. who knows :P

All I know is that there are no mipmap levels saved in the file beforehand, and the Game which I take the assets from do support Mipmaps, so the should be post generated, but how....

Maybe I need to decompress the DXT3, generate mipmaps and then compress it back to DXT3 or just leave it uncompressed... What do you think?
Using D3DXSaveTextureToFile I can now see that all the mipmaps are all solid black in the DirectX Texture Tool when viewing the alpha channel and they remain solid black with any mipmap filter applied, even D3DTEXF_NONE. Also, the color channels are clear, no mipmaps are actually generated.... Just a blank space. pTex->GenerateMipSubLevels(); Does nothing!!


also CopyMemory(rc.pBits, pTextureData+4, ((width+3)/4) * ((height+3)/4) * 16); breaks my app, my code works for this...
Hmm, still have no clue:

if(FAILED(d3d->CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
D3DUSAGE_AUTOGENMIPMAP,
D3DRTYPE_TEXTURE,
D3DFMT_DXT3)))
{
MessageBox(NULL, L"Can't D3DUSAGE_AUTOGENMIPMAP", L"Error", MB_OK);
PostQuitMessage(0);
}


This does not fail atleast. But D3DUSAGE_AUTOGENMIPMAP does not do anything anyway....
As Adam_42 said, now I think about it, I wouldn't expect GenerateMipSubLevels() to work for DXT compressed textures, since it would require uncompressing the DXT texture (Which is easy), resizing it (Which is also easy), and then re-compressing it (Which is hard - that's why art packages that save DXT compressed textures often have various settings like perceptual and so on to decide how it compresses).
This is also why there's no cards I know of that can use DXT compressed formats as render target formats.

You'd be better off making the mip levels yourself manually, and loading each mip level into the texture instead of getting D3D / the driver to try and do it itself. Or, pass 1 for the 3rd parameter to CreateTexture to create a texture with a single mip level, and don't bother calling GenerateMipSubLevels().

This topic is closed to new replies.

Advertisement