Direct3D matrix scalling problem

Started by
12 comments, last by L. Spiro 12 years, 6 months ago
Hey guys,

I'm having some kind of issue with scaling my sprites, if they are scaled by for example value of 1.5f there are some spaces between them in rendered game image, first of all let me show you my drawing engine functions:



void D3D9_engine::BeginScene(){
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
g_pd3dDevice->BeginScene();
gSprite->Begin(D3DXSPRITE_ALPHABLEND);
}

void D3D9_engine::SetScaleLevel(float scale){
scaleLevel = scale;

D3DXMatrixScaling(&matrix, scale, scale, 0.0f);
gSprite->SetTransform(&matrix);
}

float D3D9_engine::GetScaleLevel(){
return scaleLevel;
}

void D3D9_engine::PresentScene(){
gSprite->End();
g_pd3dDevice->EndScene();
g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

void D3D9_engine::DrawSprite(uint16_t sprite, uint16_t x, uint16_t y){
uint16_t vectPos = sprite / spr_limits; // TODO for bitshift operator.
uint16_t spritePos = sprite & (spr_limits - 1);

RECT srcRect;
srcRect.top = spritePos << 5;
srcRect.left = 0;
srcRect.bottom = srcRect.top + 32;
srcRect.right = 32;

static D3DXVECTOR3 vCenter(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vPosition(x, y, 0.0f);


gSprite->Draw(spritesVector[vectPos],
&srcRect,
&vCenter,
&vPosition,
D3DCOLOR_COLORVALUE(1.0f,1.0f,1.0f,1.0f));
}





Sprites are basically images of size 32x32 loaded into several textures, it works like charm and the memory usage as well as rendering speed is well above expected value.


I'm creating a Tibia game ( http://tibia.com ) client (old one, over 7 years old) just out of fun and the problem appears when I want to scale sprites using that SetScaleLevel function, sprites are not draw exactly as expected and there are some blank pixel lines between them:

clipboard01lip.jpg




Back to the blank pixel lines problem, CLAMP won't help at this point:

g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);

g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);




I'm stuck right now with this scaling problem (and I really need it but I think it might be problem with single precision numbers - hope not), hopefully you can help me.
Advertisement
Since we aren’t talking about hardware, I’d say your framerate is actually a bit on the low side; my hardware gets me around 5,000 FPS on a full 3D scene (no physics, just sorting and rendering), and for what I see here I would expect nothing below 10,000 FPS. You would benefit from some batching. With your hardware, I estimate that you are fairly optimized if you can reach 6,000 FPS. If you use both GPU’s. Take it as meaning you aren’t done optimizing just because your hardware draws you into a false sense of security. I believe batching 3D triangles will be faster than a bunch of sprite renders.

You may also benefit from some D3DSAMP_MAGFILTER and D3DSAMP_MINFILTER settings of D3DTEXF_POINT.
This will get you the vintage blocky look of nostalgic times that we all enjoy.

If you aren’t after that type of look, then you need to be aware of the center point for pixels in Direct3D 9, which is the center of the pixel, not the upper-left corner.
You can either adjust your orthographic projection to account for this with a small half-pixel shift, or add a small half-pixel shift to all of your sprites.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

To be honest I find it kind of enough, it's not like I need 10k fps when even on my low-end laptop I can reach over 100 fps in places with a lot of objects (laptop with core i3 330m and integrated graphics).

However indeed I would be glad to learn how to render using multiple GPU's, haven't found any tutorial yet.

