Jump to content
  • Advertisement
Sign in to follow this  
RobMaddison

Texel offset

This topic is 2540 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Merry Xmas everyone

I'm finalising my post processing framework and I'm testing it currently with a bloom filter. My steps are:

1) Render normal scene to RT-Canvas1 [1280x1080]
2) Post process step: Bright pass (source=RT-Canvas1, dest=PPTex1 [320x270] )
3) Post process step: Blur pass V (source=PPTex1, dest=PPTex2 [320x270] )
4) Post process step: Blur pass H (source=PPTex2, dest=PPTex1 [1820x1080] )
5) Post process step: Composite (source1=PPTex1, source2=RT-Canvas1, dest=RT-Canvas2 [1280x1080] )
6) Post process step: Copy (source1=RT-Canvas2, dest = Back Buffer)

Everything works fine but my blur is slightly offset from the image. I've changed it slightly so that my screen shows the results of the bright pass filter combined with the blur and the blur is definitely a little out - strangely, it only looks like it's out vertically (the blur is too low).

I've read that there is a 0.5 difference between texels and screen pixels, i.e. you need to take 0.5 of a unit off the texture coordinates when rendering to the screen, but my blur is rendering to a render target.

Could anyone let me know at which step above I need to add/subtract this 0.5 offset?

Thanks

Share this post


Link to post
Share on other sites
Advertisement
If you're using Direct3D 9 you have to remove a 0.5f offset when rendering fullscreen quads, as explained here (in the "Summary" of the article).

So you have to subtract 0.5 offset in every step that you use screen-space vertices (every post process step because you're probably using fullscreen quads). Edited by TiagoCosta

Share this post


Link to post
Share on other sites
Thanks for the reply, when you say every step that renders a full screen quad, does that include the steps where I'm rendering to a quarter-size quad? I'm guessing so as it's still the same size as the screen.

I still don't really get why I'd need to do it to all of them as surely that would just move everything by the offset, effectively still not aligning the bloom correctly.

I'll try and post some photos later but essentially, the haze around, e.g., a white block looks like it should be a few pixels higher, and I also get a strange white line at the top of the screen.

Share this post


Link to post
Share on other sites
Just to confirm - are you using D3D9?
Using a 2x2 resolution render-target as an example, and assuming we're using an ortho projection from 0,0 to 1,1:x--+--x
|A |B |
+--+--+
|C |D |
x--+--x
In D3D10/11, if we render a quad with a the vertices (0,0) (1,0) (1,1) and (0,1), then those vertices will line up perfectly with the 4 'x's in the diagram.
When the pixel shader executes for the pixel 'A', the pixel centre does not line up with any of our vertices -- it is correctly interpolated as (0.25, 0.25). If this is used to sample a 2x2 texture, it will correctly sample the exact centre of the top-left texel.

However, In D3D9 microsoft made a regrettable decision. The vertex (0,0) in D3D10 was plotted as the top-left 'x', but in D3D9 it is actually placed in the centre of the 'A' pixel.
Because of this mis-placed vertex, when the pixel shader executes for the pixel 'A', no interpolation actually takes place for that pixel -- it lines up perfectly with the top-left vertex, so it uses it's texture coordinate uninterpolated.
Then, if you used this texture coordinate to sample a 2x2 texture, you wouldn't be correctly sampling the top-left pixel, you'd be sampling the top-left corner of the top-left pixel. When this happens over the whole screen, it has the effect of shifting/blurring the whole screen slightly.

Share this post


Link to post
Share on other sites
Yes, what they said. And just to make matters worse -- you can often have this bug in some of your full-screen passes without realizing it. If you're doing point-sampling (perhaps because you're reading from unfilterable textures, or implementing bilateral blurs or whatever), then all of your texture taps end up EXACTLY at the intersections of four texels. In my experience, which texel gets sampled is then dependent on the math/rounding in your UV computation, the GPU you're on, etc... It might appear to work, or work for 90% of the screen, except in certain areas where the floating point math drifts enough to push the texture unit to reading a different texel. With linear filtering, it's much more obvious, because you always end up with the full half-texel shift and resulting blur.

Share this post


Link to post
Share on other sites
Thanks for all the posts, I understand now but I'm still getting strange results even after I've removed 0.5 from my texture coordinates (divided by the extents of the texture size). Here's a screen shot of a close up of the blur:

Shot1.jpg

This is essentially the following (all render targets are the same size as the back buffer, e.g. 1280x1080):

Render scene to RT1
Brightpass RT1 to RT2
Brightpass RT1 to RT3
Blur V RT2 to RT4
Blur H RT4 to RT2
Composite RT3 and RT2 to RT1
Copy RT1 to Back Buffer

