2D blending not work with DXUT

Started by
6 comments, last by Nik02 11 years, 11 months ago
Hi All,
I have built a simple library which worked perfectly for 2D sprites. However when I ported this over to DXUT I am getting strange blending problems. At first everything seemed fine as the alpha channels for the sprites were transparent against the background, but when the sprites crossed each other the bounding box of the sprite became visible. See file attached.

I have created the simplest piece of code I can to demonstrate how I am doing things. I have based this on EmptyProject10.cpp that is comes with the DirectX SDK:

..\MicrosoftDirectXSDKJune2010\Samples\C++\Direct3D10\EmptyProject10

within this there are only two callback functions that I use OnD3D10ResizedSwapChain() and OnD3D10FrameRender(). All the code and the variables are below.

Any help would be greatly appreciated as I have been stuck on this for a long time.

Edit: I have also just realised that the colours are washed out when I am using DXUT. I have read somewhere that this is related to DXUT using DXGI_FORMAT_R8G8B8A8_UNORM_SRGB. However I have already spent ages tinkering with either trying to get DXUT to use DXGI_FORMAT_R8G8B8A8_UNORM or to get my textures to be DXGI_FORMAT_R8G8B8A8_UNORM_SRGB.


#define NUM_POOL_SPRITES 32
D3DX10_SPRITE spritePool[NUM_POOL_SPRITES];
ID3DX10Sprite *pSpriteObject = NULL;
ID3D10ShaderResourceView *gSpriteTextureRV = NULL;
#define MAX_SPRITES 4
// Sprite structure
typedef struct
{
// sprite details
float width;
float height;
// sprite position
float posX;
float posY;
// sprite movement
float moveX;
float moveY;
BOOL visible;
}GameSprite2;
GameSprite2 sprites[MAX_SPRITES] = {0};
// the number of active sprites
int numActiveSprites = 0;



HRESULT CALLBACK GameApplication::OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
// create and set the viewport
D3D10_VIEWPORT viewPort;
viewPort.Width = DXUTGetWindowHeight();
viewPort.Height = DXUTGetWindowWidth();
viewPort.MinDepth = 0.0f;
viewPort.MaxDepth = 1.0f;
viewPort.TopLeftX = 0;
viewPort.TopLeftY = 0;
DXUTGetD3D10Device()->RSSetViewports(1, &viewPort);
// Create the default projection matrix
D3DXMatrixOrthoOffCenterLH(&matProjection,
(float)viewPort.TopLeftX,
(float)viewPort.Width,
(float)viewPort.TopLeftY,
(float)viewPort.Height,
0.1f,
10);


ID3D10Texture2D* texture = NULL;
ID3D10Resource* pRes = NULL;
//HRESULT hr = D3DX10CreateTextureFromFile(DXUTGetD3D10Device(), TEXT("./brick.bmp"), NULL, NULL, &pRes, NULL);
HRESULT hr = D3DX10CreateTextureFromFile(DXUTGetD3D10Device(), TEXT("./bitmaps/spheremapsmall.png"), NULL, NULL, &pRes, NULL);
testHr("OnD3D10ResizedSwapChain:D3DX10CreateTextureFromFile",hr);

// Translates the ID3D10Resource object into a ID3D10Texture2D object
pRes->QueryInterface(__uuidof( ID3D10Texture2D ), (LPVOID*)&texture);
pRes->Release();
if (texture == NULL)
{
CLogger::WriteInfoLog("failed to create texture");
return false;
}
// Get the texture details
D3D10_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
// Create a shader resource view of the texture
D3D10_SHADER_RESOURCE_VIEW_DESC SRVDesc;
ZeroMemory(&SRVDesc, sizeof(SRVDesc));
SRVDesc.Format = desc.Format;
SRVDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = desc.MipLevels;
hr=DXUTGetD3D10Device()->CreateShaderResourceView(texture, &SRVDesc, &gSpriteTextureRV);
testHr("OnD3D10ResizedSwapChain:CreateShaderResourceView",hr);
// release the texture
texture->Release();
// Create the Sprite Batch, all sprite drawing is done through
// this object
hr = D3DX10CreateSprite(DXUTGetD3D10Device(), 0, &pSpriteObject);
testHr("OnD3D10ResizedSwapChain:D3DX10CreateSprite",hr);

