Custom Shader on T3D?
by Alfio Saitta · in Torque 3D Professional · 04/19/2011 (10:02 am) · 37 replies
I'm trying since yesterday to import a shader into the T3D. But i always get an unexpected result. The shader is maded on RenderMonkey, and edited to adapt to the T3D. But i just can not get it to work.
I think the problem is relates to the matrixes. But i did not find documentation on how to import them.
The working shader:
The result:

Material Definition:
Shader Definiton:
Part of the PS:
Part of the VS:
I think the problem is relates to the matrixes. But i did not find documentation on how to import them.
The working shader:
The result:

Material Definition:
singleton CustomMaterial( SkinShaderMaterial )
{
mapTo = "Skin_defaultMat_Map-COL.jp";
sampler["DiffSampler"] = "art/shapes/SkinShader/Map-1024_COLOR";
sampler["NormalSampler"] = "art/shapes/SkinShader/Map-1024_NRM";
sampler["SpecSampler"] = "art/shapes/SkinShader/Map-1024_SPEC";
sampler["MicroSampler"] = "art/shapes/SkinShader/Default_MicroDetail_NRM";
sampler["TransSampler"] = "art/shapes/SkinShader/Map-1024_OCC";
shader = SkinShader;
//stateBlock = SkinStateBlock;
version = 3.0;
};Shader Definiton:
singleton ShaderData( SkinShader )
{
DXVertexShaderFile = "shaders/common/SkinV.hlsl";
DXPixelShaderFile = "shaders/common/SkinP.hlsl";
//OGLVertexShaderFile = "shaders/common/gl/SkinV.glsl";
//OGLPixelShaderFile = "shaders/common/gl/SkinP.glsl";
pixVersion = 3.0;
};
// DEFINED BUT DON'T USED ->
singleton GFXStateBlockData( SkinStateBlock )
{
samplersDefined = true;
samplerStates[0] = SamplerClampLinear;
samplerStates[1] = SamplerClampLinear;
samplerStates[2] = SamplerClampLinear;
samplerStates[3] = SamplerClampLinear;
samplerStates[4] = SamplerClampLinear;
//cullDefined = true;
//cullMode = "GFXCullNone";
};Part of the PS:
#define IN_HLSL
#include "shdrConsts.h"
#include "shadergen:/autogenConditioners.h"
#include "./torque.hlsl"
uniform sampler2D DiffSampler: register( S0 );
uniform sampler2D NormalSampler: register( S1 );
uniform sampler2D SpecSampler: register( S2 );
uniform sampler2D MicroSampler: register( S3 );
uniform sampler2D TransSampler: register( S4 );
uniform int UseMicroDetail = 1;
uniform int UseSpecColour = 0;
uniform float MicroScale = 55.0;
uniform float MicroFactor = 5.0;
uniform float SpecPower = 0.28;
uniform float SpecGloss = 4.5;
uniform float SpecFresnel = 3.4;
uniform float FresnelPower = 10.0;
uniform float FresnelGloss = 2.3;
uniform float TransMultiplier = 0.9;
uniform float TransRampOff = 0.4;
uniform float4 SpecColour = {0.45, 0.65, 1.00, 1.0};
uniform float4 TransColIn = {0.81, 0.91, 0.96, 1.0};
uniform float4 TransColOut = {1.0, 0.71, 0.32, 1.0};
uniform float4 TransColBack = {0.58, 0.20, 0.24, 1.0};
uniform float4 LightColour = {0.89, 0.89, 0.89, 1.0};
struct ConnectData
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float3 WorldNormal : TEXCOORD1;
float3 EyeVec : TEXCOORD2;
float3 LightVec : TEXCOORD3;
float3 WorldTangent : TEXCOORD4;
float3 WorldBinormal : TEXCOORD5;
};
....
float4 main( ConnectData IN ) : COLOR
{
....
}Part of the VS:
#include "shdrConsts.h"
#include "shadergen:/autogenConditioners.h"
uniform float4x4 matWorld;
uniform float4x4 matWorldInverseTranspose;
uniform float4x4 matWorldViewProjection;
uniform float4x4 matViewInverse;
uniform float DisplacementScale;
uniform float DisplacementBias;
uniform float3 LightPosition;
struct VertData
{
float4 Position : POSITION0;
float2 UV : TEXCOORD0;
float3 Normal : NORMAL;
float3 Tangent : TANGENT;
float3 Binormal : BINORMAL;
};
struct ConnectData
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 WorldNormal : TEXCOORD1;
float3 EyeVec : TEXCOORD2;
float3 LightVec : TEXCOORD3;
float3 WorldTangent : TEXCOORD4;
float3 WorldBinormal : TEXCOORD5;
};
ConnectData main( VertData Input )
{
ConnectData Output;
....
return Output;
}About the author
Recent Threads
#22
Also pay attention to the order of your parameters,since it does matter if it is a row or a column vector.
float3x3 TBN = {IN.T,IN.B,IN.N};
float4 worldVertexPos = mul( objTrans,IN.pos);
float3 LightVec = LightPos.xyz - worldVertexPos.xyz;
L = mul(L,TBN);
L = normalize(L);
Also check out these tutorials:
http://www.liman3d.com/tutorials.html
04/24/2011 (1:02 am)
Your light vector and your tangent vectors need to be in the same space. If these vectors are in object space and your lighting vector is in world space then you either need to transform your vectors into world space or transform your lighting vector into object space. I would transform the lighting vector since it is less intensive than translating 3 vectors.Also pay attention to the order of your parameters,since it does matter if it is a row or a column vector.
float3x3 TBN = {IN.T,IN.B,IN.N};
float4 worldVertexPos = mul( objTrans,IN.pos);
float3 LightVec = LightPos.xyz - worldVertexPos.xyz;
L = mul(L,TBN);
L = normalize(L);
Also check out these tutorials:
http://www.liman3d.com/tutorials.html
#23
As Alfio has found... its not a cut and paste job... it can often become a complete rewrite of the shader. In particular a skin shader, which is basically a different lighting algorithm, doesn't translate across to a deferred renderer which wants to unify lighting into a screen space calculation.
So your options are:
1. Not support deferred rendering on skin surfaces... only do forward rendering.
2. Develop a skin shader that can work with the light prepass buffer and deferred techniques.
In Alfio's case #1 might be his best bet. Make sure you set the 'forwardLit' property on the CustomMaterial and use the forward lighting shader constants (read shaderscommonlighting.hlsl).
The big problem... you'll get no shadows. Which when you have your character standing in a dark corner of the room with glowing skin... will be a problem.
To do #2 you need to really implement a deferred algorithm which requires actual knowledge of the light prepass technique and the math involved in skin shading.
T3D already has a deferred sub surface scattering feature meant for skin... its not that great, but i'd like to see us improve it.
The latest technique for doing skin shading does it as a post effect using stencil to mark the skin pixels on the screen. Like in Wolfgang's Rawk Demo.
04/24/2011 (2:19 pm)
Generally tools like FX Composer generate shaders which are either incompatible or impossible in a game engine. In particular when you try to make a forward rendered shader work in a deferred rendering engine like Torque you have lots of problems.As Alfio has found... its not a cut and paste job... it can often become a complete rewrite of the shader. In particular a skin shader, which is basically a different lighting algorithm, doesn't translate across to a deferred renderer which wants to unify lighting into a screen space calculation.
So your options are:
1. Not support deferred rendering on skin surfaces... only do forward rendering.
2. Develop a skin shader that can work with the light prepass buffer and deferred techniques.
In Alfio's case #1 might be his best bet. Make sure you set the 'forwardLit' property on the CustomMaterial and use the forward lighting shader constants (read shaderscommonlighting.hlsl).
The big problem... you'll get no shadows. Which when you have your character standing in a dark corner of the room with glowing skin... will be a problem.
To do #2 you need to really implement a deferred algorithm which requires actual knowledge of the light prepass technique and the math involved in skin shading.
T3D already has a deferred sub surface scattering feature meant for skin... its not that great, but i'd like to see us improve it.
The latest technique for doing skin shading does it as a post effect using stencil to mark the skin pixels on the screen. Like in Wolfgang's Rawk Demo.
#24

