第三類是設置渲染選項函數。其中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.