// Initialize the blend state for alpha drawing
D3D10_BLEND_DESC StateDesc;
ZeroMemory(&StateDesc, sizeof(D3D10_BLEND_DESC));
StateDesc.AlphaToCoverageEnable = FALSE;
StateDesc.BlendEnable[0] = TRUE;
StateDesc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
StateDesc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
StateDesc.BlendOp = D3D10_BLEND_OP_ADD;
StateDesc.SrcBlendAlpha = D3D10_BLEND_ZERO;
StateDesc.DestBlendAlpha = D3D10_BLEND_ZERO;
StateDesc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
StateDesc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
hr=DXUTGetD3D10Device()->CreateBlendState(&StateDesc, &pBlendState10);
testHr("OnD3D10ResizedSwapChain:CreateBlendState",hr);
// Loop through and set the defaults for the
// sprites in the pool
for (int i = 0; i < NUM_POOL_SPRITES; i++)
{
// Texture for this sprite to use
spritePool.pTexture = gSpriteTextureRV;
spritePool.TextureIndex = 0;
// top-left location in U,V coords
spritePool.TexCoord.x = 0;
spritePool.TexCoord.y = 0;
// Determine the texture size in U,V coords
spritePool.TexSize.x = 1;
spritePool.TexSize.y = 1;
spritePool.ColorModulate = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
}
// Create a diagonal line of overlapping sprites
for (int curSprite = 0; curSprite < MAX_SPRITES-1; curSprite++)
{
// Set the width and height of the sprite
sprites[curSprite].width = 64.0f;
sprites[curSprite].height = 64.0f;
// position in a diagonal so you can see overlap
sprites[curSprite].posX = 300+(curSprite*15);
sprites[curSprite].posY = 100+(curSprite*15);

sprites[curSprite].moveX = 0;
sprites[curSprite].moveY = 0;
// This sprite is visible
sprites[curSprite].visible = TRUE;
}

//create a single sprite on its own
sprites[MAX_SPRITES-1].width = 64.0f;
sprites[MAX_SPRITES-1].height = 64.0f;
sprites[MAX_SPRITES-1].posX = 100;
sprites[MAX_SPRITES-1].posY = 100;
sprites[MAX_SPRITES-1].visible = TRUE;

D3DXMATRIX matScaling;
D3DXMATRIX matTranslation;
int curPoolIndex = 0;
// Loop through the sprites
for (int i = 0; i < MAX_SPRITES; i++)
{
// only update visible sprites
if (sprites.visible)
{
CLogger::WriteInfoLog("OnFrameMove: moving sprites");
// set the proper scale for the sprite
D3DXMatrixScaling(&matScaling, sprites.width, sprites.height, 1.0f);
// Move the sprite to spritePosX, spritePosY
// SpriteWidth and SpriteHeight are divided by 2 to move the
// translation point to the top-left sprite corner instead of
// the center of the sprite.
D3DXMatrixTranslation(&matTranslation,
(float)sprites.posX + (sprites.width/2),
(float)(DXUTGetWindowHeight() - sprites.posY - (sprites.height/2)), 0.1f);
// Update the sprites position and scale
spritePool[curPoolIndex].matWorld = matScaling * matTranslation;
// Increment the pool index
curPoolIndex++;
}
}
// set the number of active sprites
numActiveSprites = curPoolIndex;
return S_OK;
}



void CALLBACK GameApplication::OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
float ClearColor[4] = { 0.176f, 0.196f, 0.667f, 0.0f };

pd3dDevice->ClearRenderTargetView( DXUTGetD3D10RenderTargetView(), ClearColor );
//pd3dDevice->ClearRenderTargetView( DXUTGetD3D10RenderTargetView(), D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f) );
pd3dDevice->ClearDepthStencilView( DXUTGetD3D10DepthStencilView(), D3D10_CLEAR_DEPTH, 1.0, 0 );
FLOAT OriginalBlendFactor[4];
UINT OriginalSampleMask = 0;
if (DXUTGetD3D10Device() != NULL)
{
// clear the target buffer
//pD3DDevice->ClearRenderTargetView(pRenderTargetView, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f));
if (pSpriteObject != NULL)
{

HRESULT hr = pSpriteObject->SetProjectionTransform(&matProjection);
testHr("OnD3D10FrameRender:SetProjectionTransform",hr);
// start drawing the sprites
hr=pSpriteObject->Begin(D3DX10_SPRITE_SORT_TEXTURE);
testHr("OnD3D10FrameRender:Begin",hr);
// Draw all the sprites in the pool

hr=pSpriteObject->DrawSpritesBuffered(spritePool, numActiveSprites);
testHr("OnD3D10FrameRender:DrawSpritesBuffered",hr);
// Save the current blend state
DXUTGetD3D10Device()->OMGetBlendState(&pOriginalBlendState10, OriginalBlendFactor, &OriginalSampleMask);
// Set the blend state for alpha drawing
if(pBlendState10)
{
FLOAT NewBlendFactor[4] = {0,0,0,0};
DXUTGetD3D10Device()->OMSetBlendState(pBlendState10, NewBlendFactor, 0xffffffff);
}
// Finish up and send the sprites to the hardware
hr=pSpriteObject->Flush();
testHr("OnD3D10FrameRender:Flush",hr);
hr=pSpriteObject->End();
testHr("OnD3D10FrameRender:End",hr);
}
// Restore the previous blend state
DXUTGetD3D10Device()->OMSetBlendState(pOriginalBlendState10, OriginalBlendFactor, OriginalSampleMask);
}
}
Advertisement
Alpha blended sprites need to be sorted back to front.