A solution quite concrete, concerns the use of lightinfo. But this requires a completely different approach, and in any case i do not see how to correlate with the authoring software like RenderMonkey.
or
Something similar to numLights, and a loop on every light, could solve the problem. But in any case would require indexing the lights and get into the hands really big array in complex scenes.
04/24/2011 (2:55 pm)
How Tom said, is truly complex. I think, i can create a template for RenderMonkey, but before i have yet to solve some problems. Currently, i solved part of the problem of the lights, and vectors. Now my shader receives the right light, at least until there is only one light in the scene. So i have to find a solution to manage multiple lights at once. Another problem, which is about normal, tangent, and binormal.
A solution quite concrete, concerns the use of lightinfo. But this requires a completely different approach, and in any case i do not see how to correlate with the authoring software like RenderMonkey.
or
Something similar to numLights, and a loop on every light, could solve the problem. But in any case would require indexing the lights and get into the hands really big array in complex scenes.
#25
NOTE: In this video i also used a PostFX shader for color and saturation correction.
04/24/2011 (8:53 pm)
Here are the latest results. Finally, the vectors seem to be nearly correct, although i have some problem with the TBN. Doing working in harmony DefferedRendering and calculated values from my functions, i was able to receive more light and to cast all the shadows correctly. Surely after i realize what it still does not work, but let's say that i have made some progress. :-PNOTE: In this video i also used a PostFX shader for color and saturation correction.
#26
04/24/2011 (9:18 pm)
So, you are going with #2 of Toms suggestion?
#27
Part of my PS:
04/24/2011 (9:41 pm)
Part of my VS:#define IN_HLSL
#include "shadergen:/autogenConditioners.h"
#include "hlslStructs.h"
#include "lighting.hlsl"
// Structures
struct ConnectData
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 EyeVec : TEXCOORD1;
float3 LightVec : TEXCOORD2;
float4 LightCol : TEXCOORD3;
float4 screenspacePos: TEXCOORD4;
float3 WorldNormal : NORMAL;
float3 WorldTangent : TANGENT;
float3 WorldBinormal : BINORMAL;
};
// Main
ConnectData main( VertexIn_PNTTTB IN,
uniform float4x4 modelview,
uniform float3 eyePos,
uniform float4x4 objTrans,
uniform float4 inLightPos[3],
uniform float4 inLightColor[4]
)
{
ConnectData OUT;
float3 WorldSpacePos = mul(objTrans, IN.pos).xyz;
// Calculate the Normal, Tangent, and Binormal
/*
OUT.WorldNormal = normalize( IN.normal );
float3 c1 = normalize(cross( OUT.WorldNormal, float3(0.0, 0.0, 1.0) ));
float3 c2 = cross( OUT.WorldNormal, float3(0.0, 1.0, 0.0) );
if( length(c1) > length(c2) ) {
OUT.WorldTangent = c1;
} else {
OUT.WorldTangent = c2;
}
OUT.WorldBinormal = normalize( cross(OUT.WorldNormal, OUT.WorldTangent) );
*/
OUT.WorldTangent = mul(objTrans, IN.T).xyz;
OUT.WorldBinormal = mul(objTrans, cross(IN.T, IN.normal)).xyz;
OUT.WorldNormal = mul(objTrans, IN.normal).xyz;
// Calculate the EyeVec
OUT.EyeVec = -(IN.pos.xyz - eyePos);
// Calculate the LightVector
float3 LightPosition = float3(inLightPos[0].x, inLightPos[1].x, inLightPos[2].x);
OUT.LightVec = LightPosition - WorldSpacePos;
// Calculate the LightColor
OUT.LightCol = 1; //float4(inLightColor[0].x, inLightColor[0].y, inLightColor[0].z, inLightColor[0].a);
// Texture Coordinate
OUT.TexCoord.xy = IN.uv0;
// Vertex Position
OUT.Position = mul(modelview, float4(IN.pos.xyz, 1));
// Deferred RT Lighting
OUT.screenspacePos = OUT.Position;
return OUT;
}Part of my PS:
#include "shadergen:/autogenConditioners.h"
#include "torque.hlsl"
uniform sampler DiffSampler: register( S0 );
uniform sampler NormalSampler: register( S1 );
uniform sampler SpecSampler: register( S2 );
uniform sampler lightInfoBuffer : register(S3);
uniform float4 rtParams3 : register(C0);
....
struct ConnectData
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 EyeVec : TEXCOORD1;
float3 LightVec : TEXCOORD2;
float4 LightColor : TEXCOORD3;
float4 screenspacePos: TEXCOORD4;
float3 WorldNormal : NORMAL;
float3 WorldTangent : TANGENT;
float3 WorldBinormal : BINORMAL;
};
....
float4 main( ConnectData IN ) : COLOR
{
float2 TexUV = IN.TexCoord.xy;
float3 normal = normalize(tex2D(NormalSampler, TexUV).xyz) * 2.0 - 1.0 ;
float3 WN = IN.WorldNormal;
float3 TN = IN.WorldTangent;
float3 BN = IN.WorldBinormal;
float3 N = (WN * normal.z * 2) + (normal.x * BN + normal.y * -TN);
N = normalize(N);
float3 EV = normalize(IN.EyeVec);
float3 LV = normalize(IN.LightVec.xyz);
float4 DotLN = dot(LV, N);
....
float4 BaseLighting = float4(tex2D(NormalSampler, TexUV), 1);
//---------------------------------------------------------------------------------
// Deferred RT Lighting
//---------------------------------------------------------------------------------
float2 uvScene = IN.screenspacePos.xy / IN.screenspacePos.w;
uvScene = ( uvScene + 1.0 ) / 2.0;
uvScene.y = 1.0 - uvScene.y;
uvScene = ( uvScene * rtParams5.zw ) + rtParams5.xy;
float3 d_lightcolor;
float d_NL_Att;
float d_specular;
lightinfoUncondition(tex2D(lightInfoBuffer, uvScene), d_lightcolor, d_NL_Att, d_specular);
//---------------------------------------------------------------------------------
// <- Deferred RT Lighting
//---------------------------------------------------------------------------------
Spec = float4(tex2D(SpecSampler, TexUV), 1) * d_specular;
float4 LightingOutput = BaseLighting * (float4(d_lightcolor, 1)) + Spec * d_NL_Att;
return hdrEncode(LightingOutput);
}
#28
Without MicroDetail:

