Questions about shaders

Started by
2 comments, last by belfegor 11 years ago

OK, I've been trying to implement some simple shaders today, and I can't get it working. Here are my problems:

- Do fixed function things like SetRenderState, etc.. still work if I use a shader too? Say I want to enable specular lighting alongside my simple diffuse lighting shader - can't I just enable specular lighting via SetRenderState? Do I now have to do all this stuff with my shader? The same goes for transforms - do I still need to use SetTransform to make my objects appear in the right place, or should this be done in the shader?

- I don't really get vertex declarations - I've been using FVFs up until now, but they don't seem to be used for shaders. How do I know what I need to have in my vertex declaration? What happens if I don't provide the right declaration?

Thanks.

Advertisement

1. Enable maximum debug level, and check all functions that returns HRESULT

2. You don't need SetTransform, you pass transformation matrices to shader

3. Forget about D3DMATERIAL9, D3DLIGHT9, SetTextureStageState...

4. If you are using id3dxeffect interface you can also forget about SetRenderState since you set them in technique block:


technique myTech
{
    pass myPass0
    {
        VerttexShader = ...;
        PixelShader = ...;
       
        // this will call SetRenderState under the hood by id3dxeffect
        CULLMODE = CW;
        STENCILENABLE = TRUE;
        ALPHABLENDENABLE = FALSE;
        ...// and what not
    }
};

5.

...can't I just enable specular lighting via SetRenderState?

No, you are doing it "manually" since you wish to use shaders.

6

What happens if I don't provide the right declaration?

It may draw and it may not. There should be some error/warning in debug output (if you are using VS) about that (provided that you enabled some debug flags).

7.

How do I know what I need to have in my vertex declaration?

It depends on your need. What vertex elements does your geometry have?

OK, I'm still thinking in terms of fixed function pipeline, clearly... I now see exactly what you mean about setting the render state in the block - it looks like pretty much the same thing.

By doing it manually do you mean creating it from scratch in a shader, or setting the render state via a shader? I don't mind creating my own shader to do it - it sounds like a good challenge!

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? I'm still a little confused about that one.

Thanks for the great answer! You've actually just helped me to successfully implement my very first shader - a shadow mapping one.

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?

This topic is closed to new replies.

Advertisement