Tiger.sdkmesh を描画

SDK Mesh(Tiger.sdkmesh)を描画します。
sdkmesh に関して気になるページを見つけました。
SDK メッシュ ファイル形式の概要

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. X-FILE に代わる物として DirectX10 から登場したの DXUT メッシュ形式(.sdkmesh)です。
    DirectX Sample Browser にはメッシュを描画するプロジェクトが沢山あるのですが、初心者には難しすぎます。
    今回は Tiger.sdkmesh を例にして、極力シンプルなプログラムを作成します。
    sdkmesh のモデルは DirectX SDK (June 2010) の下記のフォルダーに格納されています。
    C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\Media\
  2. DirectX10 のプロジェクトなので MultiMon10 を使うことにします。
    DirectX Sample Browser で MultiMon10 のプロジェクトを作成して下さい。
    ソースプログラムは .sdkmesh を描画 を元に修正します。
    Shader は、このページの後部に掲載します。
  3. g_Mesh が Tiger のメッシュです。
    DXUTCreateTeapot() で生成したときは ID3DX10Mesh* でしたが .sdkmesh では CDXUTSDKMesh を使っています。
    CDXUTSDKMesh は SDK Mesh の API をラップした便利なクラスです。
    tiger.sdkmesh はテクスチャを張り付けたモデルなので、g_ptxDiffuseVariable を定義します。
    g_Camera がモデルを回転する CModelViewerCamera です。
    WCHAR   MeshName[MAX_PATH] = L"tiger.sdkmesh";
    
    ID3D10Effect*                       g_pEffect = NULL;
    ID3D10InputLayout*                  g_pLayout = NULL;
    ID3D10EffectTechnique*              g_pRender = NULL;
    CDXUTSDKMesh                        g_Mesh;
    ID3D10EffectShaderResourceVariable* g_ptxDiffuseVariable = NULL;
    ID3D10EffectMatrixVariable*         g_pWorldVariable = NULL;
    ID3D10EffectMatrixVariable*         g_pViewVariable = NULL;
    ID3D10EffectMatrixVariable*         g_pProjectionVariable = NULL;
    CModelViewerCamera                  g_Camera;
    
  4. OnD3D10FrameRender() ではタイマーで World を自動的に回転した値と g_Camera.GetWorld を掛け合わせます。
    g_Camera で回転すると g_Camera 中の World 座標と View 座標が回転します。
    D3DXMatrixMultiply() に代えて次のように書くことも出来ます。
    World = *g_Camera.GetWorldMatrix() * World;
    Shader の領域 g_pWorldVariable, g_pViewVariable, g_pProjectionVariable に値を設定して描画します。
    CDXUTSDKMesh を使うと描画は簡単で g_Mesh.Render() の一行だけです。
    void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime,
            float fElapsedTime, void* pUserContext )
    {
        D3DXMATRIX  World;
        D3DXMatrixRotationY( &World, 20.0f * DEG2RAD((float)fTime) );
        D3DXMatrixMultiply( &World, &(*g_Camera.GetWorldMatrix()), &World );
    
        // Clear render target and the depth stencil 
        float ClearColor[4] = { 0.176f, 0.196f, 0.667f, 0.0f };
        pd3dDevice->ClearRenderTargetView( DXUTGetD3D10RenderTargetView(), ClearColor );
        pd3dDevice->ClearDepthStencilView( DXUTGetD3D10DepthStencilView(), D3D10_CLEAR_DEPTH, 1.0, 0 );
    
        // Update variables
        g_pWorldVariable->SetMatrix(World);
        g_pViewVariable->SetMatrix((float*)&(*g_Camera.GetViewMatrix()));
        g_pProjectionVariable->SetMatrix((float*)&(*g_Camera.GetProjMatrix()));
    
        g_Mesh.Render( pd3dDevice, g_pRender, g_ptxDiffuseVariable );
    }
    
  5. OnD3D10CreateDevice() はデバイスを生成するときに呼ばれます。
    Shader は頂点座標と法線ベクトルとテクスチャ座標で構成されるモデルを描画するものを使用します。
    g_Mesh.Create() で .sdkmesh をロードします。
    CDXUTSDKMesh を使うとロードも簡単で g_Mesh.Create() の一行で、マテリアルからテクスチャまで全て面倒を見てくれます。
    プログラムでは "C:\\DATA\\Sdkmesh" のフォルダーに格納されている "tiger.sdkmesh" をロードしています。
    &Eye, &At をパラメータとして g_Camera.SetViewParams() を設定します。
    HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice,
                 const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {
        HRESULT hr;
    
        // Find the D3DX effect file
        WCHAR str[MAX_PATH];
        V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"Texture.fx" ) );
        DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
    
        V_RETURN( D3DX10CreateEffectFromFile( str, NULL, NULL, "fx_4_0", dwShaderFlags, 0,
                 pd3dDevice, NULL, NULL, &g_pEffect, NULL, NULL ) );
    
        // Obtain the technique
        g_pRender = g_pEffect->GetTechniqueByName( "Render" );
        g_ptxDiffuseVariable = g_pEffect->GetVariableByName( "txDiffuse" )->AsShaderResource();
        g_pWorldVariable = g_pEffect->GetVariableByName( "World" )->AsMatrix();
        g_pViewVariable = g_pEffect->GetVariableByName( "View" )->AsMatrix();
        g_pProjectionVariable = g_pEffect->GetVariableByName( "Projection" )->AsMatrix();
    
        // Define the input layout
        const D3D10_INPUT_ELEMENT_DESC layout[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
            { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        };
        UINT numElements = sizeof( layout ) / sizeof( layout[0] );
    
        // Create the input layout
        D3D10_PASS_DESC PassDesc;
        g_pRender->GetPassByIndex( 0 )->GetDesc( &PassDesc );
        V_RETURN( pd3dDevice->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature,
                PassDesc.IAInputSignatureSize, &g_pLayout ) );
    
        // Set the input layout
        pd3dDevice->IASetInputLayout( g_pLayout );
    
        // Load the mesh
        SetCurrentDirectory(L"C:\\DATA\\Sdkmesh");
        V_RETURN( g_Mesh.Create( pd3dDevice, MeshName, true ) );
    
        // Initialize the view matrix
        D3DXVECTOR3 Eye( 0.0f, 0.0f, -5.0f );
        D3DXVECTOR3 At( 0.0f, 0.0f, 0.0f );
        g_Camera.SetViewParams( &Eye, &At );
    
        return S_OK;
    }
    
  6. OnD3D10ResizedSwapChain() では、縦横比を計算して g_Camera.SetProjParams() を設定します。
    次に g_Camera を使ってモデルとカメラをマウスで回転します。
    マウスで回転するプログラムは結構面倒なのですが、g_Camera を使うと g_Camera.SetWindow() を呼び出すだけです。
    マウスの左ドラッグでモデルが回転し、右ドラッグでカメラが回転します。
    HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
            const DXGI_SURFACE_DESC* pBufferSurfaceDesc, void* pUserContext )
    {
        float fAspect = static_cast( pBufferSurfaceDesc->Width ) / static_cast( pBufferSurfaceDesc->Height );
        g_Camera.SetProjParams( D3DX_PI/4, fAspect, 0.5f, 1000.0f );
        g_Camera.SetWindow( pBufferSurfaceDesc->Width, pBufferSurfaceDesc->Height );
    
        return S_OK;
    }
    
  7. 頂点座標と法線ベクトルとテクスチャ座標で構成されるモデルを描画する "Texture.fx" です。
    //--------------------------------------------------------------------------------------
    // File: Texture.fx
    //--------------------------------------------------------------------------------------
    
    
    //--------------------------------------------------------------------------------------
    // Constant Buffer Variables
    //--------------------------------------------------------------------------------------
    Texture2D txDiffuse;
    SamplerState samLinear
    {
        Filter = MIN_MAG_MIP_LINEAR;
        AddressU = Wrap;
        AddressV = Wrap;
    };
    
    cbuffer cbConstant
    {
        float3 vLightDir = float3(-0.577,0.577,-0.577);
    };
    
    cbuffer cbChangesOnce
    {
        matrix View;
    };
    
    cbuffer cbChangeOnResize
    {
        matrix Projection;
    };
    
    cbuffer cbChangesEveryFrame
    {
        matrix World;
    };
    
    struct VS_INPUT
    {
        float3 Pos      : POSITION;         //position
        float3 Norm     : NORMAL;           //normal
        float2 Tex      : TEXCOORD0;        //texture coordinate
    };
    
    struct PS_INPUT
    {
        float4 Pos      : SV_POSITION;
        float4 Norm     : TEXCOORD0;
        float2 Tex      : TEXCOORD1;
    };
    
    
    //--------------------------------------------------------------------------------------
    // Vertex Shader
    //--------------------------------------------------------------------------------------
    PS_INPUT VS( VS_INPUT input )
    {
        PS_INPUT output = (PS_INPUT)0;
        output.Pos = mul( float4(input.Pos,1), World );
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = mul( input.Norm, World );
        output.Tex = input.Tex;
        
        return output;
    }
    
    
    //--------------------------------------------------------------------------------------
    // Pixel Shader
    //--------------------------------------------------------------------------------------
    float4 PS( PS_INPUT input) : SV_Target
    {
        //calculate lighting assuming light color is <1,1,1,1>
        float fLighting = saturate( dot( input.Norm, vLightDir ) );
        float4 outputColor = txDiffuse.Sample( samLinear, input.Tex ) * fLighting;
        outputColor.a = 1;
        return outputColor;
    }
    
    
    //--------------------------------------------------------------------------------------
    // Technique
    //--------------------------------------------------------------------------------------
    technique10 Render
    {
        pass P0
        {
            SetVertexShader( CompileShader( vs_4_0, VS() ) );
            SetGeometryShader( NULL );
            SetPixelShader( CompileShader( ps_4_0, PS() ) );
        }
    }
    
  8. tiger.sdkmesh 以外にも幾つかのモデルが格納されているので試して下さい。
    このプログラムで正常に描画できるのは、頂点座標と法線ベクトルとテクスチャ座標で構成されるモデルです。
    tiny.sdkmesh は他のモデルに比べてサイズが大きいので、注意して下さい。
    Tiger.x を描画するプログラムは Tiger.x を回転しながらマウスで操作する を参照して下さい。

