0
\$\begingroup\$

I was working through an older tutorial listed here, and got it to work for the most part. The RenderTarget for Depth is colored red for some reason, and I've been scratching my head trying to figure out why.

I tried clearing the screen another way, which at least painted the other render targets white instead of black. Also, this code uses the cross platform dlls (OpenGL), but the very same code fails with a strange error with windows only dlls.

Here are some screenshots: http://imgur.com/a/LolBx

Tutorial link: http://www.catalinzima.com/xna/tutorials/deferred-rendering-in-xna/creating-the-g-buffer/

Any help appreciated.

Below is the code I am using, which is almost exactly like the tutorial:

public class Game1 : Game
{
    private GraphicsDeviceManager graphics;
    private SpriteBatch spriteBatch;

    private Model model;
    private Texture2D texture;
    private float angle = 0f;

    private Vector3 FocusPoint;
    private Vector3 CameraOffset;
    private Matrix RotationMatrix;
    private Vector3 RotationOffset;

    private Matrix World;
    private Matrix View;
    private Matrix Projection;

    private Effect ClearGBufferEffect;
    private Effect GBufferEffect;
    private QuadRenderComponent QuadRenderer;
    private RenderTarget2D ColorRenderTarget;
    private RenderTarget2D NormalRenderTarget;
    private RenderTarget2D DepthRenderTarget;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content"; 
    }

    protected override void Initialize()
    {
        // quad renderer
        QuadRenderer = new QuadRenderComponent(this);
        Components.Add(QuadRenderer);

        // world view projection
        FocusPoint = Vector3.Zero;
        CameraOffset = new Vector3(0f, 2f, 2f);
        RotationMatrix = Matrix.CreateRotationY(MathHelper.PiOver4);
        RotationOffset = Vector3.Transform(CameraOffset, RotationMatrix);
        World = Matrix.CreateScale(new Vector3(1f, 1f, 1f)) * Matrix.CreateTranslation(new Vector3(0, 0, 0));
        View = Matrix.CreateLookAt(RotationOffset, FocusPoint, Vector3.Up);
        Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 480f / 480f, 0.1f, 1000f);

        // color normal depth
        int backBufferWidth = GraphicsDevice.PresentationParameters.BackBufferWidth;
        int backBufferHeight = GraphicsDevice.PresentationParameters.BackBufferHeight;
        ColorRenderTarget = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);
        NormalRenderTarget = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);
        DepthRenderTarget = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Single, DepthFormat.Depth24Stencil8);

        base.Initialize();
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);

        // model texture gbuffer
        model = Content.Load<Model>("Tile5");
        texture = Content.Load<Texture2D>("Red");
        ClearGBufferEffect = Content.Load<Effect>("ClearGBuffer");
        GBufferEffect = Content.Load<Effect>("GBuffer");
    }

    protected override void UnloadContent() { }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        // simple rotation update
        angle += 1f;
        RotationOffset = Vector3.Transform(CameraOffset, Matrix.CreateRotationY(MathHelper.ToRadians(angle)));
        View = Matrix.CreateLookAt(RotationOffset, FocusPoint, Vector3.Up);

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        // set render targets
        GraphicsDevice.SetRenderTargets(ColorRenderTarget, NormalRenderTarget, DepthRenderTarget);

        // clear everything
        // ClearGBufferEffect.CurrentTechnique.Passes[0].Apply();
        // QuadRenderer.Render(Vector2.One * -1, Vector2.One);
        GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);

        // render options
        GraphicsDevice.DepthStencilState = DepthStencilState.Default;
        GraphicsDevice.BlendState = BlendState.Opaque;
        GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

        // set gbuffer params
        GBufferEffect.Parameters["World"].SetValue(World);
        GBufferEffect.Parameters["View"].SetValue(View);
        GBufferEffect.Parameters["Projection"].SetValue(Projection);
        GBufferEffect.Parameters["Texture"].SetValue(texture);

        // render simple mesh
        GBufferEffect.CurrentTechnique.Passes[0].Apply();
        foreach (var mesh in model.Meshes)
        {
            foreach (var part in mesh.MeshParts)
            {
                GraphicsDevice.SetVertexBuffer(part.VertexBuffer);
                GraphicsDevice.Indices = part.IndexBuffer;
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.PrimitiveCount);
            }
        }

        // unset render targets
        GraphicsDevice.SetRenderTargets(null);

        // render each target to one 4th of the screen
        int hw = GraphicsDevice.Viewport.Width / 2;
        int hh = GraphicsDevice.Viewport.Height / 2;
        spriteBatch.Begin();
        spriteBatch.Draw(ColorRenderTarget, new Rectangle(0, 0, hw, hh), Color.White);
        spriteBatch.Draw(NormalRenderTarget, new Rectangle(0, hh, hw, hh), Color.White);
        spriteBatch.Draw(DepthRenderTarget, new Rectangle(hw, 0, hw, hh), Color.White);
        spriteBatch.End();

        base.Draw(gameTime);
    }
}

public class QuadRenderComponent : DrawableGameComponent
{
    //private VertexDeclaration vertexDecl = null;
    private VertexPositionTexture[] verts = null;
    private short[] ib = null;

