矩形に Texture を貼り付ける

DirectX10 では Sprite で Texture 画像を描画する関数が見当たりません。
仕方が無いので、矩形をキャンパスに見立てて Texture を貼り付けて描画します。
カリングと座標系の説明は Windows Guid を参照して下さい。
また画像が横長/縦長にならないようにアスペクト比を保ちます。

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

プロジェクトの説明

  1. DirectX Sample Browser で EmptyProject10 のプロジェクトを作成して下さい。
    EmptyProject10 のプロジェクトをコンパイルして、空のウインドウが表示される事を確かめて下さい。
    エラーが発生するときや、正常に実行出来ないときは、残念ながら現在の構成は相性が悪いようです。
  2. EmptyProject10.cpp を修正して矩形に Texture を貼り付けて描画します。
    VER_N は頂点フォーマットを定義する配列 vertices[] の大きさです。
    IDX_N は頂点フォーマットの index を定義する配列 indices[] の大きさです。
    SimpleVertex は頂点フォーマットの形式で、三次元座標とテクスチャ座標を定義します。
    vertices[] に画像を貼り付ける矩形の座標とテクスチャを定義します。
    テクスチャ座標はポリゴンに貼り付ける画像の座標で 0.0f~1.0f の相対的な値で定義します。
    テクスチャの座標を三通りの方法で定義してみました。
    どのように描画されるか試してみて下さい。
    g_pEffect は Shader を設定する領域です。
    Shader の説明は 立方体を光源で照らして描画する を参照して下さい。
    g_pTextureRV は テクスチャ画像を Resource として設定する領域です。
    g_pDiffuseVariable は Resource を設定するポインタです。
    g_pLayout は頂点座標の Layout を設定する領域です。
    g_pVertexBuffer は頂点データを格納する VertexBuffer です。
    g_pIndexBuffer は頂点データの Index を格納する VertexBuffer です。
    g_World はワールド座標の変換行列です。
    g_View はビュー座標の変換行列です。
    g_Projection はプロジェクションの変換行列です。
    #include "DXUT.h"
    
    // Vertex(頂点座標) の定義
    #define VER_N   4
    #define IDX_N   6
    
    struct SimpleVertex
    {
        D3DXVECTOR3 Pos; 
        D3DXVECTOR2 Tex; 
    };
    SimpleVertex vertices[VER_N] =
    {
        { D3DXVECTOR3( -1.0f,  1.0f, 0.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },   //左上
        { D3DXVECTOR3(  1.0f,  1.0f, 0.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },   //右上
        { D3DXVECTOR3( -1.0f, -1.0f, 0.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },   //左下
        { D3DXVECTOR3(  1.0f, -1.0f, 0.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },   //右下
    };
    /*
    SimpleVertex vertices[VER_N] =
    {
        { D3DXVECTOR3( -1.0f,  1.0f, 0.0f ), D3DXVECTOR2( 0.3f, 0.22f ) },  //左上
        { D3DXVECTOR3(  1.0f,  1.0f, 0.0f ), D3DXVECTOR2( 0.7f, 0.22f ) },  //右上
        { D3DXVECTOR3( -1.0f, -1.0f, 0.0f ), D3DXVECTOR2( 0.3f, 0.65f ) },  //左下
        { D3DXVECTOR3(  1.0f, -1.0f, 0.0f ), D3DXVECTOR2( 0.7f, 0.65f ) },  //右下
    };
    SimpleVertex vertices[VER_N] =
    {
        { D3DXVECTOR3( -1.0f,  1.0f, 0.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },   //左上
        { D3DXVECTOR3(  1.0f,  1.0f, 0.0f ), D3DXVECTOR2( 2.0f, 0.0f ) },   //右上
        { D3DXVECTOR3( -1.0f, -1.0f, 0.0f ), D3DXVECTOR2( 0.0f, 3.0f ) },   //左下
        { D3DXVECTOR3(  1.0f, -1.0f, 0.0f ), D3DXVECTOR2( 2.0f, 3.0f ) },   //右下
    };
    */
    
    DWORD indices[IDX_N] =
    {
        0,1,2,
        2,1,3,
    };
    
    ID3D10Effect*               g_pEffect = NULL;
    ID3D10InputLayout*          g_pLayout = NULL;
    ID3D10EffectTechnique*      g_pRender = NULL;
    ID3D10Buffer*               g_pVertexBuffer = NULL;
    ID3D10Buffer*               g_pIndexBuffer = NULL;
    ID3D10EffectMatrixVariable* g_pWorldVariable = NULL;
    ID3D10EffectMatrixVariable* g_pViewVariable = NULL;
    ID3D10EffectMatrixVariable* g_pProjectionVariable = NULL;
    ID3D10ShaderResourceView*   g_pTextureRV = NULL;
    ID3D10EffectShaderResourceVariable* g_pDiffuseVariable = NULL;
    D3DXMATRIX                  g_World;
    D3DXMATRIX                  g_View;
    D3DXMATRIX                  g_Projection;
    
  3. OnD3D10CreateDevice() で DirectX とアプリケーションの初期化を行います。
    D3DX10CreateEffectFromFile() で TexRect.fx を入力して Shader を作成します。
    Shader のプログラムも Main Program と同様にアプリケーションに応じて作成します。
    今回はポリゴンにテクスチャを貼り付ける Shader を作成してみましょう。 ヽ(^^ )
    g_pRender, g_pWorldVariable, g_pViewVariable, g_pProjectionVariable, g_pDiffuseVariableを設定します。
    Shader の作成で説明しますが、"Render","World","View","Projection","txDiffuse" は Shader の中で定義します。
    続いて input layout を設定します。
    今回の頂点データは、三次元座標とテクスチャの座標を定義しています。
    vertices[VER_N] で定義した頂点座標を VertexBuffer を取得して格納します。
    indices[IDX_N] で定義した index を VertexBuffer に格納します。
    矩形は、二個の三角形を組み合わせた TRIANGLELIST で定義します。
    テクスチヤ画像を入力して g_pTextureRV を設定します。
    L"c:\\data\\test\\ayu.jpg" がテクスチヤに使用する JPEG 画像のファイルです。
    画像はページの先頭から取得して下さい。
    g_World, g_View, g_pTextureRV にレンダリング環境の初期値を設定します。
    HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
                                          void* pUserContext )
    {
        HRESULT hr = S_OK;
    
        // Read the D3DX effect file
        DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
        #if defined( DEBUG ) || defined( _DEBUG )
        dwShaderFlags |= D3D10_SHADER_DEBUG;
        #endif
        hr = D3DX10CreateEffectFromFile( L"TexRect.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0, pd3dDevice, NULL, NULL, &g_pEffect, NULL, NULL );
        if( FAILED( hr ) )
        {
            MessageBox( NULL, L"The FX file cannot be located.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
            V_RETURN( hr );
        }
    
        g_pRender = g_pEffect->GetTechniqueByName( "Render" );
        g_pWorldVariable = g_pEffect->GetVariableByName( "World" )->AsMatrix();
        g_pViewVariable = g_pEffect->GetVariableByName( "View" )->AsMatrix();
        g_pProjectionVariable = g_pEffect->GetVariableByName( "Projection" )->AsMatrix();
        g_pDiffuseVariable = g_pEffect->GetVariableByName( "txDiffuse" )->AsShaderResource();
    
        // Define the input layout
        D3D10_INPUT_ELEMENT_DESC layout[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },  
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, 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 );
    
        // Create vertex buffer
        D3D10_BUFFER_DESC bd;
        bd.Usage = D3D10_USAGE_DEFAULT;
        bd.ByteWidth = sizeof( SimpleVertex ) * VER_N;
        bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
        bd.CPUAccessFlags = 0;
        bd.MiscFlags = 0;
        D3D10_SUBRESOURCE_DATA InitData;
        InitData.pSysMem = vertices;
        V_RETURN( pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer ) );
    
        // Set vertex buffer
        UINT stride = sizeof( SimpleVertex );
        UINT offset = 0;
        pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
    
        // Create index buffer
        bd.Usage = D3D10_USAGE_DEFAULT;
        bd.ByteWidth = sizeof( DWORD ) * IDX_N;
        bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
        bd.CPUAccessFlags = 0;
        bd.MiscFlags = 0;
        InitData.pSysMem = indices;
        V_RETURN( pd3dDevice->CreateBuffer( &bd, &InitData, &g_pIndexBuffer ) );
    
        // Set index buffer
        pd3dDevice->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );
    
        // Set primitive topology
        pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
    
        // Load the Texture
        hr = D3DX10CreateShaderResourceViewFromFile( pd3dDevice, L"c:\\data\\test\\ayu.jpg", NULL, NULL, &g_pTextureRV, NULL );
    
        // Initialize the world matrices
        D3DXMatrixIdentity( &g_World );
    
        // Initialize the view matrix
        D3DXVECTOR3 Eye( 0.0f, 0.0f, -3.0f );
        D3DXVECTOR3 At( 0.0f, 0.0f, 0.0f );
        D3DXVECTOR3 Up( 0.0f, 1.0f, 0.0f );
        D3DXMatrixLookAtLH( &g_View, &Eye, &At, &Up );
    
        // Update Variables that never change
        g_pViewVariable->SetMatrix( (float*)&g_View );
        g_pDiffuseVariable->SetResource( g_pTextureRV );
    
        return S_OK;
    }
    
  4. OnD3D10ResizedSwapChain() はウインドウのサイズが変更されたときに呼ばれます。
    ここで画像が横長/縦長にならないように g_Projection を設定します。
    HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                              const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {
        // Setup the projection parameters again
        float fAspect = static_cast<float>( pBackBufferSurfaceDesc->Width )/static_cast<float>( pBackBufferSurfaceDesc->Height );
        D3DXMatrixPerspectiveFovLH( &g_Projection, D3DX_PI * 0.25f, fAspect, 0.1f, 100.0f );
        g_pProjectionVariable->SetMatrix( (float*)&g_Projection );
    
        return S_OK;
    }
    
  5. OnD3D10FrameRender() は Rendering を行う CALLBACK 関数です。
    Shader に g_pWorldVariable を設定して矩形を描画します。
    void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
    {
        // Clear the back buffer
        float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; // red, green, blue, alpha
        ID3D10RenderTargetView* pRTV = DXUTGetD3D10RenderTargetView();
        pd3dDevice->ClearRenderTargetView( pRTV, ClearColor );
    
        // Clear the depth stencil
        ID3D10DepthStencilView* pDSV = DXUTGetD3D10DepthStencilView();
        pd3dDevice->ClearDepthStencilView( pDSV, D3D10_CLEAR_DEPTH, 1.0, 0 );
    
        // Update variables that change once per frame
        g_pWorldVariable->SetMatrix( (float*)&g_World );
    
        // Render the cube
        D3D10_TECHNIQUE_DESC techDesc;
        g_pRender->GetDesc( &techDesc );
        for( UINT p = 0; p < techDesc.Passes; ++p )
        {
            g_pRender->GetPassByIndex( p )->Apply(0);
            pd3dDevice->DrawIndexed( IDX_N, 0, 0 );
        }
    }
    
  6. OnD3D10DestroyDevice() では取得した Object を開放して下さい。
    void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
    {
        SAFE_RELEASE( g_pVertexBuffer );
        SAFE_RELEASE( g_pIndexBuffer );
        SAFE_RELEASE( g_pLayout );
        SAFE_RELEASE( g_pTextureRV );
        SAFE_RELEASE( g_pEffect );
    }
    

Shader の作成

  1. DirectX10 からは Shader が必須になったようです。 (^_^;)
    一般的にはサンプルの中から用途に合うものを選んでコピーして来るのでしょうが、 今回は「テクスチャを貼り付ける Shader」を作成してみましょう。 ヽ(^^ )
    Shader も Main Program と同様に、用途に応じて様々な Programig の方法が考えられます。
    今回はなるべく簡単に解り易くプログラムしてみました。
    立方体を光源で照らして描画する で説明した Shader も合わせて参照して下さい。
  2. Main Program から GetVariableByName() で参照される名前を定義しています。
    //--------------------------------------------------------------------------------------
    // File: Main.fx    頂点座標+テクスチャ        前田 稔
    //--------------------------------------------------------------------------------------
    
    //--------------------------------------------------------------------------------------
    // Constant Buffer Variables
    //--------------------------------------------------------------------------------------
    matrix    World;
    matrix    View;
    matrix    Projection;
    Texture2D txDiffuse;
    
  3. テクスチャ座標を設定する UV Map の指定です。
    //--------------------------------------------------------------------------------------
    // Constant Buffer Variables
    //--------------------------------------------------------------------------------------
    SamplerState samLinear
    {
        Filter = MIN_MAG_MIP_LINEAR;
        AddressU = Wrap;
        AddressV = Wrap;
    };
    
  4. Shader で使用する構造体の定義です。
    VS_INPUT で Vertex Shader(頂点シェーダ)の構造体を定義します。
    PS_INPUT で Pixcel Shader の構造体を定義します。
    //--------------------------------------------------------------------------------------
    // Vertex shader output structure
    //--------------------------------------------------------------------------------------
    struct VS_INPUT
    {
        float4 Pos : POSITION;
        float2 Tex : TEXCOORD;
    };
    
    struct PS_INPUT
    {
        float4 Pos : SV_POSITION;
        float2 Tex : TEXCOORD0;
    };
    
  5. Vertex Shader の関数です。
    VS_INPUT では Main Program で VertexBuffer に格納した三次元座標とテクスチャの座標を定義しています。
    メッシュの頂点座標を計算して PS_INPUT に格納して返します。
    PS_INPUT 構造体が Pixcel Shader のパラメータとして渡されます。
    //--------------------------------------------------------------------------------------
    // Vertex Shader
    //--------------------------------------------------------------------------------------
    PS_INPUT VS( VS_INPUT input )
    {
        PS_INPUT output = (PS_INPUT)0;
        output.Pos = mul( input.Pos, World );
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Tex = input.Tex;
        
        return output;
    }
    
  6. Pixcel Shader の関数です。
    Vertex Shader から PS_INPUT 構造体を受け取ります。
    ポリゴンに貼り付けるテクスチャのピクセルを return で返します。
    txDiffuse は g_pDiffuseVariable->SetResource( g_pTextureRV ); で設定されたテクスチャのリソースです。
    //--------------------------------------------------------------------------------------
    // Pixel Shader
    //--------------------------------------------------------------------------------------
    float4 PS( PS_INPUT input) : SV_Target
    {
        return txDiffuse.Sample( samLinear, input.Tex );
    }
    
  7. DirectX10 の Render の定義です。
    vs_4_0 は Shader のバージョンで DirectX と同様にバージョンアップが繰り返されて来ました。
    DirectX10 で快適に描画するためには、vs_4_0 に対応したビデオボードが必要になります。 (^_^;)
    VS() は Vertex Shader の関数名で、PS() は Pixcel Shader の関数名です。
    //--------------------------------------------------------------------------------------
    technique10 Render
    {
        pass P0
        {
            SetVertexShader( CompileShader( vs_4_0, VS() ) );
            SetGeometryShader( NULL );
            SetPixelShader( CompileShader( ps_4_0, PS() ) );
        }
    }
    

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