5
\$\begingroup\$

I'm making a tile-based game, using one spritesheet containing all tile graphics.

Each tile has a size of 32x32 pixels.

The main problem is: when I draw the tile to the screen, if the tile position x and y are not rounded or if scale is activated in spriteBatch.Draw() method (scale != 1.0f), I get some lines of adjacent tiles on the spritesheet into the current tile drawed.

I already tried setting SamplerState to PointClamp, removing AntiAlias, but still doesn't work.

Here I'll show images of some tests that I made, with a test sprite sheet that I've created (I made a 9x9 spritesheet, with each sprite of size 32x32 containing a unique solid color).

Tests:

Tests

SpriteSheet used:

SpriteSheet used


Already tried to remove anti-alias, set PointClamp as sampler state, but still getting this issue, XNA keeps drawing part of the adjacent pixels of the texture on the screen.

What I want is to get the correct area of the tilesheet texture (as seen in the first test, that gets just the yellow pixels).

My question is: Is there any way that I can fix this, WITHOUT adding tile spacing or any other modification involving the tilesheet?

Maybe disabling a texture filtering that is done by XNA, or something like that.

\$\endgroup\$
4
  • \$\begingroup\$ I've tested this on another machine, and it worked normally, as expected. This behavior only happens when I try to execute the game on my virtual machine, don't know why, it seems that the sampler state do not change to PointClamp, or any other one, remaining as LinearClamp. I've tried to change the SamplerState using GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp and got the same result. Tried to use a power of two texture, and didn't worked also. It should be something with the virtual machine, somehow I cannot change the sampler state there. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 4:05
  • \$\begingroup\$ Oh, and forgot to mention, I'm using Reach Game Profile, cause I'm not able to run in HiDef profile on my virtual machine. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 4:15
  • 2
    \$\begingroup\$ If you were able to isolate it to a specific hardware (in this case, a virtual one), and you managed it to run correctly on other hardware, it is very likely that it's a problem with the hardware drivers. Virtual machine graphic adapters are not very featured and may not implement everything correctly. What VM are you using, and with which settings? \$\endgroup\$ Commented Dec 18, 2012 at 4:37
  • \$\begingroup\$ I'm using VMWare Fusion 5.0.2, running a 64-bit Windows 7 vm on a Mac OS X 10.7.5. Also Accelerate 3D graphics options is enabled, supporting DirectX 9.0EX with Aero and OpenGL 2.1. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 5:01

5 Answers 5

1
\$\begingroup\$

From my experience, this seems like you aren't setting your SamplerState to PointClamp correctly. This is indicated by the "blending" at the top left corner. Have you tried rendering on different hardware?

First, Your texture isn't a power-of-two Although some wouldn't agree, I always use textures^2. On some older hardware it was a requirement. I would suggesting sizing up to 128x128 (4*32, 4*32)

Second, have you tried setting GraphicsDevice.SamplerState[0] directly? (source)

\$\endgroup\$
5
  • \$\begingroup\$ Forgot to mention, this isn't really a "half-pixel" problem. Unlike mine \$\endgroup\$ Commented Dec 18, 2012 at 1:25
  • \$\begingroup\$ Thanks for your reply and that link, it seems that I'm having the same problem in here. I was running the game on my virtual machine. I've tried to use a power-of-two texture and tried to set the GraphicsDevice.SamplerState[0] directly, none have worked. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 4:08
  • \$\begingroup\$ I've tested on another machine, and it worked properly. It seems to be a problem with virtual machine drivers. The guy on that link was running the game on a virtual machine too. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 4:36
  • 1
    \$\begingroup\$ Setting a power of two is not necessary in XNA. \$\endgroup\$
    – ashes999
    Commented Jan 17, 2013 at 12:47
  • \$\begingroup\$ @ashes999 No it is not necessary, but on some cards, it does help performance. In some extreme cases, it's required by the card. \$\endgroup\$ Commented Jan 17, 2013 at 23:02