モデルサイズ&中心座標

  1. tiger.sdkmesh と tiny.sdkmesh では、あまりにもモデルのサイズが違いすぎます。
    そこで、モデルのサイズに合わせて描画する方法を説明します。
    一般的に二種類の方法が考えられます。
    1. D3DXMatrixScaling() でモデルを拡大・縮小する方法。
      拡大・縮小が大きくなると、マテリアルの効果に影響が出るようです。
    2. View(カメラ)の位置で調整する方法。
  2. どちらの方法を使うにしろ、モデルの大きさが解らなくてはプログラム出来ません。
    DirectX10 では、次の関数でモデルの大きさ(ボックス型)を取得します。
        D3DXVECTOR3     g_vExtents;   // Object の大きさ
        g_vExtents = g_Mesh.GetMeshBBoxExtents(0);  // Object の大きさを取得
    
    g_vExtents.x, g_vExtents.y, g_vExtents.z のいずれか、またはこれらを組み合わせてサイズを調整します。
    ちなみに DirectX9 でモデルの大きさを調べるときは、次の関数を使っていました。
        D3DXVECTOR3 g_vObjectCenter;    // Center of bounding sphere of object
        FLOAT       g_fObjectRadius;    // Radius of bounding sphere of object
        V_RETURN( D3DXFrameCalculateBoundingSphere( g_pFrameRoot, &g_vObjectCenter, &g_fObjectRadius ) );
    
  3. g_vObjectCenter が出たついでに、DirectX10 でモデルの中心座標を調べる関数を紹介します。
        D3DXVECTOR3     g_vCenter;      // Object の中心
        g_vCenter  = g_Mesh.GetMeshBBoxCenter(0);   // Object の中心を取得
    

超初心者のプログラム入門(DirectX10 game program)