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 :
and change:
Dont forget to add the shader def to shaders.cs ala:
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:
define our variables
~line 70 add:
add a forward decl to our function that actually sets up the shader each pass
~line 168 add:
on to particleEmitter.cpp
include shader constants
in the PartilceEmitterData constructor, ParticleEmitterData::ParticleEmitterData() (~line 54) add our defaults
Next stop ..adding hooks to ParticleEmitterData::initPersistFields
(~line 89)
on to ParticleEmitterData::preload() (~line 290) add:
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 :
the last addition to be made is our addShader() call.
(~line 1121) add
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 :
one last stop and were ready to compile and enjoy shader enhanced particles..
in the datablock for our class/particle add:
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:
Then access it in the pixel shader using PC_USERDEF1
easy as pie...
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...
About the author
#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
i can't find (ParticleEmitter::renderObject) anywhere in MS4 has it been taken out?
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
Has anyone tried to use this in prepRenderImage?
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
how can i impliment this resource ?
please teach this with tgea 1.7 .
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 .

Torque Owner Vashner