~プログラミング~ DirectX 11でテクスチャマッピング

 

C++言語でDirectX 11を使ったアプリケーションを開発してみよう。

今回はテクスチャマッピングに挑戦してみようと思います。

 

テクスチャマッピングとは、オブジェクトの表面に壁紙のように画像を貼り付ける手法で、少ないポリゴン数でも豊かな表現が可能になります。

 

 


テクスチャ座標 (UV座標)

 

オブジェクトのどの位置に画像のどの部分が張り付くようにするかを指定する為テクスチャの座標系を理解する必要があります。

 

テクスチャ座標は画像の幅・高さを 0~1 の範囲で表します。

また、画像の横方向をU軸、縦方向をV軸と呼びます。


画像ファイルの読み込み

画像ファイルの読み込みにはDirectXTexライブラリを使うと便利です。

DirectXTexライブラリは Windows SDKには含まれておらず、自身でダウンロードするなど準備が必要ですが、導入しておきましょう。

導入方法はこちら(~プログラミング~ DirectXTexライブラリを導入しよう)を参照してください。

 

DirectXTexライブラリ内のWICTextureLoader.hファイルにあるCreateWICTextureFromFile関数を使うとJPEG形式やPNG形式などメジャーな画像ファイルを読み込む事が出来ます。

    //テクスチャ読み込み
    hr = CreateWICTextureFromFile( m_pDevice, _T( "sample.png" ), &m_pTexture, &m_pTextureView );
    if ( FAILED( hr ) )
        return hr;

第2引数に画像ファイル名を渡します。

正しく読み込めれば第3引数(ID3D11Resource)と第4引数(ID3D11ShaderResourceView)が作成されます。


サンプラーの作成

テクスチャがオブジェクトに張り付く時には、引き延ばされたり縮められたりします。

拡大縮小される時の補間方法などを指定するのがサンプラーの役割です。

    D3D11_SAMPLER_DESC smpDesc;
    
    ::ZeroMemory( &smpDesc, sizeof( D3D11_SAMPLER_DESC ) );
    smpDesc.Filter   = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    smpDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
    smpDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    smpDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
    hr = m_pDevice->CreateSamplerState( &smpDesc, &m_pSampler );
    if ( FAILED( hr ) )
        return hr;

Filterメンバーは拡大・縮小時の色の取得方法です。

D3D11_FILTERを参照してください)

 

AddressU・AddressV・AddressWメンバーはUV座標が0~1の範囲外の場合の色の取得方法です。

D3D11_TEXTURE_ADDRESS_MODEを参照してください)

 


テクスチャとサンプラーをシェーダーへ渡す

テクスチャとサンプラーが作成できたらピクセルシェーダーが参照できるようにセットします。

ID3D11DeviceContext::PSSetShaderResourcesメソッドでテクスチャを、

ID3D11DeviceContext::PSSetSamplersメソッドでサンプラーをセットします。

    m_pImmediateContext->PSSetShaderResources( 0, 1, &m_pTextureView );
    m_pImmediateContext->PSSetSamplers( 0, 1, &m_pSampler );

頂点データにUV座標を追加する

テクスチャを張り付ける位置を指定する為、頂点データにUV座標を持たせます。

struct Vertex {
    float pos[ 3 ];
    float nor[ 3 ];
    float tex[ 2 ];
};

D3D11_INPUT_ELEMENT_DESC g_VertexDesc[] {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,                            0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

上記の例では、Vertexが頂点の構造体です。

頂点座標posと法線norに加えてUV座標texを持たせています。

 

頂点のレイアウトを定義するD3D11_INPUT_ELEMENT_DESCには"TEXCOORD"セマンティクスを使います。


頂点シェーダー

struct VS_IN
{
    float4 pos : POSITION0;
    float4 nor : NORMAL0;
    float2 tex : TEXCOORD0;
};

struct VS_OUT
{
    float4 pos  : SV_POSITION;
    float2 tex  : TEXCOORD0;
};

cbuffer ConstantBuffer
{
    float4x4 world;         //ワールド変換行列
    float4x4 view;          //ビュー変換行列
    float4x4 projection;    //透視射影変換行列
}

VS_OUT vs_main( VS_IN input )
{
    VS_OUT output;

    output.pos  = mul(input.pos,  world);
    output.pos  = mul(output.pos, view);
    output.pos  = mul(output.pos, projection);

    output.tex  = input.tex;

    return output;
}

シェーダーへ入力されるVS_INと出力のVS_OUTへUV座標のtexを追加しています。(5,11行目)

頂点シェーダーでUV座標は特に何もしていません。そのまま値をピクセルシェーダーへ引き渡しています。(29行目)


ピクセルシェーダー

struct PS_IN {
    float4 pos  : SV_POSITION;
    float2 tex  : TEXCOORD0;
};

Texture2D    myTexture : register(t0); //テクスチャー
SamplerState mySampler : register(s0); //サンプラー


float4 ps_main( PS_IN input ) : SV_Target
{
    return myTexture.Sample(mySampler, input.tex);
}

シェーダーへ入力されるPS_INとへUV座標のtexを追加しています。(3行目)

テクスチャを参照する為"t"で始まるレジスターを定義します。(6行目)

サンプラーを参照する為"s"で始まるレジスターを定義します。(7行目)

テクスチャのSampleメソッドにサンプラーとUV座標を渡して色を取得します。(12行目)

コメントをお書きください

コメント: 3
  • #1

    マナト (水曜日, 10 10月 2018 17:47)

    急で申し訳ないのですが、トーンマップの制作がわからなく最初に何をすればいいのかわかりません。一応、テクスチャマッピングまではできたのですが、そっからのトーンマップで手が止まってしまい、できたらヒントだけでも教えてもらえれば幸いです。

  • #2

    AraramiStudio (水曜日, 10 10月 2018 19:48)

    マナトさん
    コメントありがとうございます。

    トーンマッピングというとハイダイナミックレンジでレンダリングしてから輝度範囲をいい感じに変換して表示するというようなやつでしょうか?
    私もまだ実践したことが無く残念ながらお力になれそうにありません。

  • #3

    マナト (水曜日, 17 10月 2018 17:31)

    返信ありがとうございます。
    そうなんですね。
    わかりました、自分なり答えを探してみます。!!
    ありがとうございます!