Game Development Community

CustomMat, Shader, RenderTarget interaction help

by Shawn Kennedy · in Torque 3D Professional · 02/04/2011 (8:24 am) · 20 replies

Hello,

I'm currently trying to apply a CustomMaterial to a custom object in T3D, in which I render to a texture (RenderTarget). I'm trying to expose this RenderTarget to my shader through the CustomMaterial, but I can't seem to get it working correctly. I'm scoured these forums for any resources / tutorials relating to CustomMaterial and Shader interactions, but haven't found much of use. Here are a couple snippets of code that I'm dealing with:

ShaderData and CustomMaterial
singleton ShaderData( MyShader )
{
   DXPixelShaderFile 	= "shaders/MyShader.hlsl";   
   
   pixVersion = 1.4;
};

singleton CustomMaterial( MyObject_material )
{
   mapTo = "_01___Default";
 	
   sampler["MyMap"] = "$MyTarget";
 	
   shader = MyShader ;
   
};

Shader
struct ConnectData
{
   float2 texCoord        : TEXCOORD0;
};


struct Fragout
{
   float4 col : COLOR0;
};


//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN, 
uniform sampler2D MyMap      : register(S0))
{
   Fragout OUT;

   OUT.col = tex2D(MyMap      ,IN.texCoord);
   return OUT;
}

And finally, RenderTarget initialization
mRenderTargets[0] = new PaintRenderTarget(this);

		if( !MatTextureTarget::registerTarget( "MyTarget", mRenderTargets[0]) ) {
			Con::errorf("Cannot assign MyTarget");
		}

		// Set up custom materials.
		CustomMaterial *customMat = NULL;
		Sim::findObject( "MyObject_material", customMat );
		if ( customMat )
		{
			mRenderTargets[0]->mOverrideMatInst = customMat->createMatInstance();
		}
		else
			mRenderTargets[0]->mOverrideMatInst = MATMGR->createMatInstance( "WarningMaterial" );

		mRenderTargets[0]->mOverrideMatInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>() );

As I said above, I haven't found any resources or tutorials on how to make a CustomMaterial, Shader, and RenderTargets interact, so I'm kinda poking around to try to find a solution at this point.

A couple of things I could use clarification on:

- How to expose the RenderTarget ("MyTarget") to the CustomMaterial, and in turn have that exposed to the Shader.
- How to properly associate the CustomMaterial to my object / render target (See the RenderTarget initialization above).

If anyone could even direct me to a resource or tutorial that could lead me to a solution, that would be great.

Any help is appreciated.

Thanks.

#1
02/04/2011 (8:33 am)
Oh and to clarify further, the "MyTarget" RenderTarget is being drawn to, and applied to a 2D GUI Bitmap control so I can see that it is actually being drawn to correctly. MyObject, however, is showing up all black.
#2
02/04/2011 (9:09 am)
Update: I found that my object was showing up as black because the texture wasn't being positioned correctly (was showing up at the world origin). Added in the vertex shader that now has it positioned correctly:

singleton ShaderData( MyShader )
{
   DXVertexShaderFile 	= "shaders/MyVShader.hlsl";
   DXPixelShaderFile 	= "shaders/MyPShader.hlsl";   
   
   pixVersion = 1.4;
};

singleton CustomMaterial( MyObject_material )
{
   mapTo = "_01___Default";
 	
   sampler["MyMap"] = "$MyTarget";
 	
   shader = MyShader ;
   
};

VShader
//*****************************************************************************
// Torque -- HLSL procedural shader
//*****************************************************************************

// Dependencies:
#include "shaders/common/lighting.hlsl"
#include "shaders/common/torque.hlsl"

struct VertData
{
   float3 position        : POSITION;
   float tangentW        : TEXCOORD3;
   float3 normal          : NORMAL;
   float3 T               : TANGENT;
   float2 texCoord        : TEXCOORD0;
};


struct ConnectData
{
   float4 hpos            : POSITION;
   float2 out_texCoord    : TEXCOORD0;
};


//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
ConnectData main( VertData IN,
                  uniform float4x4 modelview       : register(C0)
)
{
   ConnectData OUT;

   // Vert Position
   OUT.hpos = mul(modelview, float4(IN.position.xyz,1));
   
   // Base Texture
   OUT.out_texCoord = IN.texCoord;
   
   return OUT;
}

So now A texture is being applied to my object, but it isn't the RenderTarget texture that I wanted (the one I can see on the GUI control). It seems to be, instead, the texture being applied to the rocket launcher that the player is holding (I'm using the Empty Room Torque example at the moment).

Once again, any help would be greatly appreciated.
#3
02/04/2011 (12:18 pm)
The Gui controls use a bit different way for rendering.
For example GuiBitmapCtrl is using drawBitmapStretch().It setups a vertex buffer (4 verts),sets a texture using setTexture() and finally renders the control.
onRender() is called for each frame and what you need to do is just call setBitmap() and pass the new texture.

