On Mon, 2010-07-12 at 14:21 +0200, Henri Verbeet wrote:
On 12 July 2010 04:48, Misha Koshelev misha680@gmail.com wrote:
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},
The part of the mesh you're interested in looks like this:
20---11---12 | /| /| |B / |A / | | / C| / | |/ |/ | 30---21---22
The "real" vertex normal at a given vertex would be {x, y, 0} for this shape (for an open cylinder anyway, for a closed cylinder you'd have {0, 0, 1.0} and {0, 0, -1.0} for the vertices from the caps), so the {-1.99491e-008, 1.0, 0.0} d3dx calculates is pretty close.
Vertex 11 is part of the faces A, B and C. Notice that faces B and C (must) have the same face normal. If you simply calculate the average (mean) of the normals for faces A, B and C, the resulting vector would be biased towards B/C. That's what happens with your code. You can mitigate that by assigning weights to the faces based on the area of the triangles and the angle between the edges at the vertex you're interested in. I'd expect d3dx to do something similar to that.
Thank you Henri for the explanation. In fact it seems that this flag is default on D3DXComputeTangentFrameEx:
D3DXTANGENT_WEIGHT_BY_AREA Weight the direction of the computed per-vertex normal or partial derivative vector according to the areas of triangles attached to that vertex. Mutually exclusive with D3DXTANGENT_WEIGHT_EQUAL.
If I specify D3DXTANGENT_WEIGHT_EQUAL, then the normal vectors I calculate are indeed correct.
However, the quirk is that, unless I am doing something wrong, all three triangles actually have the same area (I am using 1/2 of cross product of two vertices in the triangle, which I believe should in fact be correct - see below):
FLOAT ComputeArea(D3DXVECTOR3* p0, D3DXVECTOR3* p1, D3DXVECTOR3* p2) { D3DXVECTOR3 u = *p1 - *p0; D3DXVECTOR3 v = *p2 - *p0; D3DXVECTOR3 out;
D3DXVec3Cross(&out, &u, &v); return 0.5*D3DXVec3Length(&out); }
and resulting areas and vertices:
Vertex Buffer:
11 {0,1,-0.5,-1.99491e-008,1,0}
Index Buffer:
11,21,12, {0,1,-0.5}, {0,1,-0.45}, {0.587785,0.809017,-0.5}, {0.309017,0.951057,0}, Area = 0.0154509, 20,30,11, {-0.587785,0.809017,-0.5}, {-0.587785,0.809017,-0.45}, {0,1,-0.5}, {-0.309017,0.951056,0}, Area = 0.0154509, 11,30,21, {0,1,-0.5}, {-0.587785,0.809017,-0.45}, {0,1,-0.45}, {-0.309017,0.951056,0}, Area = 0.0154509,
Averaged Normals:
0.0154509 {0.309017,0.951057,0} 0.0154509 {-0.309017,0.951056,0} 0.0154509 {-0.309017,0.951056,0} 11 {-0.107677,0.994186,0} {-1.99491e-008,1,0},
Eesh...
(as always relevant code attached for the interested reader)
However, note that for simple, regular shapes like these it's *much* simpler to just generate the normals together with the vertices. E.g. for a cylinder, you'd simply have normalize(x, y, 0.0), {0.0, 0.0, 1.0} and {0.0, 0.0, -1.0}, for a sphere you'd have normalize{x, y, z}, etc.
Thank you. I have made a relevant patch for D3DXCreateCylinder test: http://github.com/misha680/wine/commit/20ed58d7ba39554d2628c00b4268843b089cd... using this simplified version.
I will focus on this simple version for now, as the goal is to implement the shape functions, and not to actually necessarily implement D3DXComputeTangentFrameEx, although that would be quite nice as well.
Thank you Misha