Drawing Indexed Meshes w/ Vertex Shaders

Started by
24 comments, last by mike_ix 17 years, 9 months ago
So, I'm a little new to Vertex Shaders, and there is one bit hanging me up right now. Every example I can find on drawing indexed meshes with vertex shaders shows them being drawn by DrawIndexedPrimitive, not DrawSubset. Why? Apprently I need to create a vertex buffer for SetStream to point to, but why can't I use DrawSubset after that? Also, can I use ppmesh->GetVertexBuffer as opposed to CreateVertexBuffer to get my vertex buffer pointer for SetStream? An example I found copies the verticies from ppmesh->GetVertexBuffer to a pointer created by CreateVertexBuffer and then hands this pointer to SetStream, but I'm not sure why it doesn't just hand the pointer from GetVertexBuffer. Any help would be greatly appreciated!!!!
Advertisement
Draw subset internally calls DrawIndexedPrimitive, and SetStreamSource.

The reason most Vertex Shader samples use a vertex + index buffer for drawing instead of a mesh, is that this gives you closer control over the vertex format.

A Mesh doesn't have a hardcoded format, which means if you load one .x file, it might have normals but not texcoords, and another might have texcoords but no normals. Now, a vertex shader relies greatly on which information it is given. If some of it is missing in the .x file, the shader won't draw properly.

For your own use, you can freely use DrawSubset with a mesh, assuming you know the vertex format fits that expected in the vertex shader. The fact you're using shaders doesn't matter when you're passing the GPU information to draw, it only affects how it draws it.

Hope this helps.
Sirob Yes.» - status: Work-O-Rama.
Quote:Draw subset internally calls DrawIndexedPrimitive, and SetStreamSource


OH! That is VERY helpful to know! so, let me see if I understand you correctly: I can use DrawSubset, but if I do, I do not need to call SetStream, and I do need to be sure I've converted my mesh to the appropriate FVF flags (that match the shader). But if I want more control over the vertex format (do you mean FVF here?) go ahead and use DrawIndexedPrimitive. Correct?
Quote:Original post by mike_ix
Quote:Draw subset internally calls DrawIndexedPrimitive, and SetStreamSource


OH! That is VERY helpful to know! so, let me see if I understand you correctly: I can use DrawSubset, but if I do, I do not need to call SetStream, and I do need to be sure I've converted my mesh to the appropriate FVF flags (that match the shader). But if I want more control over the vertex format (do you mean FVF here?) go ahead and use DrawIndexedPrimitive. Correct?


The thing to keep in mind is that D3DXMesh provides a number of abstractions of the base facilities that Direct3D provides. That is, it aims to hide a number of the specific details of how you actually go about getting triangles drawn, for the sake of making it easier to use. DrawSubset() is part of that abstraction - it covers up some of the details.

Directly using SetStreamSource(), DrawIndexedPrimitive(), etc., brings you closer to the base D3D functionality - bypassing the D3DXMesh convenience functions.

If I had an initial codebase that used D3DXMesh and I wanted to get finer control over what was going on, I would start breaking down what D3DXMesh does into the D3D calls, "weaning" the code off of the support code, until I was able to get rid of D3DXMesh entirely.

Btw, I would generally recommend that if you're using shaders, you use vertex declarations rather than FVF formats. They're quite a bit more flexible, but they are also more of a pain to deal with. Take a look at the docs for CreateVertexDeclaration() and that neighborhood to see what that's all about.
Quote:Btw, I would generally recommend that if you're using shaders, you use vertex declarations rather than FVF formats. They're quite a bit more flexible, but they are also more of a pain to deal with. Take a look at the docs for CreateVertexDeclaration() and that neighborhood to see what that's all about.


I'm glad you said that because it leads me right into my next question ;) I am currently creating a mesh using D3DXCreateMeshFVF, then loading it up with vertices that have been defined as:

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)


