天行健 君子当自强而不息

DXUT源码分析 ---- 类CDXUTMesh(4)

第三类是设置渲染选项函数。其中UseMeshMaterials()函数用于设置是否使用网格模型自身的材质和纹理进行渲染,如果调用该函数并将参数m_bUseMaterials设置为TRUE,则在渲染时使用网格模型自身的材质和纹理,这也是默认资源,如果将参数m_bUseMaterials设置为FALSE,则在渲染时不使用网格模型自身的材质和纹理,自然就使用当前为Direct3D设置的材质和纹理渲染网格模型。

void UseMeshMaterials( bool bFlag ) { m_bUseMaterials = bFlag; }

 

SetFVF()函数将网格模型克隆为参数dwFVF指定的灵活顶点格式:

HRESULT CDXUTMesh::SetFVF( LPDIRECT3DDEVICE9 pd3dDevice, DWORD dwFVF )
{
LPD3DXMESH pTempMesh = NULL;
    if( m_pMesh )
{
if( FAILED( m_pMesh->CloneMeshFVF( m_pMesh->GetOptions(), dwFVF, pd3dDevice, &pTempMesh ) ) )
{
SAFE_RELEASE( pTempMesh );
return E_FAIL;
}
        DWORD dwOldFVF = 0;
dwOldFVF = m_pMesh->GetFVF();
        SAFE_RELEASE( m_pMesh );
m_pMesh = pTempMesh;
        // Compute normals if they are being requested and the old mesh does not have them.
if( !(dwOldFVF & D3DFVF_NORMAL) && (dwFVF & D3DFVF_NORMAL) )
D3DXComputeNormals( m_pMesh, NULL );
}
    return S_OK;
}

代码相当简洁明了,这里只对函数D3DXComputeNormals()进行说明:

Computes unit normals for each vertex in a mesh. Provided to support legacy applications. Use D3DXComputeTangentFrameEx for better results.

HRESULT D3DXComputeNormals(
LPD3DXBASEMESH pMesh,
CONST DWORD * pAdjacency
);

Parameters

pMesh
[in, out] Pointer to an ID3DXBaseMesh interface, representing the normalized mesh object. This function may not take an ID3DXPMesh progressive mesh as input.
pAdjacency
[in] Pointer to an array of three DWORDs per face that specify the three neighbors for each face in the created progressive mesh. This parameter is optional and should be set to NULL if it is unused.

Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.

Remarks

The input mesh must have the D3DFVF_NORMAL flag specified in its flexible vertex format (FVF).

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.

This function simply calls D3DXComputeTangentFrameEx with the following input parameters:

D3DXComputeTangentFrameEx( pMesh,
D3DX_DEFAULT,
0,
D3DX_DEFAULT,
0,
D3DX_DEFAULT,
0,
D3DDECLUSAGE_NORMAL,
0,
D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS,
pAdjacency,
-1.01f,
-0.01f,
-1.01f,
NULL,
NULL);

函数SetVertexDecl()的功能与SetFVF()类似,只是SetVertexDecl()用于可编程流水线:

//-----------------------------------------------------------------------------
// Convert the mesh to the format specified by the given vertex declarations.
//-----------------------------------------------------------------------------
HRESULT CDXUTMesh::SetVertexDecl( LPDIRECT3DDEVICE9 pd3dDevice, const D3DVERTEXELEMENT9 *pDecl, 
                                  
bool bAutoComputeNormals, bool bAutoComputeTangents, 
                                  
bool bSplitVertexForOptimalTangents )
{
    LPD3DXMESH pTempMesh 
= NULL;

    
if( m_pMesh )
    {
        
if( FAILED( m_pMesh->CloneMesh( m_pMesh->GetOptions(), pDecl, pd3dDevice, &pTempMesh ) ) )
        {
            SAFE_RELEASE( pTempMesh );
            
return E_FAIL;
        }
    }

    
// Check if the old declaration contains a normal.

    
bool bHadNormal = false;
    
bool bHadTangent = false;

    D3DVERTEXELEMENT9 aOldDecl[MAX_FVF_DECL_SIZE];

    
if( m_pMesh && SUCCEEDED( m_pMesh->GetDeclaration( aOldDecl ) ) )
    {
        
for( UINT index = 0; index < D3DXGetDeclLength( aOldDecl ); ++index )
        {
            
if( aOldDecl[index].Usage == D3DDECLUSAGE_NORMAL )           
                bHadNormal 
= true;
           
            
if( aOldDecl[index].Usage == D3DDECLUSAGE_TANGENT )           
                bHadTangent 
= true;           
        }
    }

    
// Check if the new declaration contains a normal.

    
bool bHaveNormalNow = false;
    
bool bHaveTangentNow = false;

    D3DVERTEXELEMENT9 aNewDecl[MAX_FVF_DECL_SIZE];

    
if( pTempMesh && SUCCEEDED( pTempMesh->GetDeclaration( aNewDecl ) ) )
    {
        
for( UINT index = 0; index < D3DXGetDeclLength( aNewDecl ); ++index )
        {
            
if( aNewDecl[index].Usage == D3DDECLUSAGE_NORMAL )
                bHaveNormalNow 
= true;

            
if( aNewDecl[index].Usage == D3DDECLUSAGE_TANGENT )            
                bHaveTangentNow 
= true;            
        }
    }

    SAFE_RELEASE( m_pMesh );

    
if( pTempMesh )
    {
        m_pMesh 
= pTempMesh;

        
if!bHadNormal && bHaveNormalNow && bAutoComputeNormals )
        {
            
// Compute normals in case the meshes have them
            D3DXComputeNormals( m_pMesh, NULL );
        }

        
if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents )
        {
            ID3DXMesh
* pNewMesh;
            HRESULT hr;

            DWORD 
*rgdwAdjacency = NULL;

            rgdwAdjacency 
= new DWORD[m_pMesh->GetNumFaces() * 3];
            
if( rgdwAdjacency == NULL )
                
return E_OUTOFMEMORY;

            V( m_pMesh
->GenerateAdjacency(1e-6f, rgdwAdjacency) );

            
float fPartialEdgeThreshold;
            
float fSingularPointThreshold;
            
float fNormalEdgeThreshold;

            
if( bSplitVertexForOptimalTangents )
            {
                fPartialEdgeThreshold   
= 0.01f;
                fSingularPointThreshold 
= 0.25f;
                fNormalEdgeThreshold    
= 0.01f;
            }
            
else
            {
                fPartialEdgeThreshold   
= -1.01f;
                fSingularPointThreshold 
= 0.01f;
                fNormalEdgeThreshold    
= -1.01f;
            }

            
// Compute tangents, which are required for normal mapping
            hr = D3DXComputeTangentFrameEx( m_pMesh, 
                                            D3DDECLUSAGE_TEXCOORD, 
0
                                            D3DDECLUSAGE_TANGENT, 
0,
                                            D3DX_DEFAULT, 
0
                                            D3DDECLUSAGE_NORMAL, 
0,
                                            
0, rgdwAdjacency, 
                                            fPartialEdgeThreshold, fSingularPointThreshold, fNormalEdgeThreshold, 
                                            
&pNewMesh, NULL );

            SAFE_DELETE_ARRAY( rgdwAdjacency );
            
if( FAILED(hr) )
                
return hr;

            SAFE_RELEASE( m_pMesh );
            m_pMesh 
= pNewMesh;
        }
    }

    
return S_OK;
}

 

函数首先调用CloneMesh()以指定的顶点声明方式来克隆网格模型:
    LPD3DXMESH pTempMesh = NULL;
    if( m_pMesh )
{
if( FAILED( m_pMesh->CloneMesh( m_pMesh->GetOptions(), pDecl, pd3dDevice, &pTempMesh ) ) )
{
SAFE_RELEASE( pTempMesh );
return E_FAIL;
}
}

接下来检查原网格中是否包含法线和切线信息:

    // Check if the old declaration contains a normal.
    bool bHadNormal = false;
bool bHadTangent = false;
    D3DVERTEXELEMENT9 aOldDecl[MAX_FVF_DECL_SIZE];
    if( m_pMesh && SUCCEEDED( m_pMesh->GetDeclaration( aOldDecl ) ) )
{
for( UINT index = 0; index < D3DXGetDeclLength( aOldDecl ); ++index )
{
if( aOldDecl[index].Usage == D3DDECLUSAGE_NORMAL )
bHadNormal = true;
            if( aOldDecl[index].Usage == D3DDECLUSAGE_TANGENT )           
bHadTangent = true;
}
}