With MicroDetail:

04/25/2011 (12:22 am)
Solved also the problem of Normal, Tangent, and Binormal on VS:....
ConnectData OUT;
float3 WorldSpacePos = mul(objTrans, IN.pos).xyz;
// Calculate the Normal, Tangent, and Binormal
OUT.WorldNormal = normalize( IN.normal );
float3 c1 = cross( OUT.WorldNormal, float3(0.0, 0.0, 1.0) );
float3 c2 = cross( OUT.WorldNormal, float3(0.0, 1.0, 0.0) );
if( length(c1) > length(c2) )
{
OUT.WorldTangent = c1;
} else {
OUT.WorldTangent = c2;
}
OUT.WorldBinormal = cross(IN.T, IN.normal).xyz;
// Calculate the EyeVec
OUT.EyeVec = -(IN.pos.xyz - eyePos);
....Without MicroDetail:

With MicroDetail:

#29
04/25/2011 (7:38 am)
This shader is looking awesome, I've been following it since the start of the thread and just wanted to say you doing a fantastic job!
#30
What'd be really nifty is if RenderMonkey had a toggle for forward/deferred rendering with lighting. Then you could generate and test either one before export. I know, pipedream....
04/25/2011 (8:49 am)
Great work man - keep it up!What'd be really nifty is if RenderMonkey had a toggle for forward/deferred rendering with lighting. Then you could generate and test either one before export. I know, pipedream....
#31

