MRT in monogame, and depth buffer questions.

I’ve been having some shader trouble with my 2D platformer game. I should say that I am working in monogame windows Opengl. I’m trying to get Multiple Render Target rendering to work for a custom depth buffer implementation. Before I got that far, I was trying to get Multiple Render Targets to be rendered out at once. Thought I’d ask the monogame community for help with my implementation and suggestions for future depth buffer handling,

Here is my shader code for rendering to two render targets. The first render target samples the texture rendered to it, and the second overwrites every pixel in the drawn sample as white.

#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

sampler TextureSampler : register(s0);
float depth : SV_Depth;

struct PS_OUTPUT {
	float4 color : COLOR0;
	float4 depth : COLOR1;
};

PS_OUTPUT MainPS(float4 position : SV_Position, float4 color1 : COLOR0, float2 texCoord : TEXCOORD0)
{
	PS_OUTPUT output;
	output.color = tex2D(TextureSampler, texCoord)*color1;
	output.depth = float4(1,1,1,1);
	return output;
}


technique SpriteDrawing
{
	pass P0
	{
		PixelShader = compile PS_SHADERMODEL MainPS();
	}
};

Here is my initial implementation of my draw call to this shader:

//init function body    
RenderTarget2D colorTexture colorTexture = new RenderTarget2D(camera.graphicsDevice,
                                camera.drawSpace.Width,
                                camera.drawSpace.Height,
                                false,
                                SurfaceFormat.Color,
                                DepthFormat.Depth24,
                                1,
                                RenderTargetUsage.PreserveContents);

RenderTarget2D depthTexture = new RenderTarget2D(camera.graphicsDevice,
                                    camera.drawSpace.Width,
                                    camera.drawSpace.Height,
                                    false,
                                    SurfaceFormat.Color,
                                    DepthFormat.Depth24,
                                    1,
                                    RenderTargetUsage.PreserveContents);
Effect effect = EffectManager.Instance.effects[EffectEnum.DepthRead];

//Draw Function Body
graphics.SetRenderTargets(colorTexture, depthTexture);
graphics.Clear(Color.Transparent);
            
effect = EffectManager.Instance.effects[EffectEnum.DepthWrite];
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.AnisotropicClamp, DepthStencilState.None, null, effect, null);
spriteBatch.Draw(camera.debugTextures["debug_square"], new Vector2(200, 200), null, null, Vector2.Zero, 0f, new Vector2(1, 1), Color.White);
spriteBatch.End();

graphics.SetRenderTarget(null);
graphics.Clear(Color.Transparent);
SamplerState.AnisotropicClamp, DepthStencilState.Default, null, effect2, null);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.AnisotropicClamp, DepthStencilState.None, null, null, null);
spriteBatch.Draw(colorTexture, new Vector2(0, 0));
spriteBatch.End();

And here is the actual image being used as the ‘debug_square’ texture. It is actually just a red square.

Here is the result of the call.

You can see that the effect is associating the second render target register ‘depth’ as the first rendertarget ‘colorTexture’ in the ‘graphics.SetRenderTargets(colorTexture, depthTexture);’ call. My question is, what would I need to do in my shader to properly map the render targets? I would expect the draw call to render a red square, not the white texture that is supplied for the second render target.

if you can see where I am going with this implementation, my next question is “is this a good approach to a custom depth buffer?” I’m trying to implement depth textures with my existing game so I can render sprites in arbitrary order and still have the scene composite properly. Please post any insight into this, anything is appreciated.

I think you’re on a good path, this looks correct. Note that your second RenderTarget doesn’t have to have a depthbuffer, for MRTs only the depthbuffer of target1 is used.

I am personally using SetRenderTargets like this

´ _renderTargetAlbedo = new RenderTarget2D(_graphicsDevice, targetWidth,
targetHeight, false, SurfaceFormat.Color, DepthFormat.Depth24, 0, RenderTargetUsage.DiscardContents);

        _renderTargetNormal = new RenderTarget2D(_graphicsDevice, targetWidth,
            targetHeight, false, SurfaceFormat.HalfVector4, DepthFormat.None, 0, RenderTargetUsage.DiscardContents);

        _renderTargetDepth = new RenderTarget2D(_graphicsDevice, targetWidth,
            targetHeight, false, SurfaceFormat.Single, DepthFormat.None, 0, RenderTargetUsage.DiscardContents);

        _renderTargetBinding[0] = new RenderTargetBinding(_renderTargetAlbedo);
        _renderTargetBinding[1] = new RenderTargetBinding(_renderTargetNormal);
        _renderTargetBinding[2] = new RenderTargetBinding(_renderTargetDepth);