再接下来检查克隆后的网格模型中是否包含法线和切线信息:

    // Check if the new declaration contains a normal.
    bool bHaveNormalNow = false;
bool bHaveTangentNow = false;
    D3DVERTEXELEMENT9 aNewDecl[MAX_FVF_DECL_SIZE];
    if( pTempMesh && SUCCEEDED( pTempMesh->GetDeclaration( aNewDecl ) ) )
{
for( UINT index = 0; index < D3DXGetDeclLength( aNewDecl ); ++index )
{
if( aNewDecl[index].Usage == D3DDECLUSAGE_NORMAL )
bHaveNormalNow = true;
            if( aNewDecl[index].Usage == D3DDECLUSAGE_TANGENT )            
bHaveTangentNow = true;
}
}

如果原网格中不包含法线信息,同时克隆后的网格模型中要求包含法线信息,且参数bAutoComputeNormals被设置为true(即要求自动计算法线),则调用D3DXComputeNormals()计算法线:

    SAFE_RELEASE( m_pMesh );
    if( pTempMesh )
{
m_pMesh = pTempMesh;
        if( !bHadNormal && bHaveNormalNow && bAutoComputeNormals )
{
// Compute normals in case the meshes have them
D3DXComputeNormals( m_pMesh, NULL );
}

如果原网格中不包含切线信息,同时克隆后的网格模型中要求包含切线信息,且参数bAutoComputeTangents被设置为true(即要求自动计算切线),则首先调用GenerateAdjacency()生成网格面邻接信息,并调用D3DXComputeTangentFrameEx()来计算网格面切线:

        if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents )
{
ID3DXMesh* pNewMesh;
HRESULT hr;
            DWORD *rgdwAdjacency = NULL;
            rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3];
if( rgdwAdjacency == NULL )
return E_OUTOFMEMORY;
            V( m_pMesh->GenerateAdjacency(1e-6f, rgdwAdjacency) );
            float fPartialEdgeThreshold;
float fSingularPointThreshold;
float fNormalEdgeThreshold;
            if( bSplitVertexForOptimalTangents )
{
fPartialEdgeThreshold = 0.01f;
fSingularPointThreshold = 0.25f;
fNormalEdgeThreshold = 0.01f;
}
else
{
fPartialEdgeThreshold = -1.01f;
fSingularPointThreshold = 0.01f;
fNormalEdgeThreshold = -1.01f;
}
            // Compute tangents, which are required for normal mapping
hr = D3DXComputeTangentFrameEx( m_pMesh,
D3DDECLUSAGE_TEXCOORD, 0,
D3DDECLUSAGE_TANGENT, 0,
D3DX_DEFAULT, 0,
D3DDECLUSAGE_NORMAL, 0,
0, rgdwAdjacency,
fPartialEdgeThreshold, fSingularPointThreshold, fNormalEdgeThreshold,
&pNewMesh, NULL );
            SAFE_DELETE_ARRAY( rgdwAdjacency );
if( FAILED(hr) )
return hr;
            SAFE_RELEASE( m_pMesh );
m_pMesh = pNewMesh;
}

 

D3DXComputeTangentFrameEx()的声明如下:

Performs tangent frame computations on a mesh. Tangent, binormal, and optionally normal vectors are generated. Singularities are handled as required by grouping edges and splitting vertices.

HRESULT D3DXComputeTangentFrameEx(
ID3DXMesh * pMesh,
DWORD dwTextureInSemantic,
DWORD dwTextureInIndex,
DWORD dwUPartialOutSemantic,
DWORD dwUPartialOutIndex,
DWORD dwVPartialOutSemantic,
DWORD dwVPartialOutIndex,
DWORD dwNormalOutSemantic,
DWORD dwNormalOutIndex,
DWORD dwOptions,
CONST DWORD * pdwAdjacency,
FLOAT fPartialEdgeThreshold,
FLOAT fSingularPointThreshold,
FLOAT fNormalEdgeThreshold,
ID3DXMesh ** ppMeshOut,
ID3DXBuffer ** ppVertexMapping
);

Parameters