If you want to shade your control,drawBitmapStretch() is a good place to pass and process your shader.Keep in mind that you are shading just a plane.There are a few things you have to do.

1. You have to locate and handle (GFXShaderRef) your shader (MyShader)
2. You have to setup your shader inputs (modelview,..)
3. call setShader() at the bottom of drawBitmapStretch() before the draw call
#4
02/04/2011 (12:56 pm)
Thank you for your reply Ivan, but I think I may have mislead you. The drawing to the Gui control is working correctly. This is only being done so that I know that my render-to-texture is working properly. The problem lies in applying this render-to-texture to MyObject, which is a 3D object, derived from TSStatic. The RenderTarget initialization is happening in MyObject->onAdd().
#5
02/04/2011 (1:22 pm)
I see,I think MatTextureTarget (NamedTexTarget in beta3) can be used for this.
#6
02/04/2011 (1:29 pm)
Yeah, this is what I believe as well, but it seems I am still missing something. As you can see in the RenderTarget initialization code above, I am using MatTextureTarget.
#7
02/04/2011 (1:39 pm)
Interesting..try to replace $ with #
sampler["MyMap"] = "#MyTarget";
#8
02/04/2011 (1:44 pm)
I have tried the following:

sampler["MyMap"] = "MyTarget";
sampler["MyMap"] = "$MyTarget";
sampler["MyMap"] = "#MyTarget";
sampler["MyMap"] = "art/drop.png"; (just a simple texture I made)

texture[0] = "MyTarget";
texture[0] = "$MyTarget";
texture[0] = "#MyTarget";
texture[0] = "art/drop.png"; (this is the only one that actually applies a texture to MyObject, the texture being drop.png)

So I guess I'm supposed to be using the texture[x] syntax to specify my samplers for the shader, but I still can't figure out how to reference my render-to-texure...
#9
02/04/2011 (1:53 pm)
It seems your target somehow is not valid.

The correct syntax is:
sampler["MyMap"] = "#MyTarget";

I think texture[0] is correct for post effects only .
#10
02/04/2011 (1:57 pm)
Is it possible for the target to not be valid and yet draw correctly to a 2D Gui Control? Because I set the bitmap of a Gui Control to the TexHandle of the target, and it is indeed drawing correctly.
#11
02/04/2011 (2:03 pm)
MatTextureTargetRef target = MatTextureTarget::findTargetByName( "MyTarget" );
GFXTextureObject *tObject = target;
GFX->setTexture( 0, tObject );

Then sample using register S0.This should work.
#12
02/07/2011 (6:46 am)
I'm afraid this isn't working for me either Ivan. Can't convert MatTextureTargetRef to GFXTextureObject*.
#13
02/07/2011 (6:55 am)
Sorry about that,my fault.

GFXTextureObject *tObject = (target)? target->getTargetTexture(0): NULL;
#14
02/07/2011 (8:21 am)
Alright Ivan, it's compiling and running now, thank you. However, it didn't seem to solve the problem. I'm still only seeing the texture of my Rocket Launcher on MyObject. This is with:

sampler["MyMap"] = "#MyTarget";

in the CustomMaterial.

And:

uniform sampler2D DropMap : register(S0)

in the shader.
#15
02/07/2011 (8:42 am)
MyMap should be the same as the sampler definition,unless you do your own sampler redefinition in your shaderdata interface:
samplerNames[0] = "DropMap";

In your case you should just use:

uniform sampler2D MyMap;
#16
02/07/2011 (8:50 am)
Ah yes, sorry, that was simply a typo on my part.

sampler["MyMap"] = "#MyTarget";

in the CustomMaterial.

And:

uniform sampler2D MyMap: register(S0)

Is what I have. Do I need the samplerNames defined in the ShaderData, so that I can do sampler["Name"] in the CustomMaterial? I added the samplerNames, but still no change with the texture on MyObject.
#17
02/07/2011 (8:54 am)
You don't need to define samplerNames.
Your code works,you set your target,but it seems that it is reset with another one texture, somewhere in processedShaderMaterial or processedCustomMaterial.
If you cannot locate the problem,you could render your objects within a new separate bin.That way you will isolate these types of issues.
I actually have a resource about that,I did it for multiple glow support.
I can give it to you.
#18
02/07/2011 (9:09 am)
Yeah, I just tested changing the texture being applied to my Rocket Launcher and sure enough it changed MyObject as well. I'll see if I can't do some poking around and find out how the texture is being overridden.

Oh and any resource that may help me isolate the issue would be a tremendous help.
#19
02/07/2011 (9:14 am)
Shawn,give me an email address.
#20
02/07/2011 (9:16 am)
shawn.kennedy ((at)) vrsim.net