SetRenderTargets(RTBinding);

I can’t tell if this helps, but it’s working well for me. If you want to see the actual implementation you can check out my Deferred Engine Playground - download deferred engine which uses MRTs.

I am pretty sure that this works the same in OpenGL.

2 Likes

Wow this looks like a great place to start. Thanks for the quick response.

I appreciate your response but your suggestion actually made things worse haha.

renderTargetBindings = new RenderTargetBinding[2];
renderTargetBindings[0] = new RenderTargetBinding(colorTexture);
renderTargetBindings[1] = new RenderTargetBinding(depthTexture);
graphics.SetRenderTargets(renderTargetBindings);

I changed the way that I was binding the render targets to the graphics device. The above code was added before any draw calls were made, so I’m sure that the prior rendertargets are being properly bound. Now the screen is being drawn completely black. I think this may just be a bug with the windows version of monogame. Thoughts?

Update:
I vaguely remembered reading that the content profile for the content project must be set to ‘Hi-Def’ instead of ‘Reach’ so that monogame supported MRT. However this did not help. I will continue to search for a proper implementation of this technique using shaders.

Update2:
After about a week of reading up on monogame/xna mrt capabilities, it seems more worth the effort than its worth to uncover how this functionality works in practice. I’ve decided to work around my lack of understanding by making multiple render calls. Please update me if anybody finds a resource explaining this technique and its implementation in monogame. Thanks for your patience.

Oh no, sorry it is confusing, it should basically work without any issues :frowning:

The renderTargetBindings stuff is usually not done every frame but at the same time when you create the RTs, but it shouldn’t matter.

I’ve build a sample solution for you. It’s super small, 160kb i think, and implements MRT. I hope it helps you:)

You have to open the content pipeline and build it again, since we probably use different monogame versions.

And here is a version that uses just spritebatch, maybe it’s easier to understand for you :slight_smile:

1 Like

Thanks very much. I tried your spritebatch code and it definetly renders to multiple targets at once, and your content project isn’t even ‘HI-DEF’ so I’m not sure what is going on here. The problem must be with my code. I will compare your sample with my rendering code to get to the bottom of this. Thanks!

After playing around with the differences for a bit, I noticed that you are using Pixel Shader version 4.0 and I am using version 3.0. When I copied your effect code over to my shader and tried to change it to Pixel Shader 4.0, I get an effect shader error. We are both using monogame version 3.5 with a windows opengl template, and I can run your sample just fine. I’m not sure what is going on here, any ideas?

Update:
I think I am using an older template of monogame that utilized an opengl backend. I will create a shared project template and create a new windows project to test this theory. Thanks for your help so far, it is greatly appreciated.

No actually my solution is DirectX aka (default windows monogame solution), sorry I didn’t notice you use OpenGL. It’s also a newer monogame version, but I think nothing changed about MRTs since 3.5

@Jjagg @KonajuGames MRT should work with OpenGl, right?

Do you know if there is any way to see what the unexpected failure is? ‘Unexpected error’ is extremely unhelpful.

yes.
Download the monogame source code, built the DLLs and reference them in your project. Then you can trace inside the monogame source code when errors occur

I’m not sure, but it’s likely that it isn’t implemented :confused: I don’t think it’s implemented in develop so probably not in 3.5 either.

To get inside the Mg source I always add the relevant MG project to my solution and reference it.

I had a feeling this might be the case. Thanks for looking into it. I will work with multiple passes to handle depth map saving for now.

THANKS!!!.. i been stuck on something like this for days trying random stuff, hacking like a complete fool because i dont wanna dig deep into the sources of MG…Here is where i found the answer to my GL build. when i set the PreferredMultiSample count to 0 from 1 then the GL version of my shader finally works… phew… im uploading working shader samples so people dont have to go through what i just endured… in DX you can multisample, butnot GL, on render target . But in GL i think you can do MipMaps on them but i just wanna show the a very simple 2 texture sampling shader and its been a nightmare. Thanks. i think clear, simple, working , updated , net 6, netstandard, mg8 building , updated samples of very basic stuff any 2d game would need is lacking out there so im putting the samples that build and run in a public vault noone should spend 9 days to clip a polygon because they didnt base off a working sample . im just doing desktop DX, GL, and Android not consoles but im hoping my sample works on everything . i put it here https://github.com/damian-666/MGShadersXPlatform. in case anyone ends up here and it helps… finally my masking nightmare is over…