1.
By doing it manually do you mean creating it from scratch in a shader, or setting the render state via a shader?
Usually this is done in pixel shader by using (or even creating new) existing lightning algorithms (like these ones , you should be able to easily convert these to dx9).
2.
OK, I read that I can still use FVFs if they can cover my geometry's needs. So by 'What vertex elements does my geometry have' are you talking about how it's defined in the vertex buffer/mesh file, or what the input structs in the shaders have?
This depends on witch file format you are using for your meshes, and even on export options from modeling program that you used. And options you choose depends on what your shader needs for input.
This is not easy to explain. Normaly, there is this as some kind of common vertex elements you could find in a mesh:
D3DVERTEXELEMENT9 vertexElements[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
{0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()
};
hr = d3d9device->CreateVertexDeclaration(vertexElements, &vDeclaration);
if(FAILED(hr))
// handle fail
and in a shader your input struct then should be (to mach these):
struct VertexInput
{
float3 Position : POSITION;
float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0;
};
If you want to use bump-mapping (as most games do), you probably need these (if you don't have the export options for these in modeling program, you should generate this programatically (find some algo's by googling):
D3DVERTEXELEMENT9 vertexElements[] =
{
{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_TANGENT, 0},
{0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0},
{0, 48, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()
};
hr = d3d9device->CreateVertexDeclaration(vertexElements, &vDeclaration);
if(FAILED(hr))
// handle fail
and in a shader your vertex input struct then should be:
struct VertexInput
{
float3 Position : POSITION;
float3 Normal : NORMAL;
float3 Tangent : TANGENT;
float3 Binormal : BINORMAL;
float2 TexCoord0 : TEXCOORD0;
};
For example, for my grass i use instancing and input vertex looks like this:
D3DVERTEXELEMENT9 vertexElements[] =
{
{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_TANGENT, 0},
{0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0},
{0, 48, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
{1, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
{1, 16, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2},
{1, 32, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3},
{1, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4},
D3DDECL_END()
};
hr = d3d9device->CreateVertexDeclaration(vertexElements, &vDeclaration);
Last 4 texcoords actually stores transformation matrix for each instance. There is no "one way - best way", some people does it different based on their needs.
You could extract info for which vertex elements your mesh have programatically (cropped from my code. this is for 'X' file format meshes):
D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
hr = mesh->GetDeclaration(decl);
if(FAILED(hr))
// handle error
...
bool havePosition = false;
bool haveNormal = false;
bool haveTexCoord0 = false;
bool haveTexCoord1 = false;
bool haveTangent = false;
bool haveBinormal = false;
WORD offsetToPosition = 0;
WORD offsetToNormal = 0;
WORD offsetToTexCoord0 = 0;
WORD offsetToTexCoord1 = 0;
WORD offsetToTangent = 0;
WORD offsetToBinormal = 0;
for(UINT i = 0; i < MAX_FVF_DECL_SIZE; ++i)
{
// if end of declaration, exit loop
if(decl.Stream == 0xFF || decl.Stream != 0)
{
break;
}
if(decl.Usage == D3DDECLUSAGE_POSITION)
{
offsetToPosition = decl.Offset;
havePosition = true;
continue;
}
if(decl.Usage == D3DDECLUSAGE_NORMAL)
{
offsetToNormal = decl.Offset;
haveNormal = true;
continue;
}
if(decl.Usage == D3DDECLUSAGE_TEXCOORD && decl.UsageIndex == 0)
{
offsetToTexCoord0 = decl.Offset;
haveTexCoord0 = true;
continue;
}
if(decl.Usage == D3DDECLUSAGE_TEXCOORD && decl.UsageIndex == 1)
{
offsetToTexCoord1 = decl.Offset;
haveTexCoord1 = true;
continue;
}
if(decl.Usage == D3DDECLUSAGE_TANGENT)
{
offsetToTangent = decl.Offset;
haveTangent = true;
continue;
}
if(decl.Usage == D3DDECLUSAGE_BINORMAL)
{
offsetToBinormal = decl.Offset;
haveBinormal = true;
continue;
}
}
...
I just checked for elements that i need, you could check for others and even for their sizes (there is no rule that for example TEXCOORD be float2, it could be float4...). You get the idea?