Archived

This topic is now archived and is closed to further replies.

Dovyman

Improving 2D Graphics Quality

Recommended Posts

Ok I've created a simple (basically just a blitter at this point) sprite engine using the "textured quad" technique in DX8. This boosted framerates in comparison to the old sprite engine I was using, however the image quality seems to have degraded significantly. I'm not exactly sure what to do to improve the quality, so any help would be great. For brevity, I have included the code to prepare DX for blitting, Initialize a sprite, create a surface from file, create a texture from the surface, and blit the texture. (the essentials to bring a sprite from file to screen) //-------------------------------------------------------------------------------------------- // Initializes the sprite from file //-------------------------------------------------------------------------------------------- void Sprite::Init(char *filename, int s_width, int s_height, int numsprites, D3DCOLOR colorkey) { //init variables SPRITE_WIDTH = s_width; SPRITE_HEIGHT = s_height; NUM_SPRITES = numsprites; for (int index = 0; index < NUM_SPRITES; index++) { g_lpTextures[index] = NULL; } // Initialise the textures. LPDIRECT3DSURFACE8 lpSurface; //setup source rect accounting for rectangle bug RECT srcRect = { 0, 0, SPRITE_WIDTH - 1, SPRITE_HEIGHT - 1 }; //create surface from file and then textures from the surface //CreateSurfaceFromFile(&lpSurface, filename, colorkey); //CreateSurfaceFromFile(&bg_surf, filename, colorkey); LoadBitmapToSurface( filename, &lpSurface, g_pDevice ); for (index = 0; index < NUM_SPRITES; index++) { CreateTextureFromSurface(lpSurface, &srcRect, &g_lpTextures[index]); srcRect.left += SPRITE_WIDTH; srcRect.right += SPRITE_WIDTH; } lpSurface->Release(); } //----------------------------------------------------------------------------- // Creates a surface from the specified graphics file. // // ppSurface - The created surface object. // szFileName - The bitmap file name. // colourKey - The transparent colour. Opaque black by default. //----------------------------------------------------------------------------- void CreateSurfaceFromFile(LPDIRECT3DSURFACE8* ppSurface, char szFileName[], D3DCOLOR colourKey = 0xFF000000) { const D3DFORMAT SURFACE_FORMAT = D3DFMT_A1R5G5B5; LPDIRECT3DSURFACE8 pSurface; D3DXIMAGE_INFO srcInfo; // Optional PALETTEENTRY palette[256]; // Optional // A quick hack to get the size of the image into srcInfo. DXTEST( g_pDevice->CreateImageSurface(1, 1, SURFACE_FORMAT, &pSurface) ); DXTEST( D3DXLoadSurfaceFromFile(pSurface, NULL, NULL, szFileName, NULL, D3DX_FILTER_NONE, 0, &srcInfo) ); pSurface->Release(); // Create a surface to hold the entire file DXTEST( g_pDevice->CreateImageSurface(srcInfo.Width, srcInfo.Height, SURFACE_FORMAT, ppSurface) ); pSurface = *ppSurface; // The default colour key is 0xFF000000 (opaque black). Magenta // (0xFFFF00FF) is another common colour used for transparency. DXTEST( D3DXLoadSurfaceFromFile(pSurface, palette, NULL, szFileName, NULL, D3DX_FILTER_NONE, colourKey, &srcInfo) ); } //----------------------------------------------------------------------------- // Creates a texture from a region of the surface. // // pSurface - the source surface. // pSrcRect - portion of the source surface to create the texture from. // ppTexture - the created texture object. //----------------------------------------------------------------------------- void CreateTextureFromSurface(LPDIRECT3DSURFACE8 pSurface, RECT* pSrcRect, LPDIRECT3DTEXTURE8* ppTexture) { int width = pSrcRect->right - pSrcRect->left; // + 1; int height = pSrcRect->bottom - pSrcRect->top; // + 1; D3DSURFACE_DESC surfDesc; DXTEST( pSurface->GetDesc(&surfDesc) ); DXTEST( D3DXCreateTexture(g_pDevice, width, height, 1, 0, surfDesc.Format, D3DPOOL_DEFAULT, ppTexture) ); // Retrieve the surface image of the texture. LPDIRECT3DSURFACE8 pTexSurface; LPDIRECT3DTEXTURE8 pTexture = *ppTexture; DXTEST( pTexture->GetLevelDesc(0, &surfDesc) ); DXTEST( pTexture->GetSurfaceLevel(0, &pTexSurface) ); // Create a clean surface to clear the texture with. LPDIRECT3DSURFACE8 pCleanSurface; D3DLOCKED_RECT lockRect; DXTEST( g_pDevice->CreateImageSurface( surfDesc.Width, surfDesc.Height, surfDesc.Format, &pCleanSurface) ); DXTEST( pCleanSurface->LockRect(&lockRect, NULL, 0) ); memset((BYTE*)lockRect.pBits, 0, surfDesc.Height * lockRect.Pitch); DXTEST( pCleanSurface->UnlockRect() ); DXTEST( g_pDevice->CopyRects(pCleanSurface, NULL, 0, pTexSurface, NULL) ); pCleanSurface->Release(); // Copy the image to the texture. POINT destPoint = { 0, 0 }; DXTEST( g_pDevice->CopyRects(pSurface, pSrcRect, 1, pTexSurface, &destPoint) ); pTexSurface->Release(); /* // The following srcRect should clear the texture. RECT srcRect = { 0, 0, surfDesc.Width - 1, surfDesc.Height - 1 }; // But the right and bottom edges are not clean. // Instead you have to use the following srcRect. RECT srcRect = { 0, 0, surfDesc.Width, surfDesc.Height }; POINT destPoint = { 0, 0 }; g_lpD3DDevice->CopyRects(pCleanSurface, &srcRect, 1, pTexSurface, &destPoint); // Thankfully, there's an easier way: use NULL for srcRect and // destPoint. See above. */ } //----------------------------------------------------------------------------- // Draws a textured quad (sprite). // // pTexture - The source texture used for the sprite. // pDest - Draw sprite at these screen coordinates. // pCenter - Centre of scaling and rotation, relative to pDest. // pScaling - Scaling vector. If NULL, it is treated as identity. // angle - Angle of rotation in radians. // colour - The RGB and alpha channels are modulated by this value. Use // 0xFFFFFFFF for a standard blit. //----------------------------------------------------------------------------- void Blit(LPDIRECT3DTEXTURE8 pTexture, POINT* pDest, D3DXVECTOR2* pCentre, D3DXVECTOR2* pScaling = NULL, float angle = 0, D3DCOLOR colour = 0xFFFFFFFF) { D3DSURFACE_DESC surfDesc; DXTEST( pTexture->GetLevelDesc(0, &surfDesc) ); float left = (float)pDest->x; float top = (float)pDest->y; float right = left + surfDesc.Width; // - 1; float bottom = top + surfDesc.Height; // - 1; const float z = 1.0f, rhw = 1.0f; D3DTLVERTEX vertices[4] = { // x, y, z, rhw, colour, tu, tv { left, top, z, rhw, colour, 0, 0 }, { right, top, z, rhw, colour, 1, 0 }, { right, bottom, z, rhw, colour, 1, 1 }, { left, bottom, z, rhw, colour, 0, 1 } }; D3DXVECTOR2 centre, scaling; centre.x = (float)pDest->x + pCentre->x; centre.y = (float)pDest->y + pCentre->y; pCentre = ¢re; // Don't want to modify the argument passed in. if (pScaling == NULL) // Use identity: no scaling performed. { scaling.x = scaling.y = 1; pScaling = &scaling; } TransformVertices(vertices, pCentre, pScaling, angle); // Draw the sprite DXTEST( g_pDevice->SetTexture(0, pTexture) ); DXTEST( g_pDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, vertices, sizeof(D3DTLVERTEX)) ); } //----------------------------------------------------------------------------- // Sets up Direct3D for blitting. //----------------------------------------------------------------------------- void InitBlit() { // Turn off culling, so we see the front and back of primitives DXTEST( g_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE) ); // Enable alpha blended transparency. DXTEST( g_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE) ); DXTEST( g_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA) ); DXTEST( g_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA) ); // Allow modulation of the texture's and diffuse colour's alpha. // By default, the texture and diffuse colour's RGB are modulated. // This lets us create transparency and tinting effects by setting // the (diffuse) colour in Blit(). DXTEST( g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE) ); DXTEST( g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE) ); DXTEST( g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE) ); DXTEST( g_pDevice->SetVertexShader(D3DFVF_TLVERTEX) ); } [edited by - Dovyman on October 17, 2002 10:08:18 PM]

