Issue Rendering Full Screen Quad in Screen Space

Started by
8 comments, last by JWalsh 15 years, 4 months ago
I have a project for which I have to render a fullscreen, background image. Based on the methods we use for rendering, using SpriteBatch is not an option. So I chose what I figured was the easiest method: render a quad using screen space. So first I created this class to be my class for screen space quads (since I also will later need to render background images that aren't full screen):

public class ScreenSpaceQuad : IDisposable 
{ 
    VertexDeclaration vd; 
    Effect effect; 
    Texture2D texture; 
 
    readonly Rectangle? location; 
 
    readonly VertexPositionTexture[ vertices = new VertexPositionTexture[ 
    { 
        new VertexPositionTexture(new Vector3(-1f, -1f, 1f), new Vector2(0f, 1f)), 
        new VertexPositionTexture(new Vector3(-1f, 1f, 1f), new Vector2(0f, 0f)), 
        new VertexPositionTexture(new Vector3(1f, -1f, 1f), new Vector2(1f, 0f)), 
        new VertexPositionTexture(new Vector3(1f, 1f, 1f), new Vector2(1f, 1f)), 
    }; 
 
    public ScreenSpaceQuad( 
        ContentManager content, 
        GraphicsDevice device, 
        Texture2D texture) 
    { 
        location = null; 
        this.texture = texture; 
        load(content, device); 
    } 
 
    public ScreenSpaceQuad( 
        ContentManager content, 
        GraphicsDevice device, 
        Texture2D texture, 
        Rectangle location) 
    { 
        this.texture = texture; 
        this.location = location; 
        load(content, device); 
    } 
 
    void load(ContentManager content, GraphicsDevice device) 
    { 
        effect = content.Load<Effect>("ScreenSpaceQuad"); 
        vd = new VertexDeclaration(device, VertexPositionTexture.VertexElements); 
    } 
 
    public void Draw() 
    { 
        GraphicsDevice d = effect.GraphicsDevice; 
 
        d.VertexDeclaration = vd; 
 
        int sWidth = d.Viewport.Width; 
        int sHeight = d.Viewport.Height; 
 
        int tWidth = texture.Width; 
        int tHeight = texture.Height; 
 
        if (location.HasValue) 
        { 
            float x1 = (float)location.Value.X / sWidth; 
            float y1 = (float)location.Value.Bottom / sHeight; 
            float x2 = (float)location.Value.Right / sWidth; 
            float y2 = (float)location.Value.Y / sHeight; 
 
            x1 = x1 * 2f - 1f; 
            x2 = x2 * 2f - 1f; 
            y1 = y1 * 2f - 1f; 
            y2 = y2 * 2f - 1f; 
 
            vertices[0].Position = new Vector3(x1, y1, 1f); 
            vertices[1].Position = new Vector3(x1, y2, 1f); 
            vertices[2].Position = new Vector3(x2, y1, 1f); 
            vertices[3].Position = new Vector3(x2, y2, 1f); 
        } 
        else 
        { 
            float wScale = (float)tWidth / sWidth; 
            float hScale = (float)tHeight / sHeight; 
 
            float x = Math.Max(wScale, 1); 
            float y = Math.Max(hScale, 1); 
 
            vertices[1].Position = new Vector3(-1f, y, 1f); 
            vertices[2].Position = new Vector3(x, -1f, 1f); 
            vertices[3].Position = new Vector3(x, y, 1f); 
        } 
 
        effect.Parameters["DiffuseTexture"].SetValue(texture); 
 
        effect.Begin(); 
        foreach (EffectPass p in effect.CurrentTechnique.Passes) 
        { 
            p.Begin(); 
            d.DrawUserPrimitives(PrimitiveType.TriangleStrip, vertices, 0, 2); 
            p.End(); 
        } 
        effect.End(); 
    } 
 
    public void Dispose() 
    { 
        vd.Dispose(); 
        effect = null; 
        texture = null; 
    } 
}
So for my full screen quad, I call the first constructor, setting location to null and thus running the code path in Draw that should set up vertices for rendering in full screen. As a note here, I'm well aware there will be scaling issues if the texure is smaller than the viewport being that I don't scale equally on both axis, but since my current texture is larger than the viewport, that's not the current issue. The shader I load in the code above is this:

texture DiffuseTexture; 
 
sampler2D diffuseSampler = sampler_state 
{ 
    Texture = <DiffuseTexture>; 
    MinFilter = linear; 
    MagFilter = linear; 
    MipFilter = linear; 
    AddressU = wrap; 
    AddressV = wrap; 
}; 
 
struct VertexInput 
{ 
    float3 Position : POSITION0; 
    float2 TexCoord : TEXCOORD0; 
}; 
 
struct VertexOutput 
{ 
    float4 Position : POSITION0; 
    float2 TexCoord : TEXCOORD0; 
}; 
 
VertexOutput VertexShaderFunction(VertexInput input) 
{ 
    VertexOutput output = (VertexOutput)0; 
    output.Position = float4(input.Position, 1); 
    output.TexCoord = input.TexCoord; 
    return output; 
} 
 
float4 PixelShaderFunction(VertexOutput input) : COLOR0 
{ 
    return tex2D(diffuseSampler, input.TexCoord); 
} 
 
technique Technique1 
{ 
    pass Pass1 
    { 
        VertexShader = compile vs_1_1 VertexShaderFunction(); 
        PixelShader = compile ps_1_1 PixelShaderFunction(); 
    } 
} 
Yet, when I go to render, the image appears skewed. It's not just scaling improperly, but it appears as though the texturing is simply wrong and is being skewed (e.g. one horizontal line in the image winds up on an angle when rendered). Is there some trick to this that I'm missing?
Advertisement
Your texture coordinates appear to be incorrect. Shouldn't

readonly VertexPositionTexture[] vertices = new VertexPositionTexture[]{     new VertexPositionTexture(new Vector3(-1f, -1f, 1f), new Vector2(0f, 1f)),     new VertexPositionTexture(new Vector3(-1f, 1f, 1f), new Vector2(0f, 0f)),     new VertexPositionTexture(new Vector3(1f, -1f, 1f), new Vector2(1f, 0f)),     new VertexPositionTexture(new Vector3(1f, 1f, 1f), new Vector2(1f, 1f)), }; 


be

readonly VertexPositionTexture[] vertices = new VertexPositionTexture[]{     new VertexPositionTexture(new Vector3(-1f, -1f, 1f), new Vector2(0f, 0f)),     new VertexPositionTexture(new Vector3(-1f, 1f, 1f), new Vector2(0f, 1f)),     new VertexPositionTexture(new Vector3(1f, -1f, 1f), new Vector2(1f, 0f)),     new VertexPositionTexture(new Vector3(1f, 1f, 1f), new Vector2(1f, 1f)), }; 


?
Mike Popoloski | Journal | SlimDX
Actually you'd want the texture coordinates like this:

readonly VertexPositionTexture[] vertices = new VertexPositionTexture[]{     new VertexPositionTexture(new Vector3(-1f, -1f, 1f), new Vector2(0f, 1f)),     new VertexPositionTexture(new Vector3(-1f, 1f, 1f), new Vector2(0f, 0f)),     new VertexPositionTexture(new Vector3(1f, -1f, 1f), new Vector2(1f, 1f)),     new VertexPositionTexture(new Vector3(1f, 1f, 1f), new Vector2(1f, 0f)), }; [


Also...don't forget about this.
Ah, you're right. I was going to say something about the texels to pixels issue, but forgot to. The XNA SpriteBatch handles it in the shader.
Mike Popoloski | Journal | SlimDX
Quote:Original post by Mike.Popoloski
Ah, you're right. I was going to say something about the texels to pixels issue, but forgot to. The XNA SpriteBatch handles it in the shader.

From now I'm just pointing to Drillian's Understanding half-pixel and half-texel offsets. Looks like Nick needs it too :)
Sirob Yes.» - status: Work-O-Rama.
Quote:Original post by sirob
Quote:Original post by Mike.Popoloski
Ah, you're right. I was going to say something about the texels to pixels issue, but forgot to. The XNA SpriteBatch handles it in the shader.

From now I'm just pointing to Drillian's Understanding half-pixel and half-texel offsets. Looks like Nick needs it too :)


Ahh, that's a good one to add to my bookmarks. [smile]

I figured while I was home from work I'd post my own code for this, just for sake of completeness.

-Making the VB for the quad (I also store an in index for retrieving the location of the corresponding frustum corner, so don't worry about the third component of texCoordAndCornerIndex)
protected VertexBuffer CreateFullScreenQuad(VertexDeclaration vertexDeclaration){			    // Create a vertex buffer for the quad, and fill it in    VertexBuffer vertexBuffer = new VertexBuffer(graphicsDevice, typeof(QuadVertex), vertexDeclaration.GetVertexStrideSize(0) * 4, BufferUsage.None);    QuadVertex[] vbData = new QuadVertex[4];    // Upper right    vbData[0].position = new Vector3(1, 1, 1);    vbData[0].texCoordAndCornerIndex = new Vector3(1, 0, 1);    // Lower right    vbData[1].position = new Vector3(1, -1, 1);    vbData[1].texCoordAndCornerIndex = new Vector3(1, 1, 2);    // Upper left    vbData[2].position = new Vector3(-1, 1, 1);    vbData[2].texCoordAndCornerIndex = new Vector3(0, 0, 0);    // Lower left    vbData[3].position = new Vector3(-1, -1, 1);    vbData[3].texCoordAndCornerIndex = new Vector3(0, 1, 3);		    vertexBuffer.SetData(vbData);    return vertexBuffer;}


-Vertex shader (does proper screen/texel alignment)
void PostProcessVS (	in float3 in_vPositionOS		: POSITION,			in float3 in_vTexCoord			: TEXCOORD0,								out float4 out_vPositionCS		: POSITION,			out float2 out_vTexCoord		: TEXCOORD0,			out float3 out_vFrustumPlaneVS	: TEXCOORD1	){	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.xy;	out_vFrustumPlaneVS = g_vFrustumCornersVS[in_vTexCoord.z];}	

I can't imagine how it's a half-texel issue. While that is something I didn't take into account, that should just blur things a little. What I'm seeing is a distinct skewing of the texture, more so on one axis than the other.

Anyway, I was on a deadline and when I couldn't figure this out quickly I rearranged some code so that I could just use SpriteBatch and be done with it. But thanks for the help anyway.
Actually, the half-pixel part was the topic being sidetracked (because we spotted another issue in your code). The general understanding was simply that the texture coords were incorrect, though I guess you're the only one who can say if that was indeed the issue [smile].
Sirob Yes.» - status: Work-O-Rama.
While I know you've completed your project, I also wanted to point out that there is an Effect file containing the vertex and pixel shaders required to render sprites now available on the Creators Club website Here.

You'd still need to set up the data side of the equation and any render states you want to set, but that's a decent head-start for anyone else wanting a home-brew sprite implementation.

Cheers!
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

This topic is closed to new replies.

Advertisement