Problem with Multi Render Targets (MRT) and DirectX (HLSL)

arkangel2803

Newcomer
Hi all

Well i searched in forum and google and i didnt found any solution for my problem, i think that its a very little thing, but i cant found why my code with Multi Render Targets dosents works :(

I will try to make it as clear as i can. Im trying to output 3 values of float4 from my shader for my first time, but the only i gets is the background filled with the color of Clear(...) instruction.

I will shoy you some of my code to make it clear:

Render Target Creation:
-----------------------
Code:
	HRESULT hr;
	hr = D3DXCreateTexture( m_Device, 
				inWidth, //Hanchura
				inHeight, //Altura
				1, //mip levels
				D3DUSAGE_RENDERTARGET,
				m_ColorFormat,
				D3DPOOL_DEFAULT,
				&m_tTexture);
	if(hr != D3D_OK)
	{
		return (false);
	}

	hr = m_tTexture->GetSurfaceLevel(0, &m_Surface);
	assert (hr == D3D_OK);

        m_bUseDepthBuffer = ( m_StencilZBufferFormat != D3DFMT_UNKNOWN );
	hr = D3DXCreateRenderToSurface( m_Device,
					desc.Width, 
					desc.Height,
					desc.Format,
					m_bUseDepthBuffer,
					m_StencilZBufferFormat,
					&m_RenderToSurface
				       );
	return (hr == D3D_OK);


- inWidth and inHeight are initialized to backbuffer's size.
- The format of the pixel is D3DFMT_A8R8G8B8,D3DFMT_D24X8
- The format of the Backbuffer is D3DFMT_X8R8G8B8

Render Loop:
------------
Code:
	HRESULT Hr;
	IDirect3DSurface9 *BackBuffer;
	

	// Clear the backbuffer and the zbuffer
        m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(64,128,200), 1.0f, 0 );

	m_pD3DDevice->GetRenderTarget(0, &BackBuffer);
	m_pD3DDevice->SetRenderTarget(0,m_RT_LDR.GetSurface());
	m_pD3DDevice->SetRenderTarget(1,m_RT_HDR.GetSurface());
	m_pD3DDevice->SetRenderTarget(2,m_RT_Normal.GetSurface());

	// Begin the scene
       if( SUCCEEDED( m_pD3DDevice->BeginScene() ) )
       {
				
		FillRenderSlots();

		//Renderizado de mallas solidas (sin transparencias)
		m_pD3DDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
		m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,false);
	
		RenderSlots(inElapsedTime,&m_vRenderSlotsSolid);

		//Renderizado de mallas con alpha (con transparencias)
		m_pD3DDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
		m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
		m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
	       m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
		m_pD3DDevice->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_ADD);

		RenderSlots(inElapsedTime,&m_vRenderSlotsAlpha);

		m_HelpText.Render();

		//Renderizamos las lineas del HelperLine
		RenderLines();
	
        // End the scene
        m_pD3DDevice->EndScene();
    }
	m_pD3DDevice->SetRenderTarget(0, BackBuffer);
	m_pD3DDevice->SetRenderTarget(1,NULL);
	m_pD3DDevice->SetRenderTarget(2,NULL);

	m_RT_LDR.DumpSurface(0,0,m_iBackBufferWidth,m_iBackBufferHeight);
	m_RT_LDR.SaveToFile("RT_LDR");
	m_RT_HDR.SaveToFile("RT_HDR");
	m_RT_Normal.SaveToFile("RT_Normal");

	// Present the backbuffer contents to the display
    m_pD3DDevice->Present( NULL, NULL, NULL, NULL );



- RenderTarget.GetSurface() retunrs a pointer to the surface of this RT.
- I save in BackBuffer pointer the direction of the old Backbuffer to replace later again.
- RenderSlots(...) render the mesh, one for solid and another for transparent meshes
- When all meshes are rendered, i set again Backbuffer an put NULL on the other 2 slots
- RenderTarget.DumpSurface(...) takes the surface of the RT and makes a StretchRect on Backbuffer
- RenderTarget.SaveToFile(...) saves the surface in HD as a *.bmp

Shader code:
------------
Code:
struct RETURN_MRT
{
	float4 OutColor0	: COLOR0;
	float4 OutColor1	: COLOR1;
	float4 OutColor2	: COLOR2;
};

// ...

VS_OUTPUT RenderSceneVS	(
			  float4 inPos  : POSITION, 
			  float3 inNormal : NORMAL,
			  float2 inTex0 : TEXCOORD0,
			  float2 inTex1 : TEXCOORD1,
			  float3 inTangent : TEXCOORD2,
			  float3 inBinormal : TEXCOORD3
			)
{
	VS_OUTPUT output;
        
        // ...
        return output;
}

RETURN_MRT RenderScenePS(
			  float4 inTex0m10	: TEXCOORD0,
			  float4 inTexL1L2	: TEXCOORD1,
			  float4 inTexDistL1L2	: TEXCOORD2,
			  float3 pos_world	: TEXCOORD3,
			  float3 tangent	: TANGENT,
			  float3 norm		: NORMAL,
			  float3 binormal	: BINORMAL
			) : COLOR
{
	LIGHT_INFO i;
	RETURN_MRT Ret;

        //...

        Ret.OutColor0 = i.Result_LDR;
	Ret.OutColor1 = i.Result_HDR;
	Ret.OutColor2 = i.NormalPixel.xyzz;

        return (Ret);
}


