Sprite Rendering

Started by
7 comments, last by firlionel 17 years ago
Hey, I'm currently trying to implement pixel-exact 2d sprite rendering. But now I'm stuck with a strange bug. I will just post first all the information that I think is needed to find the bug and then explain whats wrong (if it's not obvious). First some screenshots and a PIXrun-file with which you can watch the whole frame: http://rimproject.dyndns.org/das.png http://rimproject.dyndns.org/sprite_screenshot2.png http://rimproject.dyndns.org/sprite_run.pixrun Source to render a sprite:

float_t zn = 0.1f;
float_t zf = 10000.0f;
Matrix4x4f mat;
mat.ToIdentity();
Rectf r(0, 800, 0, 600);
mat._11 = 2 / (r.right - r.left);//2 / 799.0f;
mat._14 = (r.left + r.right) / (r.left - r.right);//-1.0f - 1.0f/799.0f;
mat._22 = 2 / (r.top - r.bottom);//-2 / 599.0f;
mat._24 = (r.top + r.bottom) / (r.bottom - r.top);//1.0f + 1.0f/599.0f;
mat._33 = 1 / (zf - zn); 
mat._34 = zn / (zn - zf); 
mat._44 = 1.0f;
vshader_constants_->SetFloatMatrix(constant_mat_orthogonal_, mat);

Vertex* v = vstream_->Lock<Vertex>(0, 0, LockDiscard);

Rectf texrect1 = texrect;
if (texrect1.right == 0)	texrect1.right = texture->GetWidth();
if (texrect1.bottom == 0)	texrect1.bottom = texture->GetHeight();

v[0].x = (float_t)position.left - 0.5f;
v[0].y = (float_t)position.top - 0.5f;
v[0].z = 0.5f;
v[0].rhw = 1.0f;
v[0].tu = texrect1.left / (float_t)texture->GetWidth();
v[0].tv = texrect1.top / (float_t)texture->GetHeight();

v[1].x = (float_t)position.right - 0.5f;
v[1].y = (float_t)position.top - 0.5f;
v[1].z = 0.5f;
v[1].rhw = 1.0f;
v[1].tu = texrect1.right / (float_t)texture->GetWidth();
v[1].tv = texrect1.top / (float_t)texture->GetHeight();

v[2].x = (float_t)position.left - 0.5f;
v[2].y = (float_t)position.bottom - 0.5f;
v[2].z = 0.5f;
v[2].rhw = 1.0f;
v[2].tu = texrect1.left / (float_t)texture->GetWidth();
v[2].tv = texrect1.bottom / (float_t)texture->GetHeight();

v[3].x = (float_t)position.right - 0.5f;
v[3].y = (float_t)position.bottom - 0.5f;
v[3].z = 0.5f;
v[3].rhw = 1.0f;
v[3].tu = texrect1.right / (float_t)texture->GetWidth();
v[3].tv = texrect1.bottom / (float_t)texture->GetHeight();

vstream_->Unlock();

graphicsdriver_->GetBlendState()->Enable();
graphicsdriver_->GetBlendState()->SetColorBlendOp(AlphablendOpAdd);
graphicsdriver_->GetBlendState()->SetColorSrcBlend(AlphablendSrcAlpha);
graphicsdriver_->GetBlendState()->SetColorDestBlend(AlphablendInvSrcAlpha);

pshader_constants_->SetFloatVector(constant_color_, Vector4f(modulation_color.r, modulation_color.g, modulation_color.b, modulation_color.a));
graphicsdriver_->GetVertexProcessingState()->SetVertexDeclaration(vertex_declaration_);
graphicsdriver_->GetVertexProcessingState()->SetVertexStream(0, vstream_, sizeof(Vertex));
graphicsdriver_->GetVertexProcessingState()->SetVertexShader(vshader_);
graphicsdriver_->GetVertexProcessingState()->SetConstantBuffer(vshader_constants_);
graphicsdriver_->GetPixelProcessingState()->SetPixelShader(pshader_);
graphicsdriver_->GetPixelProcessingState()->SetConstantBuffer(pshader_constants_);
graphicsdriver_->GetSamplerState(0)->SetTexture(texture);
graphicsdriver_->GetSamplerState(0)->SetMinFilter(TexFilterPoint);
graphicsdriver_->GetSamplerState(0)->SetMagFilter(TexFilterPoint);
graphicsdriver_->DrawPrimitive(PrimitiveTriangleStrip, 0, 2);

graphicsdriver_->GetBlendState()->Disable();

Source which calls the sprite-rendering:

sprite_->Render(test_texture_b_, Rect(0, 799, 0, 599), Rectf(0, 0, 0, 0), Colorf::WHITE());
// top left
sprite_->Render(test_texture_v_, Rect(5, 14, 5, 14), Rectf(0, 0, 0, 0), Colorf::WHITE());
sprite_->Render(test_texture_h_, Rect(5, 14, 15, 24), Rectf(0, 0, 0, 0), Colorf::WHITE());
// bottom left
sprite_->Render(test_texture_v_, Rect(5, 14, 575, 584), Rectf(0, 0, 0, 0), Colorf::WHITE());
sprite_->Render(test_texture_h_, Rect(5, 14, 585, 594), Rectf(0, 0, 0, 0), Colorf::WHITE());
// top right
sprite_->Render(test_texture_v_, Rect(785, 794, 5, 14), Rectf(0, 0, 0, 0), Colorf::WHITE());
sprite_->Render(test_texture_h_, Rect(785, 794, 15, 24), Rectf(0, 0, 0, 0), Colorf::WHITE());
// bottom right
sprite_->Render(test_texture_v_, Rect(785, 794, 575, 584), Rectf(0, 0, 0, 0), Colorf::WHITE());
sprite_->Render(test_texture_h_, Rect(785, 794, 585, 594), Rectf(0, 0, 0, 0), Colorf::WHITE());

The HLSL-Source:

sampler texSampler;

void vs_main(
  float4 inPosition : POSITION,
  float2 inTexCoord : TEXCOORD0,
  out float4 outPosition : POSITION,
  out float2 outTexCoord : TEXCOORD0,
  uniform float4x4 orthogonalProjection
)
{
  outPosition = mul(orthogonalProjection, inPosition);
  outTexCoord = inTexCoord;
}

void ps_main(
  float2 inTexCoord : TEXCOORD0,
  out float4 outColor : COLOR,
  uniform float4 modulationColor : float4
)
{
  outColor =
    tex2D(texSampler, inTexCoord) *
    modulationColor
  ;
}

The problem is that it doesn't render the images pixel-exact. As you may see in 'das.png' the colors are wrong. when you look at 'sprite_screenshot2.png' you can see that the row in the middle of the picture is white instead of black (every second row/column is black on the texture). I'd be very grateful if anyone could point me into a direction to solve the problem! Thanks, Chris
Advertisement
Directly Mapping Texels to Pixels from the DX SDK Documentation should prove helpful.
Sirob Yes.» - status: Work-O-Rama.
Well this article is actualy the source of my '-0.5f' modifiers to calculate the vertices.
Right, sorry, didn't see you were modifying the position.

While I don't think this would fix it, I think offsetting the texcoords would be a better idea than offsetting the position. In addition, I believe a factor of around 0.4 might be more accurate.

Hope this helps.
Sirob Yes.» - status: Work-O-Rama.
And with what amount would you offset texcoords?

[Edited by - firlionel on March 18, 2007 12:03:43 PM]
(-0.5 / texmap_width) and (-0.5 / texmap_height). It is common to pass the texmap dimensions as a shader constant.
Well I tried to offset texture coordinates instead of positions which resulted in the same picture as when offsetting positions.

I also noticed that in both configurations (when offseting positions and when offsetting texture coordinates) the rendering differs between reference rasterizer and hardware-device. Although when offsetting texture coordinates the difference between them isn't as big. Additionaly neither ref-rast nor the hardware-device create the expected output.

Do you have an idea why?

I also had a look into Crazy Eddies Gui (he's offsetting positions by -0.5 as I did) and irrlicht (uses completly another procedure with calculating vertices on the cpu) and I'm wondering if their results are pixel-exact...

Thanks for ideas!
Chris
Thinking about it, I think I should've suggested a positive offset in tex coords of 0.5/w and 0.5/h - sorry.
Now that you mention it and I look again at my code I see that I've coded it by intuition (or mistake...) the right way around yesterday. I just tried with negative offsets which (logically) results in textures wrapping around so I think I would have noticed that.

Chris

This topic is closed to new replies.

Advertisement