Drawing Indexed Meshes w/ Vertex Shaders

Started by
24 comments, last by mike_ix 17 years, 9 months ago
Quote:Also, why are you releasing the mesh in your render function? That makes no sense. You should release it when you are done with it (like when the program exits).



The code I posted is a mish-mash of snipets from tutorials I've read. Sorry if it didn't make sense. But I think I understand now. So better code would be:

bool MyWorldObj::RenderMesh(LPD3DXMESH rmesh, int numMaterials, LPDIRECT3DTEXTURE9 meshTextures[], D3DMATERIAL9 meshMaterials[], int wire) {	if (wire > 0) {		g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, 2);	}	for (DWORD i=0; i<numMaterials; i++)	{		g_pd3dDevice->SetVertexShader( g_pVertexShader );		g_pd3dDevice->SetMaterial(&meshMaterials);		g_pd3dDevice->SetTexture(0,meshTextures);		g_pd3dDevice->SetPixelShader( g_pPixelShader );		rmesh->DrawSubset( i );		g_pd3dDevice->SetVertexShader( NULL );                g_pd3dDevice->SetPixelShader( NULL );	}	return true;}



And to have more control and flexibilty with my vertex data in the vertex shader it is best to avoid the constraints of the FVF format and just use a vertx declaration. I think I'm getting it. I also found this discussion on implementing the vertex declaration very informative.
Advertisement
OK, so understanding it and implementing it are two different things. I'm not sure what I'm doing wrong, but this code is throwing errors at runtime. So far, this is how I'm initializing my shader:

void MyWorldObj::initShader( void ){	D3DVERTEXELEMENT9 declaration[] =    {        { 0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },		{ 0, 12, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,	 0 },        { 0, 24, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0 },        { 0, 40, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },        D3DDECL_END()    };	g_pd3dDevice->CreateVertexDeclaration(declaration, &g_pVertexDeclaration);    		HRESULT hr;    LPD3DXBUFFER pCode;    DWORD dwShaderFlags = 0;	LPD3DXBUFFER pBufferErrors = NULL;    // Assemble the vertex shader from the file    hr = D3DXCompileShaderFromFile( "vertex_shader.vsh", NULL, NULL, "main",                                    "vs_1_1", dwShaderFlags, &pCode,                                    &pBufferErrors, &g_pConstantTableVS );    if( FAILED(hr) )	{		LPVOID pCompilErrors = pBufferErrors->GetBufferPointer();		MessageBox(NULL, (const char*)pCompilErrors, "Vertex Shader Compile Error",			MB_OK|MB_ICONEXCLAMATION);	}    // Create the vertex shader    g_pd3dDevice->CreateVertexShader( (DWORD*)pCode->GetBufferPointer(),                                     &g_pVertexShader );    pCode->Release();    }



And this is the HLSL vertex_shader.vsh file that is loaded by D3DXCompileShaderFromFile:

float4x4 worldViewProj;struct VS_INPUT{	float3 position  : POSITION0;	float3 normal   : NORMAL0;	float4 color    : COLOR0;	float2 texcoord : TEXCOORD0;};struct VS_OUTPUT{	float4 hposition : POSITION0;	float3 normal   : NORMAL0;	float4 color    : COLOR0;	float2 texcoord : TEXCOORD0;};VS_OUTPUT main( VS_INPUT IN ){	VS_OUTPUT vOut;	float4 v = float4( IN.position.x,		               IN.position.y,					   IN.position.z,					   1.0f );    vOut.hposition = mul( v, worldViewProj );    vOut.normal	= IN.normal;    vOut.color    = IN.color;    vOut.texcoord = IN.texcoord;    return vOut;}


So far, as far as my understanding, everything should work fine, and the code does compile without errors. Unfortunately D3DXCompileShaderFromFile HRESULT fails with the error: "invalid output semantics 'NORMAL0'" and VS.NET debugger breaks on g_pd3dDevice->CreateVertexShader. What am I missing here? Is the pixel shader neccessary to run without error? From my understanding, this vertex shader is the simplest shader that can be written in HLSL and should just output everything input to it plus transform world coordinates to screen coordinates and thus take over the pipeline. Why can't I implement it? Ho-hum,

I do thank everyone who's given their time to this! As soon as I get this running, I'll be sure to post the working code so everyone can understand how to do this. There just aren't enough practical tutorials available for "putting it all together" when it comes to shaders. So, once again, thank you!
OK, so I found this discussion that states that pixel shaders cannot receive INPUT from the vertex shaders OUTPUT that has the semantic "NORMAL", so I changed my code so that the .vsh file has two TEXCOORD semantics as suggested and BAM, the darn thing runs. Only, none of my meshes are being rendered! OH, do I have the vertex shader blues! Here's my .vsh file now (some of the other names have changed as well during my effort to trouble shoot):

float4x4 worldViewProj;struct VS_INPUT{	float3 iposition  : POSITION;	float3 inormal   : NORMAL;	float4 icolor    : COLOR0;	float2 itexcoord : TEXCOORD1;};struct VS_OUTPUT{	float4 hposition : POSITION;	float3 hnormal   : TEXCOORD0;	float4 hcolor    : COLOR0;	float2 htexcoord : TEXCOORD1;};VS_OUTPUT main( VS_INPUT myIN ){	VS_OUTPUT vOut;	float4 v = float4( myIN.iposition.x,		               myIN.iposition.y,					   myIN.iposition.z,					   1.0f );    vOut.hposition = mul( v, worldViewProj );    vOut.hnormal	= myIN.inormal;    vOut.hcolor    = myIN.icolor;    vOut.htexcoord = myIN.itexcoord;    return vOut;}


[Edited by - mike_ix on July 7, 2006 8:02:08 PM]
Quote:Original post by mike_ix
OK, so I found this discussion that states that pixel shaders cannot receive INPUT from the vertex shaders OUTPUT that has the semantic "NORMAL", so I changed my code so that the .vsh file has two TEXCOORD semantics as suggested and BAM, the darn thing runs. Only, none of my meshes are being rendered! OH, do I have the vertex shader blues! Here's my .vsh file now (some of the other names have changed as well during my effort to trouble shoot):

*** Source Snippet Removed ***


OK, so you changed the normal semantic to TEXCOORD0. Does your pixel shader know about this? Do you even use normals in the pixel shader? I'm not too sure, but I think you will also have to write a pixel shader to be able to see any geometry. Something like this might work:

sampler2D samp;struct PS_OUTPUT{        float4 hcolor : COLOR0;};struct PS_INPUT{	float4 hposition : POSITION;	float3 hnormal   : TEXCOORD0;	float4 hcolor    : COLOR0;	float2 htexcoord : TEXCOORD1;};PS_OUTPUT main( PS_INPUT myIN ){    PS_OUTPUT pOut;    pOut.xyz = tex2D(s_2D, myIN.htexcoord);    //forgot the w    pOut.w = 1.0f;    //when all else fails    //pOut.x = 1.0f; // will output red color no matter what    return pOut;}


[Edited by - deathkrush on July 7, 2006 11:29:59 PM]
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
Quote:Does your pixel shader know about this? Do you even use normals in the pixel shader? I'm not too sure, but I think you will also have to write a pixel shader to be able to see any geometry. Something like this might work:


Now we're getting to the crux of this discussion!!!


1) Does this mean I MUST ALSO include a pixel shader to render my mesh files?

2) WHERE ARE NORMALS CALCULATED? If I create a mesh from scratch, I have to calulate normals at some point. Currently I do this with D3DXComputeNormals, but when I mess with the FVF flags of the Mesh to make them match my vertex declaration I get Debug errors. Should I be doing this in the shaders instead?

3) If the Pixel shader only outputs color, how are the normals handled? Where do they go?

Remember, I just want to replicate what the fixed pipline does, but with shaders. In other words, I just want to see my meshes rendered. No fancy tricks just yet ;) I'm going to try to implement your pixel shader, and I'll let you know how it goes. Thanks for the help!
Quote:Original post by mike_ix
1) Does this mean I MUST ALSO include a pixel shader to render my mesh files?