I'm then using D3DXComputeNormals to get results like this (click to see screenshot). Notice that I can distinctly see each face's lighting; that it's not uniform. From my understanding, I need to use vertex blending to make the shadows more smooth and I need to use vertex shaders to accomplish this (correct?). So, my question is this: To do all of this should I get rid of D3DXCreateMeshFVF and the FVF format altogether, and if so how do I calculate normals for my meshes? Is that done by the shader?
Quote:Original post by mike_ix
So, my question is this: To do all of this should I get rid of D3DXCreateMeshFVF and the FVF format altogether, and if so how do I calculate normals for my meshes? Is that done by the shader?


Usually you don't calculate normals for meshes, it's preferrable to have these pre-calculated by the modeler and exported in the .X file. In the vertex shader you will be transforming the normals based on the Current Transformation Matrix, World Matrix, Projection Matrix, however you choose to do it.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
Quote:Usually you don't calculate normals for meshes, it's preferrable to have these pre-calculated by the modeler and exported in the .X file. In the vertex shader you will be transforming the normals based on the Current Transformation Matrix, World Matrix, Projection Matrix, however you choose to do it.


OK, but what if you are creating a mesh using D3DXCreateMeshFVF? What if you are doing both, importing .x files and creating meshes from scratch and rendering them together? I can still create a mesh using D3DXCreateMesh, and leave out the FVF flags, but I need to calculate normals somewhere, right? Currently I'm using D3DXComputeNormals which gives results like my screenshot. Do I use shaders to caculate normals on my terrain mesh, or do I continue to calculate them with D3DXComputeNormals and use shaders to do vertex blending to smooth out my shadows? I'm just trying to figure out exactly how the shaders fit into the scope of things.
I just had to manually draw a mesh with DrawIndexedPrimitive, because I am using hardware or stream instancing, which requires you to set the vertex declaration to include the stream 1 data (see the Instancing sample in the SDK).

Anyway, manually drawing it involves doing things that the DrawSubset() function would do for you:

1) Set the vertex declaration - pDevice->SetVertexDeclaration()

2) Get and set the vertex and index buffers:
pMesh->GetVertexBuffer()
pMesh->GetIndexBuffer()
pDevice->SetIndices()
pDevice->SetStreamSource(0, ...)

3) Get the attribute table so you can draw each subset - pMesh->GetAttributeTable()

4) Loop through the attribute buffer array to draw each one, rather than just looping through your subsets, to set the materials and textures for each subset:

for each attribute item
pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
0,
0,
pMesh->GetNumVertices(),
pAttrRangleTbl[iSub].FaceStart * 3,
pAttrRangleTbl[iSub].FaceCount) );

5) release the vertex and index buffers and delete the attribute table array.


So, those are all the things that DrawSubset() does for you.
--------------------------Most of what I know came from Frank D. Luna's DirectX books
So, something like this should be legal, so long as my mesh FVF flags match my vertex declaration or I have no FVF flags at all?

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->SetVertexDeclaration( g_pVertexDeclaration );		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 );	}	rmesh->Release;	return true;}


Or do I even have these statements in the right place?

Thanks for all the help!!!!!!!!


I found a similar discussion about the FVF format vs. D3DVERTEXELEMENT9 that helps answer my question about whether I should use flags. In a nutshell, I guess it depends on the hardware I want to support, right?

[Edited by - mike_ix on July 7, 2006 3:53:37 PM]
Almost correct. The instruction:

"g_pd3dDevice->SetVertexDeclaration( g_pVertexDeclaration );

will accomplish nothing since the DrawSubset call does it internally for you. That is why I had to do it manually. It would set the declaration to the actual vertex declaration, over-riding my attempt to set it for instancing, and the instancing didn't work.

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).

And, you can use FVF as long as your vertex buffer is in a format that can be represented by FVF codes AND the elements in the buffer are in the correct order. If you want to re-order them, or add elements it does not support (like tangents and binormals used for normal mapping), then you gotta use a declaration.
--------------------------Most of what I know came from Frank D. Luna's DirectX books

This topic is closed to new replies.

Advertisement