Here's a quick shot of the top of the screen, you can see some banding on the first few pixels, this is only along the top, the left side of the screen has no banding.
Shot2.jpg

As you can see from the first screenshot, the blur is offset bottom right and from the second screenshot there is some banding. I'm fairly sure my post processing C++ code is correct, so here are my shaders, they are v simple and probably not too efficient yet.

Bright Pass:


OutputVS TransformVS(float3 posL : POSITION0, float2 texC : TEXCOORD0)
{
OutputVS outVS = (OutputVS)0;
outVS.posH = float4(posL.xy, 0.0f, 1.0f);
outVS.texC = texC;
outVS.texC.x -= 0.5 / 1280;
outVS.texC.y -= 0.5 / 1080;
return outVS;
}
float4 TransformPS(OutputVS In) : COLOR
{
float4 colour = tex2D( DiffuseTexture, In.texC );
float col = (colour.x + colour.y + colour.z) / 3;
colour = float4(col, col, col, 1.0f);
colour -= g_brightPassThreshold;
colour = max(0, colour);
colour *= 10;
return colour;
}


Here is my blur shader. This runs as two passes, P0 then P1:


OutputVS VShader(float3 posL : POSITION0, float2 texC : TEXCOORD0)
{
OutputVS outVS = (OutputVS)0;
outVS.posH = float4(posL.xy, 0.0f, 1.0f);
outVS.texC = texC;
outVS.texC.x -= 0.5f / g_blurTexSizeX;
outVS.texC.y -= 0.5f / g_blurTexSizeY;
return outVS;
}
float4 PShaderH(OutputVS In) : COLOR
{
float x = 1.0f / g_blurAmountX;
float4 blur = 0.0f;
blur += tex2D(DiffuseTexture, float2(In.texC.x - 4.0 * x, In.texC.y)) * 0.05f;
blur += tex2D(DiffuseTexture, float2(In.texC.x - 3.0 * x, In.texC.y)) * 0.09f;
blur += tex2D(DiffuseTexture, float2(In.texC.x - 2.0 * x, In.texC.y)) * 0.12f;
blur += tex2D(DiffuseTexture, float2(In.texC.x - 1.0 * x, In.texC.y)) * 0.15f;
blur += tex2D(DiffuseTexture, float2(In.texC.x - 0.0 * x, In.texC.y)) * 0.18f;
blur += tex2D(DiffuseTexture, float2(In.texC.x + 1.0 * x, In.texC.y)) * 0.15f;
blur += tex2D(DiffuseTexture, float2(In.texC.x + 2.0 * x, In.texC.y)) * 0.12f;
blur += tex2D(DiffuseTexture, float2(In.texC.x + 3.0 * x, In.texC.y)) * 0.09f;
blur += tex2D(DiffuseTexture, float2(In.texC.x + 4.0 * x, In.texC.y)) * 0.05f;
return blur;
}
float4 PShaderV(OutputVS In) : COLOR
{
float y = 1.0f / g_blurAmountY;
float4 blur = 0.0f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y - 4.0 * y)) * 0.05f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y - 3.0 * y)) * 0.09f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y - 2.0 * y)) * 0.12f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y - 1.0 * y)) * 0.15f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y - 0.0 * y)) * 0.18f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y + 1.0 * y)) * 0.15f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y + 2.0 * y)) * 0.12f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y + 3.0 * y)) * 0.09f;
blur += tex2D(DiffuseTexture, float2(In.texC.x, In.texC.y + 4.0 * y)) * 0.05f;
return blur;
}


Here is my composite shader:


OutputVS TransformVS(float3 posL : POSITION0, float2 texC : TEXCOORD0)
{
OutputVS outVS = (OutputVS)0;
outVS.posH = float4(posL.xy, 0.0f, 1.0f);
outVS.texC = texC;
return outVS;
}
float4 TransformPS(OutputVS In) : COLOR
{
float4 colour1 = tex2D( DiffuseTexture, In.texC );
float4 colour2 = tex2D( CompositeTexture, In.texC );

return (colour1 + colour2);
}


And here's my copy shader:


OutputVS TransformVS(float3 posL : POSITION0, float2 texC : TEXCOORD0)
{
OutputVS outVS = (OutputVS)0;
outVS.posH = float4(posL.xy, 0.0f, 1.0f);
outVS.texC = texC;
outVS.texC.x -= 0.5 / 1280;
outVS.texC.y -= 0.5 / 1080;
return outVS;
}
float4 TransformPS(OutputVS In) : COLOR
{
return tex2D( DiffuseTexture, In.texC );
}


I tried putting the 0.5 subtraction in the pixel shaders for each of these and it doesn't make any difference.

Any more ideas? Perhaps there's something wrong with my blur shader, as if I don't include the blur pass, I don't get any banding.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!