Having some trouble implementing D3DXComputeNormals... any hints/advice?

Misha Koshelev misha680 at gmail.com
Sun Jul 11 21:48:54 CDT 2010


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/0e975e7360f403eb123055b6ff996137e0b2289a
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.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: wine.zip
Type: application/zip
Size: 15736 bytes
Desc: not available
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20100711/e98652be/attachment-0001.zip>


More information about the wine-devel mailing list