    public QuadRenderComponent(Game game)
        : base(game) { }

    protected override void LoadContent()
    {
        IGraphicsDeviceService graphicsService =
            (IGraphicsDeviceService)Game.Services.GetService(
                                        typeof(IGraphicsDeviceService));

        verts = new VertexPositionTexture[]
                {
                        new VertexPositionTexture(
                            new Vector3(0,0,1),
                            new Vector2(1,1)),
                        new VertexPositionTexture(
                            new Vector3(0,0,1),
                            new Vector2(0,1)),
                        new VertexPositionTexture(
                            new Vector3(0,0,1),
                            new Vector2(0,0)),
                        new VertexPositionTexture(
                            new Vector3(0,0,1),
                            new Vector2(1,0))
                };

        ib = new short[] { 0, 1, 2, 2, 3, 0 };

    }

    public void Render(Vector2 v1, Vector2 v2)
    {
        IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)
            base.Game.Services.GetService(typeof(IGraphicsDeviceService));

        GraphicsDevice device = graphicsService.GraphicsDevice;

        verts[0].Position.X = v2.X;
        verts[0].Position.Y = v1.Y;

        verts[1].Position.X = v1.X;
        verts[1].Position.Y = v1.Y;

        verts[2].Position.X = v1.X;
        verts[2].Position.Y = v2.Y;

        verts[3].Position.X = v2.X;
        verts[3].Position.Y = v2.Y;

        device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, verts, 0, 4, ib, 0, 2);
    }
}

Here is the ClearGBufferEffect:

    #if OPENGL
    #define SV_POSITION POSITION
    #define VS_SHADERMODEL vs_3_0
    #define PS_SHADERMODEL ps_3_0
#else
    #define VS_SHADERMODEL vs_4_0_level_9_1
    #define PS_SHADERMODEL ps_4_0_level_9_1
#endif

struct VertexShaderInput
{
    float3 Position : SV_POSITION;
};

struct VertexShaderOutput
{
    float4 Position : SV_POSITION;
};

struct PixelShaderOutput
{
    float4 Color : COLOR0;
    float4 Normal : COLOR1;
    float4 Depth : COLOR2;
};

VertexShaderOutput MainVS(in VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

    output.Position = float4(input.Position, 1);

    return output;
}

PixelShaderOutput MainPS(VertexShaderOutput input)
{
    PixelShaderOutput output = (PixelShaderOutput)0;

    output.Color.rgb = 0.0f;
    output.Color.a = 0.0f;

    output.Normal.rgb = 0.5f;
    output.Normal.a = 0.0f;

    output.Depth = 1.0f;

    return output;
}

technique T0
{
    pass P0
    {
        VertexShader = compile VS_SHADERMODEL MainVS();
        PixelShader = compile PS_SHADERMODEL MainPS();
    }
};

Here is the GBufferEffect:

    #if OPENGL
#define SV_POSITION POSITION
#define VS_SHADERMODEL vs_3_0
#define PS_SHADERMODEL ps_3_0
#else
#define VS_SHADERMODEL vs_4_0_level_9_1
#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

matrix World;
matrix View;
matrix Projection;
float SpecularIntensity = 0.8f;
float SpecularPower = 0.5f;

texture Texture;
sampler DiffuseSampler = sampler_state
{
    Texture = (Texture);
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
    MIPFILTER = LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

struct VertexShaderInput
{
    float4 Position : SV_POSITION;
    float3 Normal : NORMAL0;
    float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : SV_POSITION;
    float2 TexCoord : TEXCOORD0;
    float3 Normal : TEXCOORD1;
    float2 Depth : TEXCOORD2;
};

struct PixelShaderOutput
{
    half4 Color : COLOR0;
    half4 Normal : COLOR1;
    half4 Depth : COLOR2;
};

VertexShaderOutput MainVS(in VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

    output.TexCoord = input.TexCoord;

    output.Normal = mul(input.Normal, World);

    output.Depth = output.Position.zw;

    return output;
}

PixelShaderOutput MainPS(VertexShaderOutput input)
{
    PixelShaderOutput output = (PixelShaderOutput)0;

    output.Color.rgb = tex2D(DiffuseSampler, input.TexCoord);
    output.Color.a = SpecularIntensity;

    output.Normal.rgb = 0.5f * (normalize(input.Normal) + 1.0f);
    output.Normal.a = SpecularPower;

    output.Depth = input.Depth.x / input.Depth.y;

    return output;
}

technique T0
{
    pass P0
    {
        VertexShader = compile VS_SHADERMODEL MainVS();
        PixelShader = compile PS_SHADERMODEL MainPS();
    }
};
\$\endgroup\$
2
\$\begingroup\$

You use a SurfaceFormat.Single type. This means your texture holds only one value per pixel instead of 4 (rgba). This is perfectly fine for a depth buffer as you only need to record the depth value.

When you render that texture, the spriteBatch expects a Texture2D: the RenderTarget is cast to the Texture2D. With only one value it converts to 1,0,0 hence it shows up as red.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.