pMesh
[in] Pointer to an input ID3DXMesh mesh object.
dwTextureInSemantic
[in] Specifies the texture coordinate input semantic. If D3DX_DEFAULT, the function assumes that there are no texture coordinates, and the function will fail unless normal vector calculation is specified.
dwTextureInIndex
[in] If a mesh has multiple texture coordinates, specifies the texture coordinate to use for the tangent frame computations. If zero, the mesh has only a single texture coordinate.
dwUPartialOutSemantic
[in] Specifies the output semantic for the type, typically D3DDECLUSAGE_TANGENT, that describes where the partial derivative with respect to the U texture coordinate will be stored. If D3DX_DEFAULT, then this partial derivative will not be stored.
dwUPartialOutIndex
[in] Specifies the semantic index at which to store the partial derivative with respect to the U texture coordinate.
dwVPartialOutSemantic
[in] Specifies the D3DDECLUSAGE type, typically D3DDECLUSAGE_BINORMAL, that describes where the partial derivative with respect to the V texture coordinate will be stored. If D3DX_DEFAULT, then this partial derivative will not be stored.
dwVPartialOutIndex
[in] Specifies the semantic index at which to store the partial derivative with respect to the V texture coordinate.
dwNormalOutSemantic
[in] Specifies the output normal semantic, typically D3DDECLUSAGE_NORMAL, that describes where the normal vector at each vertex will be stored. If D3DX_DEFAULT, then this normal vector will not be stored.
dwNormalOutIndex
[in] Specifies the semantic index at which to store the normal vector at each vertex.
dwOptions

Combination of one or more D3DXTANGENT flags that specify tangent frame computation options. If NULL, the following options will be specified:

Description D3DXTANGENT Flag Value
Weight the normal vector length by the angle, in radians, subtended by the two edges leaving the vertex. & !( D3DXTANGENT_WEIGHT_BY_AREA | D3DXTANGENT_WEIGHT_EQUAL )
Compute orthogonal Cartesian coordinates from texture coordinates (u, v). See Remarks. & !( D3DXTANGENT_ORTHOGONALIZE_FROM_U | D3DXTANGENT_ORTHOGONALIZE_FROM_V )
Textures are not wrapped in either u or v directions & !( D3DXTANGENT_WRAP_UV )
Partial derivatives with respect to texture coordinates are normalized. & !( D3DXTANGENT_DONT_NORMALIZE_PARTIALS )
Vertices are ordered in a counterclockwise direction around each triangle. & !( D3DXTANGENT_WIND_CW )
Use per-vertex normal vectors already present in the input mesh. & !( D3DXTANGENT_CALCULATE_NORMALS )
 
[in] If D3DXTANGENT_GENERATE_IN_PLACE is not set, the input mesh is cloned. The original mesh must therefore have sufficient space to store the computed normal vector and partial derivative data.

 

pdwAdjacency
[in] Pointer to an array of three DWORDs per face that specify the three neighbors for each face in the mesh. The number of bytes in this array must be at least 3 * ID3DXBaseMesh::GetNumFaces * sizeof(DWORD).
fPartialEdgeThreshold
[in] 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] 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] 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.
ppMeshOut
[out] Address of a pointer to an output ID3DXMesh mesh object that receives the computed tangent, binormal, and normal vector data.
ppVertexMapping
[out] Address of a pointer to an output ID3DXBuffer buffer object that receives a mapping of new vertices computed by this method to the original vertices. The buffer is an array of DWORDs, with the array size defined as the number of vertices in ppMeshOut.

Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.

Remarks

A simplified version of this function is available as D3DXComputeTangentFrame.

The computed normal vector at each vertex is always normalized to have unit length.

The most robust solution for computing orthogonal Cartesian coordinates is to not set flags D3DXTANGENT_ORTHOGONALIZE_FROM_U and D3DXTANGENT_ORTHOGONALIZE_FROM_V, so that orthogonal coordinates are computed from both texture coordinates u and v. However, in this case, if either u or v is zero, then the function will compute orthogonal coordinates using D3DXTANGENT_ORTHOGONALIZE_FROM_V or D3DXTANGENT_ORTHOGONALIZE_FROM_U, respectively.


posted on 2008-05-31 10:59 lovedday 阅读(1130) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论