1
\$\begingroup\$

Based on the images you're showing, it is very clear that the renderer is interpolating, so you have to disable this interpolation. This has little to do with half pixels.

Now, I'm not really knowledgeable in XNA, but a quick google of "xna nearest neighbor interpolation" shows that there are at least two places you can try to disable interpolation:

This one suggests using PointClamp as a parameter for SpriteBatch.Begin(), while this other one suggests setting it at GraphicsDevice.SamplerStates.

You mention you tried setting the sampler state to PointClamp, so how about trying at SpriteBatch.Begin()?

\$\endgroup\$
1
  • \$\begingroup\$ Thank you, I've corrected the post title. With SpriteBatch.Begin() with no arguments, the SamplerState used is set to SamplerState.LinearClamp. But I've tried it anyway, and didn't worked. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 4:17
1
\$\begingroup\$

I had exactly the same problem that User24092, and I tried all those suggestions with no result. Finally the solution suggested by Panda Pajama made the trick!

Just set the PointClamp sampler state when you begin the spriteBatch:

spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, null,null);

It worked like a charm!

\$\endgroup\$
0
\$\begingroup\$

The best way to fix this is to round your X and Y values before drawing them.

The "usual" way I handle this in XNA is to keep X and Y internally as a float, and to expose them externally (eg. to SpriteBatch) as an int. You should, of course, use Math.Floor to get you the correct tile.

The code would look something like this:

float x, y = 0f;
// ...
public int X { get { return (int)Math.Floor(this.x); } }
public int Y { get { return (int)Math.Floor(this.y); } }
spriteBatch.Draw(this.texture, new Vector2(this.X, this.Y), Color.White);
\$\endgroup\$
5
  • \$\begingroup\$ I've already tried this, there is just a problem, I want to do scaling too, and with scale even if the sprite location is setted with Math.Floor, this strange behavior ocurrs. Maybe I should calculate the size of the sprite after the scale applying a Vector2.Tranform and remove it from the location calculated? I'm gonna try this, btw, thanks for your reply. \$\endgroup\$
    – user24092
    Commented Dec 18, 2012 at 4:12
  • \$\begingroup\$ Use the overload for Draw that takes in a source rectangle, and pass in the proper coordinates. That should solve the problem; scaling shouldn't matter then. \$\endgroup\$
    – ashes999
    Commented Dec 18, 2012 at 12:40
  • \$\begingroup\$ I'm already using the approach that takes in a source rectangle. Do you mean destination rectangle? I tried using destination rectangle, instead of using scale, and scaling it by setting the width and height of the rectangle, which are Integers, but still having that interpolation problem. \$\endgroup\$
    – user24092
    Commented Dec 19, 2012 at 5:16
  • \$\begingroup\$ No, I meant a source rectangle, not a destination rectangle. \$\endgroup\$
    – ashes999
    Commented Dec 19, 2012 at 12:11
  • \$\begingroup\$ Can you try creating a new project and using a different spritesheet image? That'll isolate if it's related to something else in your project or not. \$\endgroup\$
    – ashes999
    Commented Jan 17, 2013 at 12:49
0
\$\begingroup\$

I know it may be a bit extreme, but try to apply a pixel shader with filter point to force that the exact pixels are rendered. I'm really curious with this problem.

Texture Sprite;
sampler SamplerSprite = sampler_state {
    texture = <Sprite>;
    filter = POINT;
};

float4 Ps_Main(float2 coord : TEXCOORD0) : COLOR {
    return tex2D(SamplerSprite, coord);
}

technique Tech_0 {
    pass Pass_0 {
            pixelshader = compile ps_2_0 Ps_Main();
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Yeah, I've tried this already! I thought the same, haha, it's kind of extreme but.. let's try it out! But the result is: it still keeps interpolating ): \$\endgroup\$
    – user24092
    Commented Dec 19, 2012 at 5:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .