XNA Creators Club Online
Page 1 of 1 (12 items)
Sort Posts: Previous Next

HLSL - Texcoords

Last post 19/10/2009 19:57 by Lintford Pickle. 11 replies.
  • 14/10/2009 14:59

    HLSL - Texcoords

    Hi Everyone,

    I have several vertex grids defined in world space (33x33 verts), and I want to run a shader to generate noise based on each of the verts' world space coordinates in the PS.  To do this I am using the texcoord positions in a pixel shader to recreate 3d positions in world space given the four corner coordinates of the plane, and interpolating them using the TexCoords.  

    I am using a full screen quad and a custom effect (no sprite batch), but I'm having problems using the texture coordinates in the PS because it seems they don't ever actually equal 0 or 1.  Here is some code to better describe the problem:

    The vertices for the full screen quad are defined in screen space, like so:

    private void CreateVertices() 
        // Define the vertex positions. 
        m_Vertices[0] = new VertexPositionTexture(new Vector3(-1, 1, 0f), new Vector2(0, 1)); 
        m_Vertices[1] = new VertexPositionTexture(new Vector3(1, 1, 0f), new Vector2(1, 1)); 
        m_Vertices[2] = new VertexPositionTexture(new Vector3(-1, -1, 0f), new Vector2(0, 0)); 
        m_Vertices[3] = new VertexPositionTexture(new Vector3(1, -1, 0f), new Vector2(1, 0)); 
     


    The vertex shader just passes the screen space and texture coordinate straight through to the PS, so there is nothing special happening:

    void VertexShaderFunction(  float4 inPosition   : POSITION, 
                                float2 inTexCoords  : TEXCOORD0, 
                            out float4 outHPosition : POSITION, 
                            out float2 outTexCoords : TEXCOORD0) 
        outHPosition = inPosition; 
        outTexCoords = inTexCoords; 


    Just to test I have cut down my 'problem', and created the following PS to try and understand what is happening. I understand that the TEXCOORD variables are interpolated between the VS and the PS, but I thought that the first and last values of each dimension should be at least equal to 0 and 1 (i.e. .x and .y).  

    float4 PSEdgeTest(float2 inTexCoords : TEXCOORD0) : COLOR0 
        float4 land = (float4)0; 
     
        if(inTexCoords.x==0){land=float4(1,0,0,1);} // hit 
        if(inTexCoords.x==1){land=float4(1,0,0,1);} // never hit 
        if(inTexCoords.y==0){land=float4(1,0,0,1);} // never hit 
        if(inTexCoords.y==1){land=float4(1,0,0,1);} // never hit 
         
        return land; 


    Linear interpolation - which is what I assume is being used if any shouldn't change the 'bounds' of the coordinates should it?  The above PS outputs a black texture (GraphicsDevice.Clear(Color.Black)) with a red line on the left hand edge.  I expected this would output a black texture surround by a red square :)

    In case somebody can suggest a better method, this is how I am currently trying to interpolate through the grid of vertices in the PS:

    float3 xNE; // passed from app 
    float3 xNW; // passed from app  
    float3 xSE; // passed from app  
    float3 xSW; // passed from app 
     
    float4 PS_INoise(float2 inTexCoords : TEXCOORD0) : COLOR0 
        float land = 0; 
     
        // Get the direction, from the origin (top-left) to the end vector (bottom-right). 
        float3 xDirection = xNE - xNW; 
        float3 zDirection = xSW - xNW; 
         
        // Scale the distance by the texture coordinate (which is between 0 and 1). 
        xDirection *= inTexCoords.x; 
        zDirection *= 1 - inTexCoords.y; 
         
        // Lerp outward from the origin (top-left) in the direction of the end vector (bottom-right) 
        float3 lerpedWorldPos = xNW + xDirection + zDirection; 
         
        // just to test, generate 3d noise based on this 'lerped' coord 
        land = perlinnoise(lerpedWorldPos); 
     
        return land; 


    Now this does work, but like I mentioned above, because the texcoord.x and .y never actually equal 0 or 1, there are slight gaps when I generate heightmaps for nodes which are next to each other.  


    Thanks in advance for any help,
    -John

  • 14/10/2009 15:29 In reply to
    • (2342)
    • premium membership MVP
    • Posts 1.226

    Re: HLSL - Texcoords

    Answer
    Reply Quote
    In D3D9 (and by extension, XNA) there's an annoying little issue regarding pixel coordinates and texel coordinates...it's described in full here.  If you want the short version, it's basically this: your vertex coordinates need to be offset by half a pixel for a full-screen quad to line up right.  I usually apply the offset in the shader so that I can just keep quad vertex buffer for a ll RenderTarget sizes, like this:

    void PostProcessVS (    in float3 in_vPositionOS        : POSITION,
                            in float2 in_vTexCoord            : TEXCOORD0,                    
                            out float4 out_vPositionCS        : POSITION,
                            out float2 out_vTexCoord        : TEXCOORD0
                       )
    {
        out_vPositionCS.x = in_vPositionOS.x - (1.0f / g_vDestinationDimensions.x);
        out_vPositionCS.y = in_vPositionOS.y + (1.0f / g_vDestinationDimensions.y);
        out_vPositionCS.z = in_vPositionOS.z;
        out_vPositionCS.w = 1.0f;
        out_vTexCoord = in_vTexCoord;
    }   


    But of course you could apply the offset directly in your vertex coordinates, if you wish.  Just remember that you'll need to change the offset depending on the size of the RenderTarget you're rendering to.

    Matt Pettineo | DirectX/XNA MVP


    Ride into The Danger Zone | PIX With XNA Tutorial
  • 14/10/2009 16:18 In reply to

    Re: HLSL - Texcoords

    Answer
    Reply Quote
    In addition to the half pixel offset, you should be aware that your texture coordinates will never go all the way up to 1 at the right or bottom of your image. Polygon filling is exclusive of the right and bottom edges, not inclusive (if it was inclusive, connected meshes of multiple triangles would not join up contiguously).

    For instance if you drew a 4x1 texture onto a 4x1 sprite positioned with the top left at -0.5,-0.5, the four pixels rendered will be:

       0,0 - texcoord 0
       1,0 - texcoord 0.25
       2,0 - texcoord 0.5
       3,0 - texcoord 0.75
    XNA Framework Developer - blog - homepage
  • 15/10/2009 7:39 In reply to

    Re: HLSL - Texcoords

    Hello to you both,

    Thanks a lot for the answer MJP, this is something I wasn't aware of, but now I'll definitely keep it in mind for future work, and it was an interesting read.  And also thanks Shawn for explaining the behavior once the offset applied.

    After making the change and adding the offset, i still couldn't get the above example to output what I expected (where the output texture should be outlined with a red pixel) - again there was only one side of the texture affected (.y==0).  I will try this again tonight, but I am hoping it will be possible to use a texel grid which has 1 texel more in both dimensions.  But maybe in the meantime someone may have an idea if this is a 'fool's errand'? or if there is a better way (if it is even possible).

    anyway, thanks for the help
    -John
  • 15/10/2009 8:09 In reply to

    Re: HLSL - Texcoords

    Despite the above about half pixel centres, there was a big discussion about this on the DX mailing list and the output wisdom from that was that it works better if you apply 0.35 instead of 0.5 as your pixel offset. Nobody really explained why this should be, but I can verify it worked for me when doing similar tasks to yours. Maybe give it a try?
    Paul Johnson - CEO of rubicondev.com, speaking for myself.
  • 15/10/2009 20:22 In reply to

    Re: HLSL - Texcoords

    The 0.35 value is there to compensate for various buggy drivers and non-conforming implementations, and is generally only to be used when you use POINT filtering, for 1:1 texture:screen application (GUIs and the like).
    When doing filtering, you should use the 0.5 texel offset, and if the user is using a bad driver, it will look slightly off, but that's OK.

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 15/10/2009 21:20 In reply to

    Re: HLSL - Texcoords

    Hi there, Jon.

    I was just floating ideas as he still clearly has a problem.

    I take your point about the 0.35 not being intended as a universal salve, but ime that's exactly what it has become. I had occasional problems with 0.5 across the board of usages, but since that discussion I've used 0.35 across the board and have never seen a problem since in anything. Which is good :)

    Paul Johnson - CEO of rubicondev.com, speaking for myself.
  • 15/10/2009 21:50 In reply to

    Re: HLSL - Texcoords

    It's important to understand that the 0.35 thing only works if you are using point sampling.

    If you want exact equality of texture coordinates with a specific value that you are going to test in the shader (like in this example where he is explicitly testing for texcoords being exactly zero) then any variation in the rasterization behavior will make this test fail. It's only going to work if your code exactly matches what the driver and hardware is doing, which means using an exact 0.5 offset (as that is what the spec says and what most cards provide most of the time), and just living with the fact that this code will fail on buggy hardware/drivers that do something slightly different, or on implementations that have unusually poor numeric precision.

    The 0.35 thing works with point sampling, because in that case you are rounding the result to the nearest integer, so the goal is no longer to come up with an exactly right value, just a value that is close enough to right that it will round in the right direction in spite of a wide range of implementation variability. 0.35 happens to do that pretty well for the particular set of hardware and driver bugs that exist today. But if you turn off point sampling, which disables the rounding, it makes no sense to use this at all since you will end up with unwanted filtering on every sample.

    To the OP: it's not generally a good idea to rely on exact equality of results like this in graphics programming, because such things are very fragile in the presence of filtering, antialiasing, poor numeric precision, etc. It's usually better to formulate things in terms of continuous equations that will react gracefully when scaled, rather than testing specific values for exact equality.
    XNA Framework Developer - blog - homepage
  • 15/10/2009 22:28 In reply to

    Re: HLSL - Texcoords

    Nod on the point sampling, though I do have a feedback routine that uses bilinear that's never given a *perfect* sphere of outward munge, though with 0.35 it worked better than other things I tried to fix it, so I guess I've relied on it ever since. This despite the slight shift in alignment in the vertex positions that's also been discussed to death on the mailing list. I guess stuff like this is why this topic never quite dies.

    And O/T I see you worked about 3 miles from me at Climax, Shawn. I'm sure our paths have crossed at some point. I just checked your blog and saw you list the Oric in your rap sheet. LOL, not many of us old-timers about!   ping, zap, etc.
    Paul Johnson - CEO of rubicondev.com, speaking for myself.
  • 15/10/2009 22:43 In reply to

    Re: HLSL - Texcoords

    Heh, Oric was awesome!

    Forget all this XACT rubbish, all you need is the four standard sound effect play methods: zap(), ping(), shoot(), and explode().  No matter what game you're making, those four should be enough for everyone, right?
    XNA Framework Developer - blog - homepage
  • 15/10/2009 22:51 In reply to

    Re: HLSL - Texcoords

    I'll settle for that if you don't inflict that bizarror bit/pixel arrangement on me as well. That thing was truly "challenged" :)
    Paul Johnson - CEO of rubicondev.com, speaking for myself.
  • 19/10/2009 19:57 In reply to

    Re: HLSL - Texcoords

    Hi again everyone,

    just to update this thread a little, using the information provided by MJP I have managed to offset the original vertex grid to get the texcoords to include the [0,1] bounds in the interpolated values (checked in PIX).

    Shawn Hargreaves:
    To the OP: it's not generally a good idea to rely on exact equality of results like this in graphics programming, because such things are very fragile in the presence of filtering, antialiasing, poor numeric precision, etc. It's usually better to formulate things in terms of continuous equations that will react gracefully when scaled, rather than testing specific values for exact equality.


    Thanks again with your advice here Shawn and I understand and agree with you, so I will continue to look for alternatives to my current 'fix', although the the output in the game doesn't depend on total equality of results.  Until I find another solution, I'll try and keep with implementation.

    For others who may want to try this, I just applied a small offset of 1 extra pixel to the width and height of the pre-transformed vertex positions:

    1 float ps = 1.0f / renderTargetWidth;  
    2   
    3 // Define the vertex positions.  
    4 m_Vertices[0] = new VertexPositionTexture(new Vector3(-1, 1, 0f), new Vector2(0, 1));  
    5 m_Vertices[1] = new VertexPositionTexture(new Vector3(1, 1, 0f), new Vector2(1 + ps, 1));  
    6 m_Vertices[2] = new VertexPositionTexture(new Vector3(-1, -1, 0f), new Vector2(0, 0 - ps));  
    7 m_Vertices[3] = new VertexPositionTexture(new Vector3(1, -1, 0f), new Vector2(1 + ps, 0 - ps));  


    Thanks for you help,
    -John
Page 1 of 1 (12 items) Previous Next