Dear All:
Hope you are having a nice Sunday.
I have decided that, as all the D3DXCreate... functions return vertex normals in addition to vertices themselves, it is more efficient to implement D3DXComputeNormals and use this function rather than calculate normals individually for each shape (or create an internal function that does the same thing).
As you can see from this patch: http://github.com/misha680/wine/commit/0e975e7360f403eb123055b6ff996137e0b22... the normals computed by D3DXComputeNormals are almost equal to those originally returned by D3DXCreateCylinder for complex enough values of the radii, length, slices, and stacks (these values are within 10^-6 of each other).
I have taken the following description from MSDN: "A normal for a vertex is generated by averaging the normals of all faces that share that vertex.
If adjacency is provided, replicated vertices are ignored and 'smoothed' over. If adjacency is not provided, replicated vertices will have normals averaged in from only the faces explicitly referencing them."
and implemented it in a test case (attached - code, compilation script, and compiled executable via MingW).
For a very simplified cylinder (radii 1.0f, length 0.0f, 3 slices, 2 stacks) all seems well, and my normals match perfectly to those created by D3DXComputeNormals.
However, for a more complicated case: static const FLOAT radius = 1.0f; static const FLOAT length = 1.0f; static const UINT slices = 10; static const UINT stacks = 20;
You can see something quite perplexing. Specifically, below you can see all 3 triangles that contain vertex #11 (11, 21, and 12 are vertex indices, below you will see the actual vertex X,Y,Z values, and finally the computed normal for the specific triangle).
Below, you can see under "Averaged Normals", first the normal computed by averaging and normalizing the normals for each of the triangles, and then the original normal computed by D3DXComputeNormals.
Notice that they are quite (significantly) different. This does not happen for a lot of vertices, but for quite a few (say 5-10%).
Index Buffer:
11,21,12, {0,1,-0.5}, {0,1,-0.45}, {0.587785,0.809017,-0.5}, {0.00954915,0.0293893,0}, 20,30,11, {-0.587785,0.809017,-0.5}, {-0.587785,0.809017,-0.45}, {0,1,-0.5}, {-0.00954915,0.0293893,0}, 11,30,21, {0,1,-0.5}, {-0.587785,0.809017,-0.45}, {0,1,-0.45}, {-0.00954915,0.0293893,0},
Averaged Normals:
11 {-0.107677,0.994186,0} {-1.99491e-008,1,0},
I have used the equivalent statement for D3DXComputeNormals:
if (!SUCCEEDED(D3DXComputeTangentFrameEx(mesh, D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0, D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS, NULL, -1.01f, -0.01f, -1.01f, NULL, NULL)))
and tried to vary the parameters listed below, but honestly am a bit stumped...
I will investigate further, but if perhaps I am missing something simple, would be great to know!
Thanks Misha
p.s. Fyi these are the relevant parameters for D3DXComputeTangentFrameEx from http://msdn.microsoft.com/en-us/library/bb172745%28v=VS.85%29.aspx Thank you! Misha
fPartialEdgeThreshold [in] FLOAT
Specifies the maximum cosine of the angle at which two partial derivatives are deemed to be incompatible with each other. If the dot product of the direction of the two partial derivatives in adjacent triangles is less than or equal to this threshold, then the vertices shared between these triangles will be split.
fSingularPointThreshold [in] FLOAT
Specifies the maximum magnitude of a partial derivative at which a vertex will be deemed singular. As multiple triangles are incident on a point that have nearby tangent frames, but altogether cancel each other out (such as at the top of a sphere), the magnitude of the partial derivative will decrease. If the magnitude is less than or equal to this threshold, then the vertex will be split for every triangle that contains it.
fNormalEdgeThreshold [in] FLOAT
Similar to fPartialEdgeThreshold, specifies the maximum cosine of the angle between two normals that is a threshold beyond which vertices shared between triangles will be split. If the dot product of the two normals is less than the threshold, the shared vertices will be split, forming a hard edge between neighboring triangles. If the dot product is more than the threshold, neighboring triangles will have their normals interpolated.