Game Development Community

Tough TGEA shader question... I need an expert

by Ves · in Torque Game Engine Advanced · 09/03/2007 (7:08 am) · 6 replies

Well I have searched an searched. I can't for the life of me reveal the secret of the mysterious way in which shaders are receiving thier Vertex input. It's easy enough to set a shader constant:
GFX->setVertexShaderConstF( VC_EYE_POS, (float*)&eyePos, 1 );
which when it gets to the shader ends up here
ConnectData main( VertData IN,
                  uniform float4x4 modelview       : register(C0),
                  uniform float4   inLightColor    : register(C25),
                  uniform float3   inLightVec      : register(C24),
 --->>         uniform float3   eyePos          : register(C20),
                  uniform float3   fogData         : register(C22),
                  uniform float4x4 objTrans        : register(C12)
)

My problem is the the "VertData IN", I can not for the life of me trace that down in code to where it is actually set. My best guess is that in some strange way when the shaders are created they are assigned a set of shader features (though I honestly thought shader features were only relivent to the generated shaders) and those features setup the Vertex shader inputs (pixel shader inputs are set by the vertex shader). In the case of this particular sharer (its a TGEA procedually generated shader that does bump, spec and fogging), this is the VertData struct:

struct VertData
{
   float2 texCoord        : TEXCOORD0;
   float2 lmCoord         : TEXCOORD1;
   float3 T               : TEXCOORD2;
   float3 B               : TEXCOORD3;
   float3 N               : TEXCOORD4;
   float3 normal          : NORMAL;
   float4 position        : POSITION;
};

Where is THIS data set and passed to the shader in code? There is no corresponding "setVertexShaderConstF" call for these variables. I would have thought there would be a place where "T" is set to TEXCOORD2 (as opposed to TEXCOORD0 or 1). It may be a simple FIFO queue that its placed in but even that "should" have GFX call I would imagine.

My lack of fully understanding how the shader layer is working is clearly showing through. But I can't find any good documentation on how the shader code is implemented (via c++). Any help that anyone can give would be great.

Ves

#1
09/03/2007 (8:52 am)
Its set in material.cpp and costummaterial.cpp in the update calls for them.
At least most of the variables are set there. But not with the names you have there in the VertData but set to the constants you find in shader const header file in the shader directory.
#2
09/03/2007 (9:54 am)
After looking some more I think I found the answer to my question. In "gfxStructs.h" it lists the Vertex input structure for various things. It's here that these variables are actually associated with the Vertex input data. What that means is that the vertex INPUT stucture is completely derived from the Vertex data itself. You actually have to change the Vertex data to add or remove anything from this structure. This makes much more sense now.

So say you wanted to add bumpmapping to atlas terrian, to do this you need to rewrite parts of the atlas save file to include the TBN vertex variables needed for that shader operation (which would greatly increase file size). Or I suppose you could precalculte them and store them in a tex (and do the math at the pixel shader level) but I don't think those values can be interpolated correctly via UVW coords. ICK...

Ves
#3
09/03/2007 (6:10 pm)
I had a go at adding bump mapping to Atlas on a shader level. Also, why would you need tangent, binormal, and normal information added to the Atlas file? Anyhow, here's the shader code. I'm experiencing some "interesting" issues and this shader is no where close to being done. Still, it does apply normal mapping to Atlas terrains.

AtlasDetailP
struct ConnectData
{
   float4 hpos      : POSITION;
   float2 detCoord  : TEXCOORD0;
   float3  fade     : TEXCOORD1;
   float3  Light	: TEXCOORD2;
};

struct Fragout
{
   float4 col : COLOR0;
};

Fragout main( ConnectData IN,
              uniform sampler2D detMap          : register(S0)
              )
{
   Fragout OUT;
   float4 col = {1,1,1,1};//tex2D(detMap, IN.detCoord);
   float4 diff = tex2D(detMap, IN.detCoord) * 2.0 - 1.0;
   diff.xyz = normalize(diff.xyz-0.5);
   float3 l = normalize(IN.Light);
   float Norm = saturate(dot(l,diff));
   float4 finalCol = col * Norm;
   
   OUT.col = lerp(float4(0.5,0.5,0.5,1), finalCol, IN.fade.x);
   return OUT;
}