Share this post


Link to post
Share on other sites
When you draw the sprite, you''re gonna want to change your code to:

float left = (float)pDest->x-0.5f;
float top = (float)pDest->y-0.5f;
float right = left + surfDesc.Width-0.5f; // - 1;
float bottom = top + surfDesc.Height-0.5f; // - 1;


The -0.5f is because the center of a texel drawn to the screen is actually at the topleft of the pixel. So we need to shift the entire quad up and to the left by half a pixel.

Hopefully that will help

Brett

Share this post


Link to post
Share on other sites
Could you define ''quality'' a bit better? I''m guessing you have bilinear filtering on when you render and that will smudge and blur a previously clean 2D image.

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]

Share this post


Link to post
Share on other sites
In response to Brettidos comment: I made the changes you talked about, it made not visual difference but hopefully that should help the sprites to line up properly?

On the quality issue, it can be solved by applying triangluar filtering with dithering on (D3DX_DEFAULT) in the final surface creation in CreateSurfaceFromFile, but im wondering why filtering is taking place at all, shouldn't the image retain its orginal size and thus not need to be filtered? or is something wrong my math?

[edited by - Dovyman on October 18, 2002 3:48:36 PM]

Share this post


Link to post
Share on other sites
A part of the problem could be that you seem to define a RECT with inclusive-inclusive coordinates. That is, the width of the rectangle is (right - left + 1) However DirectX and the rest of Windows does not agree, because for DirectX the width of a RECT is (right - left). I see that you have noticed this yourself with, for example, CopyRects.

For example the line
RECT srcRect = { 0, 0, SPRITE_WIDTH - 1, SPRITE_HEIGHT - 1 };
seems wrong, because it will create a texture with a width of one less than SPRITE_WIDTH. To create a texture with the width SPRITE_WIDTH you need to write:
RECT srcRect = { 0, 0, SPRITE_WIDTH, SPRITE_HEIGHT};

All above of course applies to the hight of a rectangle, as well. Summary: In Windows the width of a rectangle is (right - left) and the height is (bottom - top).

Share this post


Link to post
Share on other sites