- As you can see, i have a return structure declared, if i dont set any render target, i have a normal output of muy render, in other words, the color on COLOR0 variable is put on backbuffer.
- When in C++/DirectX code i set one or three surface to render on them, i only get a nice backbuffer filled with the clear color in Clear(...) fuction.

I think im missing something, any of you can help me to spot it ?

A lot of thanks for your time :)

LLORENS

P.D: If you need more information, let me know and i will post it
 
What are the formats of the three render targets you're outputting to? Unless you're using a DX10-class graphics card there is a very good chance that your GPU doesn't expose the "MRT independant bit depth" cap, in which case you won't be allowed to bind e.g. a R16G16B16A16F render target in slot 0 with a R8G8B8A8 in slot 1.
 
Hi

The format of 3 RT's is D3DFMT_A8R8G8B8
I was seeing an Nvidia deferred example, and they use this format for pixels of RT surface.

I dont know what is wrong :(

LLORENS
 
I just realized you have alpha blending enabled when rendering to your 3 MRTs; check whether the D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING cap is exposed in your graphic card (what graphic card are you using?). If not, then blending when more than 1 RT is bound will be undefined.
If the cap is set then the problem is elsewhere. First, disable RT 1 and 2 (only leave RT 0). Is something rendered onto it? I would also check your function that saves the content of a RT onto disk - maybe this is where the problem is. I suggest you use PIX to actually see what is being rendered onto your RTs.
 
Thx for your help SuperCow, im checking all you said to me, as fast as i can i will post the results.

Btw, my graphic card is Nvidia GForce 8800 GTX.
 
Hi again

I tested D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING cap and i have it enabled, so blending on multiple render targets its allowed.

Code:
	m_pD3DDevice->GetDeviceCaps(&m_DeviceCaps);

	if(!(m_DeviceCaps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING)) 
	{
		int a = 1; // Never enters here!
	}

I also dissabled RT 1 and 2, so i only have enabled RT 0, and i have again garbage in output, i noticed that if i dont execute any SetRenderTarget(...) function all is ok, but as fast as i put only one SetRenderTarget(...), i get garbage again.

Code:
void CCore::Render(float inElapsedTime)
{
	HRESULT Hr;
	IDirect3DSurface9 *BackBuffer;
	
	m_pD3DDevice->GetRenderTarget(0, &BackBuffer);
	m_pD3DDevice->GetDepthStencilSurface(&m_pDepth);
	
	// Begin the scene
    if( SUCCEEDED( m_pD3DDevice->BeginScene() ) )
    {
		m_pD3DDevice->SetRenderTarget(0,m_RT_LDR.GetSurface());
		//m_pD3DDevice->SetRenderTarget(1,m_RT_HDR.GetSurface());
		//m_pD3DDevice->SetRenderTarget(2,m_RT_Normal.GetSurface());
		//m_pD3DDevice->SetDepthStencilSurface(m_pDepth);
		
		// Clear the backbuffer and the zbuffer
		m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(64,128,200), 1.0f, 0 );

		FillRenderSlots();

		//Renderizado de mallas solidas (sin transparencias)
		m_pD3DDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
		m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,false);
	
		RenderSlots(inElapsedTime,&m_vRenderSlotsSolid);

		//Renderizado de mallas con alpha (con transparencias)
		m_pD3DDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
		m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
		m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
		m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
		m_pD3DDevice->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_ADD);

		RenderSlots(inElapsedTime,&m_vRenderSlotsAlpha);

		m_HelpText.Render();

		//Renderizamos las lineas del HelperLine
		RenderLines();
	
        // End the scene
        m_pD3DDevice->EndScene();
    }
	m_pD3DDevice->SetRenderTarget(0, BackBuffer);
	//m_pD3DDevice->SetRenderTarget(1,NULL);
	//m_pD3DDevice->SetRenderTarget(2,NULL);

	m_RT_LDR.DumpSurface(0,0,m_iBackBufferWidth,m_iBackBufferHeight);
	m_RT_LDR.SaveToFile("RT_LDR");
	m_RT_HDR.SaveToFile("RT_HDR");
	m_RT_Normal.SaveToFile("RT_Normal");

	// Present the backbuffer contents to the display
    m_pD3DDevice->Present( NULL, NULL, NULL, NULL );

}

And i also paste here the code for RT.SaveToFile(...) function and RT.DumpSurface(...) function, i can say that it works fine for me in another previous version of engine.

Code:
void CRenderTarget::DumpSurface( unsigned int inPosX1, unsigned int inPosY1,unsigned int inOffX2,unsigned int inOffY2 ) 
{
  // target rect for our surface
  RECT DstRect;
  DstRect.left = inPosX1;
  DstRect.top = inPosY1;
  DstRect.right = inPosX1 + inOffX2;
  DstRect.bottom = inPosY1 + inOffY2;				

  IDirect3DSurface9* backBuffer = NULL;

  // draw our our surface to screen
  if( !FAILED( m_Device->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer ) ) )
  {
    m_Device->StretchRect( m_Surface, NULL, backBuffer, &DstRect, D3DTEXF_NONE );
  }
  backBuffer->Release();
}

Code:
void CRenderTarget::SaveToFile(const char* inName)
{
	if(m_tTexture != NULL)
	{
		HRESULT hr;
		string sFullFilename = string( inName ) + ".bmp";
		hr = D3DXSaveTextureToFileA( sFullFilename.c_str(), D3DXIFF_BMP, m_tTexture, NULL );
	}
}
 
Back
Top