AtlasDetailV
#define IN_HLSL
#include "../shdrConsts.h"
#include "atlas.h"
struct NormData
{

float3 Normal	:	NORMAL;
float3 Binormal :	BINORMAL;
float3 Tangent  :	TANGENT; 

};
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
VertDetailConnectData main( VertData IN,
				  NormData Norm,
                  uniform float4x4 modelView : register(C0),
                  uniform float3   lightVec  : register(VC_LIGHT_DIR1),
                  uniform float3   eyePos    : register(VC_EYE_POS),
                  uniform float    morphT    : register(C49),
                  uniform float3   detData   : register(C50)
                  )
{
   VertDetailConnectData OUT;

   // Apply morph calculations.
   float4 realPos = IN.position + (IN.morphCoord * morphT);
   realPos.w = 1;

   // Transform and return.
   OUT.hpos = mul(modelView, realPos);
   float3x3 tangentspace=float3x3(Norm.Tangent,Norm.Binormal,Norm.Normal);
   OUT.Light = -lightVec;
   OUT.Light = mul(tangentspace, OUT.Light);
 
   // Do the detail map calculations.
   float eyeDist = distance( IN.position, eyePos );
   OUT.detCoord = IN.texCoord * detData.z;                 // Amplify texcoords.
   OUT.fade.x     = 1.0 - (eyeDist - detData.x) * detData.y; // And fade with distance.
   OUT.fade.yz    = 0.f;
   
   return OUT;
}

Terrain_Water_Demo/client/scripts/shaders.cs Line 274
new ShaderData( AtlasShaderDetail )
{
   DXVertexShaderFile   = "shaders/atlas/atlasDetailV.hlsl";
   DXPixelShaderFile    = "shaders/atlas/atlasDetailP.hlsl";
   pixVersion = 2.0;
};

Atlas.h Line 45
struct VertDetailConnectData
{
   float4 hpos               : POSITION;
   float2 detCoord           : TEXCOORD0;
   float3  fade              : TEXCOORD1;
   float3  Light	: TEXCOORD2;
};
#4
09/06/2007 (10:53 am)
Also, why would you need tangent, binormal, and normal information added to the Atlas file?

How else would you get it to the Vertex shader? Those variables are dependant on the triangles of the mesh and they vary by vertex. Right now I am calculating my T variable at mesh load time but this is a slow process and atlas is constanly swaping chunks in and out of memory. B can be calculated in the vertex shader with a cross(N,T) but N and T must be calculated by vertex. In one of previous updates they added the normal variable to the atlas format so it already has that variable at the vertex level. How exactly are you passing those variables in?

VertDetailConnectData main( VertData IN,
                                               NormData Norm,
....

Where is the hook on the torque engine side to pass Norm into the vertex shader? Is there some extra GFX command to pass in extra data beyond what the vertex provides?? That would be very useful, some vertex data buffer that provides additional vertex data.....

I really would love to know how you got "NormData Norm" into the shader (from the engine), thats an important peice of knowledge for me :).

Ves
#5
12/14/2007 (2:46 pm)
Vertex shader input semantics:

typedef struct D3DVERTEXELEMENT9 {
WORD Stream;
WORD Offset;
BYTE Type;
BYTE Method;
BYTE Usage;
BYTE UsageIndex;
} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;

For example how is passed:

D3DVERTEXELEMENT9 declaration[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
{ 0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
{ 0, 36, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
D3DDECL_END()
};

Old semantic was passed using Floatible vertex flow (FVF).
#6
12/15/2007 (1:05 pm)
We're currently still using FVFs in TGEA (Flexible Vertex Format, actually). I'm not thrilled with FVFs because they have implied ordering of elements, and I personally find them not at all "flexible".

Here's a rough overview of how data is moved. I am sure there are better descriptions in DX docs.

Data is passed from the game engine to the vertex shader. This data is parallelized and processed on the GPU, and data is then passed from the vertex shader to the pixel shader. The pixel shader outputs data to the blend stage, which is then blended into the frame-buffer.

Data from the game engine is passed into the vertex shader via vertex streams, and vertex constants. A vertex constant is, exactly that, a constant. It is useful for things like lighting information, matrix data, and anything else that applies to the entire vertex stream(s). The vertex shader operates on the data, and outputs a structure to the pixel shader.

If you want to add a tangent-space matrix to Atlas to do bump mapping, you will need to change what vertex format Atlas uses to the PNTTBN vertex type, and fill in the data during the vertex uploads.

It's actually worth noting that the TGEA vertex, GFXVertexPNTTBN, can be trimmed down to 64-bytes instead of the 72-byte monstrosity that it is now. The normal is duplicated, and it doesn't need to be. I saw big performance wins on both GPU side and CPU side from changing this. It's not a bad change to make. Shadergen, TSMesh and Interiors need to be modified. It may take a few hours, but your vertex cache will love you for it.