Well, if you already wrote a vertex shader, then pixel shaders are very easy. Don't worry about it.

Quote:Original post by mike_ix
2) WHERE ARE NORMALS CALCULATED? If I create a mesh from scratch, I have to calulate normals at some point. Currently I do this with D3DXComputeNormals, but when I mess with the FVF flags of the Mesh to make them match my vertex declaration I get Debug errors. Should I be doing this in the shaders instead?


Like I said earlier, you could have the normals stored in the .x file or you could calculate them at runtime using the D3DXComputeNormals() function or using any other function that does the job. The point is that the normals need to be calculated and passed into the vertex shader.

What you do with normals at that point is entirely up to you. You can implement any kind of lighting equation in the vertex shader, calculate the per-vertex color (using the normals and light info) and pass the color to the pixel shader. The only thing that the pixel shader needs to know is the per-vertex color so that it can interpolate and blend the color with the texture.

Quote:Original post by mike_ix
3) If the Pixel shader only outputs color, how are the normals handled? Where do they go?


You either use the normals in the vertex shader to compute color which will give you per-vertex lighting or you use them in the pixel shader to compute color which gives per-pixel lighting.

Quote:Original post by mike_ix
Remember, I just want to replicate what the fixed pipline does, but with shaders. In other words, I just want to see my meshes rendered. No fancy tricks just yet ;) I'm going to try to implement your pixel shader, and I'll let you know how it goes. Thanks for the help!


Unfortunately when you start using shaders, you lose the fixed function pipeline and you have to implement it in the shader. You can take a look at the Nvidia website, they have a pretty huge shader that re-implements the fixed function pipeline. But if you just want your meshes rendered without any fancy stuff, the shaders are very short (no more than 3 lines of code for the main function).
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
Shouldn't your vertex declaration be:

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

28 instead of 40. As far as I can tell, D3DCOLOR's offset is 4
Quote:Original post by Zee Man
Shouldn't your vertex declaration be:

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

28 instead of 40. As far as I can tell, D3DCOLOR's offset is 4


I believe 40 is the right number. The offset is caculated as 4 * sizeof(float) = 16.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
No. It is 4. A D3DCOLOR type is just like a D3DCOLOR in your code (4 packed bytes, or 1 DWORD). It gets converted to 4 floats before being sent to the shader, and gets converted to the D3DXCOLOR format, where each color is a float 0.0 - 1.0. In the D3DCOLOR DWORD the RGBA values are 0-255 (or 0x00 - 0xff). So, if you have white for your D3DCOLOR it will be 0xffffffff, and before beings sent to the shader it gets converted to 4 floats 1.0, 1.0, 1.0, 1.0.
--------------------------Most of what I know came from Frank D. Luna's DirectX books
Quote:I believe 40 is the right number. The offset is caculated as 4 * sizeof(float) = 16.


Yeah, I caught that last night. It's now 28.

Quote:The point is that the normals need to be calculated and passed into the vertex shader.


This is exactly what I wanted to know! Thank you!

Quote:You either use the normals in the vertex shader to compute color which will give you per-vertex lighting or you use them in the pixel shader to compute color which gives per-pixel lighting.


Bingo! So normal calculations are done before shaders, then used by the shaders to implement tricks.

Quote:OK, so you changed the normal semantic to TEXCOORD0. Does your pixel shader know about this? Do you even use normals in the pixel shader? I'm not too sure, but I think you will also have to write a pixel shader to be able to see any geometry. Something like this might work:


I'm currnetly trying to implement the code you gave me, but D3DXCompileShaderFromFile fails with the error "error X3018: invalid subscript 'xyz'", and to be honest I'm not sure what this does yet:

pOut.xyz = tex2D(s_2D, myIN.htexcoord);

The struct pOut doesn't define an 'xyz'?

This topic is closed to new replies.

Advertisement