As you can see, the objects behind the CustomMaterial do not appear. And in their place you see the clouds.
Uncle Tom, can you explain why? XD
My StateBlock:
05/06/2011 (2:19 am)
Continuing the study of the CustomShader, i found another problem, at the moment unsurmountable for me. In practice, when i go to set the alpha channel into the PixelShader, i get this result:
As you can see, the objects behind the CustomMaterial do not appear. And in their place you see the clouds.
Uncle Tom, can you explain why? XD
My StateBlock:
singleton GFXStateBlockData( SimpleStateBlock )
{
blendDefined = true;
blendEnable = true;
blendSrc = GFXBlendSrcAlpha;
blendDest = GFXBlendInvSrcAlpha;
cullDefined = true;
cullMode = "GFXCullCCW";
};
#32
05/06/2011 (8:56 am)
You have to mark your material as translucent.
#33
05/07/2011 (2:04 pm)
@Ivan: Thanks Ivan. I thought that was not requested in a customshader.
#34
05/08/2011 (4:01 am)
Yes,translucent objects are rendered through the RenderTranslucentMgr bin,therefore you have to mark your material as translucent in order to add your render instance.
#35
05/08/2011 (5:37 pm)
Also note that when your object is marked as translucent that it is forward shaded... it will not get shadows or more than 4 lights.
#36
Now. I tried to set the definition of my shader like this:
But the shader does not receive the value, then it is not compiled.
05/14/2011 (3:01 am)
I think i have solved some of the problems i had concerning the CustomShader. Now i was wondering if i could somehow pass variables directly to my shader. Looking through the code of the various templates, i have noticed that some shaders include the keyword "DEFINES", and that in this way, the shader in question receives the value.singleton ShaderData( PFX_DOFBlurYShader )
{
DXVertexShaderFile = "shaders/common/postFx/dof/DOF_Gausian_V.hlsl";
DXPixelShaderFile = "shaders/common/postFx/dof/DOF_Gausian_P.hlsl";
pixVersion = 2.0;
defines = "BLUR_DIR=float2(0.0,1.0)";
};VertToPix main( PFXVert IN )
{
VertToPix OUT;
....
OUT.uv0 = IN.uv + ( ( BLUR_DIR * 3.5f ) / texSize0 );
OUT.uv1 = IN.uv + ( ( BLUR_DIR * 2.5f ) / texSize0 );
OUT.uv2 = IN.uv + ( ( BLUR_DIR * 1.5f ) / texSize0 );
OUT.uv3 = IN.uv + ( ( BLUR_DIR * 0.5f ) / texSize0 );
....
return OUT;
}Now. I tried to set the definition of my shader like this:
singleton ShaderData( SkinShader )
{
DXVertexShaderFile = "shaders/common/SkinV.hlsl";
DXPixelShaderFile = "shaders/common/SkinP.hlsl";
....
defines = "SpecPower=float(0.2)";
....
pixVersion = 3.0;
};But the shader does not receive the value, then it is not compiled.
#37
But in any case, the change of variable is not managed. So for the moment this method is useful just for set the vectors directly from the script.
05/14/2011 (6:34 am)
Ok, is SemiColons delimited =)$SkinShader::MicroScale = 50;
singleton ShaderData( SkinShader )
{
DXVertexShaderFile = "shaders/common/SkinV.hlsl";
DXPixelShaderFile = "shaders/common/SkinP.hlsl";
defines = "MicroScale=float(" @ $SkinShader::MicroScale @ ");MicroFactor=float(0.1);SpecPower=float(0.2)";
pixVersion = 3.0;
};But in any case, the change of variable is not managed. So for the moment this method is useful just for set the vectors directly from the script.
Torque Owner Alfio Saitta
Collateral Studios
After doing some other small improvements, i also recorded a video.
NOTE: If anyone knows how to import the vectors and matrixes regarding the lights (like LightVec, LightColor, LightPosition), please intervening.