The problem lies in the fact that the depth buffer values for a pixel get written even though said pixel would appear to be transparent due to its alpha value.

Niko Suni

Thanks for your response. I changed the code above from


hr=pSpriteObject->Begin(D3DX10_SPRITE_SORT_TEXTURE);


to


hr=pSpriteObject->Begin(D3DX10_SPRITE_SORT_DEPTH_BACK_TO_FRONT);


However this hasn't changed the result in any way. I still get the image attached to the OP.

Is there something else I need to be doing?
If you don't care about the sprite ordering, disable depth testing (and don't sort). If you do care about the ordering, assign meaningful depth values to the sprites for the depth sort to work :)

Niko Suni

Thanks Niko for your help. I have disabled the depth and the the bounding boxes are no longer visible.

To fix the issue I have added the following code to the OnD3D10ResizedSwapChain() method:


//START NEW CODE
D3D10_DEPTH_STENCIL_DESC depthDisabledStencilDesc;
ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc));

depthDisabledStencilDesc.DepthEnable = false;
depthDisabledStencilDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
depthDisabledStencilDesc.DepthFunc = D3D10_COMPARISON_LESS;
depthDisabledStencilDesc.StencilEnable = true;
depthDisabledStencilDesc.StencilReadMask = 0xFF;
depthDisabledStencilDesc.StencilWriteMask = 0xFF;
depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR;
depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
depthDisabledStencilDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR;
depthDisabledStencilDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS;

hr = DXUTGetD3D10Device()->CreateDepthStencilState(&depthDisabledStencilDesc, &g_pDepthDisabledStencilState);
DXUTGetD3D10Device()->OMSetDepthStencilState(g_pDepthDisabledStencilState, 1);
testHr("CreateDepthStencilState",hr);
//END NEW CODE


The colours are still washed out, but this is a separate issue may raise seperately.

Cheers,
Ben.
Color washout is a separate issue. It is usually related to color space and/or gamma issues. Search for "srgb d3d" to find more info ;)

Niko Suni

Great! and the solution to the washed out colour is to create a new SRGB texture in memory (not from file), copy the non-SRGB texture (from file) into it and use that to create the texture resource. See method below. Now my balls are perfect!


ID3D10Texture2D* GameManager::convertTextureToSRGB(ID3D10Texture2D* srcTexture){
if(srcTexture==NULL) {
throw GameException(E_POINTER,"GameManager::convertTextureToSRGB: srcTexture was null");
}
D3D10_TEXTURE2D_DESC origDesc;
srcTexture->GetDesc(&origDesc);
D3D10_TEXTURE2D_DESC srgbDesc;
ZeroMemory( &srgbDesc, sizeof(srgbDesc));
srgbDesc.Width = origDesc.Width;
srgbDesc.Height = origDesc.Height;
srgbDesc.MipLevels = 1;
srgbDesc.ArraySize = 1;
srgbDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
srgbDesc.SampleDesc.Count = 1;
srgbDesc.Usage = D3D10_USAGE_DYNAMIC;
srgbDesc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
srgbDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
ID3D10Texture2D *pSRGBTexture = NULL;
HRESULT hr=DXUTGetD3D10Device()->CreateTexture2D( &srgbDesc, NULL, &pSRGBTexture );
testHr("CreateTexture2D:CreateTexture2D",hr);
//copy the original texture in the into the SRGB texture
D3D10_BOX sourceRegion;
sourceRegion.left = 0;
sourceRegion.right = origDesc.Width;
sourceRegion.top = 0;
sourceRegion.bottom = origDesc.Height;
sourceRegion.front = 0;
sourceRegion.back = 1;
DXUTGetD3D10Device()->CopySubresourceRegion(pSRGBTexture, 0, 0, 0, 0, srcTexture, 0,&sourceRegion);
return pSRGBTexture;
}
This process (in general) is usually called "color space conversion" and can be avoided if you author the textures in the destination space to begin with. Alternatively, you can specify a destination format manually (via the pLoadInfo parameter of the load function) when you load the texture.

Glad to hear you got your balls working biggrin.png

Niko Suni

This topic is closed to new replies.

Advertisement