void D3D9_engine::InitD3DBasics(){
g_pd3dDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
g_pd3dDevice->SetRenderState (D3DRS_LIGHTING, FALSE);
g_pd3dDevice->SetRenderState (D3DRS_LASTPIXEL, FALSE );
g_pd3dDevice->SetRenderState (D3DRS_ALPHABLENDENABLE, TRUE);
g_pd3dDevice->SetRenderState (D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState (D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState (D3DRS_CULLMODE, D3DCULL_NONE);
g_pd3dDevice->SetRenderState (D3DRS_ZENABLE, false);
// g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);


/* We need to get info about video card limitations and set max amount of sprites per texture */
D3DCAPS9 caps;
g_pd3dDevice->GetDeviceCaps(&caps);
uint16_t limit = caps.MaxTextureHeight >> 5;

spr_limits = limit;
D3DXCreateSprite(g_pd3dDevice, &gSprite);
}


MAG and MINFILTER doesn't change anything, it still looks the same (even using anisotropic) and it still has problems with those blank pixels.




You can either adjust your orthographic projection to account for this with a small half-pixel shift, or add a small half-pixel shift to all of your sprites.[/quote]

Shift in sprites wouldn't be such a good option, I need to be able to set the scaling (different values) all the time without any problems like that - about that adjustment, what are you suggesting?

Multiple GPU’s are used automatically. There is no special coding to enable them. If they are there, they are used.
I assume you are using an offset orthographic projection.
So add to its X offset 1 / (screen width) * 0.5 and to its Y 1 / (screen height) * 0.5.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I do not understand how screen width/height has to do anything here, sprites are already made to imitate "3D" look from specific point and they are looking just like those items you are looking from TOP - SOUTHEAST.




This is how some sprites look like normally saved in file:


tutspredit1.png





This is how the code looks like at least partially:




int renderTile(unsigned int pTicks, int offset, int x, int y, int z, int pz, int sx, int sy, int mainOffsetx, int mainOffsety, std::vector< std::pair< Creature*, RECT> > &namesMap){
Tile *tile = game->getMap()->getTile(Position(x + offset, y + offset, z));
TxPosition tileDrawPosition( (x - sx) * 32 + mainOffsetx, (y - sy) * 32 + mainOffsety, 0);
std::vector<Thing*> objects = tile->getThingsToDraw();
.......
//go through whole map..., bleble
engine->DrawSprite(type.sprites[spriteId], tileDrawPosition.x + tileDrawPosition.offset, tileDrawPosition.y + tileDrawPosition.offset);
.......
//end
}


engine->BeginScene();
engine->SetScaleLevel(1.5f);

for(int z = fromz; z >= toz; --z){
for(int x = sx; x < sx + 18; x++){
for(int y = sy; y < sy + 14; y++){
int offset = playerPosition.z - z;
int ret = renderTile(pTicks, offset, x, y, z, pz, sx, sy, mainOffsetx, mainOffsety, namesMap);
...
}
}
}

float scale = engine->GetScaleLevel();
engine->SetScaleLevel(1.0f);
//render creature names
engine->PresentScene();
Can your DrawSprite() function not just subtract 0.5 from the Y that is passed to it?


If you are doing everything with integers (as it appears you are, but without naming conventions I can only guess) then you can expect to have this kind of trouble.
If you are going to scale things up and down, you had better be using floating-point math.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


Can your DrawSprite() function not just subtract 0.5 from the Y that is passed to it?


If you are doing everything with integers (as it appears you are, but without naming conventions I can only guess) then you can expect to have this kind of trouble.
If you are going to scale things up and down, you had better be using floating-point math.


L. Spiro


I agree but that's not really my job to scale it, every kind of position in this game is based on 32 so floating point won't help here - I do understand you, but I'm telling the MATRIX and dx to scale it for me to specified floating point value - it won't matter if parameter received is integer or float since 32 in single precision floating point = 32 in integer.

Coordinates of the sprite are based on 32 * x, where x is natural number - it doesn't change, ever.

I could understand when the problem would be about scaling by myself (like let's say texture specified size draw with triangles), but it has to do something with the Direct itself.

1.0 scale (perfect):

clipboard02xg.jpg

and 8.5 scale:

clipboard01xe.jpg







However I do think it has something to do with single precision floating points and calculations used in Directx, maybe double precision would help.

D3DCREATE_FPU_PRESERVE - doesn't work though.

According to your code you are using a scale of 1.5. Not a whole number. How do whole numbers look?

What you are seeing could be caused by any number of things.
#1: You could have slightly off X/Y coordinates when you render the sprites.
-> Could be due to integer rounding errors.
#2: You could have slightly incorrect cell parameters. Be sure each cell starts at exactly a 32×32 boundary and is exactly 32×32. If off even by epsilon you will get these artifacts as you zoom in.

Are you sure point sampling did not help?
Because it is very clear that it is sampling the next texel up (or down) on the texture.

Why not give an example of where the second sprite over and down is being drawn?
The exact X and Y coordinates, the exact scale, and the exact position on the texture atlas.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


According to your code you are using a scale of 1.5. Not a whole number. How do whole numbers look?

What you are seeing could be caused by any number of things.
#1: You could have slightly off X/Y coordinates when you render the sprites.
-> Could be due to integer rounding errors.
#2: You could have slightly incorrect cell parameters. Be sure each cell starts at exactly a 32×32 boundary and is exactly 32×32. If off even by epsilon you will get these artifacts as you zoom in.

Are you sure point sampling did not help?
Because it is very clear that it is sampling the next texel up (or down) on the texture.

Why not give an example of where the second sprite over and down is being drawn?
The exact X and Y coordinates, the exact scale, and the exact position on the texture atlas.


L. Spiro





This is the scale of 2.0f :


clipboard02nr.jpg




The problem is pretty much the same.




#1 - They are fine, at this point even a 1px offset would be noticeable, coordinates are always multiple of 32.

#2 - That's kind of interesting suggestion, I will check it for sure.




Yes - I'm pretty sure, any kind of changing MAG filter doesn't change anything in the rendered image, strange though.

I have not used ID3DXSprite in over 15 years.
I just checked the documentation.
They batch automatically, so I was wrong to suggest doing so.

Also, sprites change many render states. You need to pass D3DXSPRITE_DONOTMODIFY_RENDERSTATE to Begin() on the sprites to avoid this.
D3DXSPRITE_DONOTSAVESTATE would probably help too to improve performance. So would D3DXSPRITE_DO_NOT_ADDREF_TEXTURE.

If you want all of the other states, except for the min/mag filter, you could alternatively set the min/mag filters after calling ID3DXSprite::Begin().

http://msdn.microsoft.com/en-us/library/bb174250(VS.85).aspx
http://msdn.microsoft.com/en-us/library/bb205466(v=VS.85).aspx



L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement