Game Development Community

dev|Pro Game Development Curriculum

Adding shaders to a TSE class..or shader enhanced particles

by Kevin Johnson · 10/16/2005 (12:32 am) · 9 comments

Adding a shader to an arbitrary TSE class

The shader we will be using in this example is from the "A Basic Shader" tutorial with a small change.


in the Vertex shader, remove the uniform param :
uniform float4x4 texMat          : register(VC_TEX_TRANS1)
we dont need it.

and change:

//set base texture coord   
Out.outTexCoord = mul(texMat, In.texCoord);
to :
//set base texture coord   
Out.outTexCoord = In.texCoord;
We'll keep the pixel shader the same.

Dont forget to add the shader def to shaders.cs ala:
new ShaderData( redShader )
{
   DXVertexShaderFile    = "shaders/redShaderV.hlsl";
   DXPixelShaderFile    = "shaders/redShaderP.hlsl";
   pixVersion = 1.1;
};

now compile your shader using fxc.exe or just run TSE and check for any errors/mistypings.


If all goes well you should be ready to dig in...


Digging in
Now that we have our test shader and have verified it compiles open up our arbitrary class, in this case ParticleEmitter

dig down to game/fx/ParticleEmitter.h

first we need to include shaderdata
~line 16 add:
//Shader
#ifndef _SHADERDATA_H_
#include "materials/shaderData.h"
#endif


define our variables
~line 70 add:
ShaderData        *effectShader;
   const char        *effectShaderName;


add a forward decl to our function that actually sets up the shader each pass
~line 168 add:
// shader enhancements
   void addShader();
(more on this function later)


on to particleEmitter.cpp

include shader constants
// Shader stuff
#include "../../example/shaders/shdrConsts.h"

in the PartilceEmitterData constructor, ParticleEmitterData::ParticleEmitterData() (~line 54) add our defaults
effectShaderName = NULL;
This is very important because we dont want the game to crash if we do not have a shader defined in the datablock.


Next stop ..adding hooks to ParticleEmitterData::initPersistFields
(~line 89)
// Shader stuff
   addField("shader", TypeString,  Offset(effectShaderName, ParticleEmitterData));


on to ParticleEmitterData::preload() (~line 290) add:

// Shader stuff

if( effectShaderName) 
    effectShader = static_cast<ShaderData*>(Sim::findObject(effectShaderName ) );

This is where we find our shader and assign it


Im going to take a little time and explain how shaders are implimented (or at least attempt to)
If you already know this you can skip this part..

Shaders do their magic in the render loop, they are called just before the draw calls..
If the class we were trying to put our shader in, used, for example primbuilder,
we would call our shader function before the PrimBuild::begin call. Most often in Torque this occurs in the RenderObject function.

Shaders offer us an easy interface to the way things are drawn. We just need to set up the proper registers and continue with our drawing.


so.. in ParticleEmitter::renderObject (~line 514)

just before our GFX->drawIndexedPrimitive call we add :
if(mDataBlock->effectShaderName) addShader();

the last addition to be made is our addShader() call.
(~line 1121) add
void ParticleEmitter::addShader()
 {
 
 
 //Setup Test Shader
   MatrixF proj = GFX->getProjectionMatrix();
   MatrixF world = GFX->getWorldMatrix();
   proj.mul(world);
   proj.transpose();
   GFX->setVertexShaderConstF( VC_WORLD_PROJ, (float*)&proj, 4 );
   mDataBlock->effectShader->shader->process(); 
 }

Of primary importance to our vertext shader is the register VC_WORLD_PROJ. IN CG you would call this the modelViewProj, and we need it in order to transform our position from object space to view space, and is defined here as the product of proj and world matrices. After we have transformed it we load it into the shader using :
GFX->setVertexShaderConstF( VC_WORLD_PROJ, (float*)&proj, 4 );
Luckily, TSE has already loaded our texture in the texture sampler (S0) register, so
one last stop and were ready to compile and enjoy shader enhanced particles..

in the datablock for our class/particle add:
shader = "RedShader";

Extending the basic idea

If we wanted to extend this a little (and i hope you will) and wanted to specify some other parameter to our shader, perhaps color, we could add the folowing line to our addShader function:
GFX->setPixelShaderConstF( PC_USERDEF1,(float*)&newColor,4);

Then access it in the pixel shader using PC_USERDEF1

easy as pie...

#1
11/20/2005 (1:33 pm)
Thanks nice resource.
#2
05/08/2006 (7:52 am)
This is a great resouce. It helps to understand shaders in the engine more.
#3
08/03/2006 (6:57 am)
Have they updated the particles? Because I'm not seeing ParticleEmitter::renderObject...
#4
09/18/2006 (8:21 pm)
Hi Kevin,

i can't find (ParticleEmitter::renderObject) anywhere in MS4 has it been taken out?
#5
10/15/2006 (9:48 pm)
help? PLEASE !! lol
#6
03/15/2007 (5:30 am)
So do we have an answer for this yet? :)
#7
10/25/2007 (2:27 pm)
lol nope
#8
12/28/2007 (5:21 pm)
renderObject was never in TGEA. It might have been in TSE as a leftover from TGE because it was originally in the particleEngine.cc file.

Has anyone tried to use this in prepRenderImage?
#9
12/04/2008 (8:25 am)
hi , i am have using tgea 1.7 .
how can i impliment this resource ?
please teach this with tgea 1.7 .