LoginSignup
10
10

More than 5 years have passed since last update.

DirectX(D3D11.1)再入門 Texture

Posted at

気が変わったのでTextureを先にやる。
今回のコード

これもD3DX無き世界でどうするかとなるのだけどWindowsのWindowsImagingComponent略してWICを使ってみる。

今回のシェーダー
Texture2D diffuseTexture;
SamplerState diffuseTextureSampler;

struct VS_IN
{
    float4 Position: POSITION;
    float4 Color: COLOR;
    float2 Tex: TEXCOORD0;
};

struct VS_OUT
{
    float4 Position: SV_POSITION;
    float4 Color: COLOR;
    float2 Tex: TEXCOORD0;
};

typedef VS_OUT PS_IN;

//cbuffer buffer
//{
    float4x4 ModelMatrix;
//};

VS_OUT vsMain(VS_IN input)
{
    VS_OUT Output;
    Output.Position = mul(input.Position, ModelMatrix);
    Output.Color = input.Color;
    Output.Tex = input.Tex;
    return Output;    
}

float4 psMain(PS_IN input) : SV_TARGET
{
    float4 texel = diffuseTexture.Sample(diffuseTextureSampler, input.Tex);
    return input.Color * texel;
}

Texture2D(ShaderResourceView)とSamplerStateが増えてTextureの参照座標Texが頂点属性に追加になった。

増えた
// 定義
Texture2D diffuseTexture;
SamplerState diffuseTextureSampler;

struct VS_IN
{
    float4 Position: POSITION;
    float4 Color: COLOR;
    float2 Tex: TEXCOORD0; // 増えた
};

struct VS_OUT
{
    float4 Position: SV_POSITION;
    float4 Color: COLOR;
    float2 Tex: TEXCOORD0; // 増えた
};

// 使う
    float4 texel = diffuseTexture.Sample(diffuseTextureSampler, input.Tex);

プログラム側

デバイスからID3D11Texture2Dを作って、ID3D11Texture2DからID3D11ShaderResourceView(SRV)を作る。
SamplerStateも作る。
作ったSRVをデバイスコンテキストにセットする。
SamplerStateはセットしなくてもデフォルトのものが使われる(分かりにくい)。
謎のimageutil::ImageはWICから画像を読み込んだもの。次節で。

テクスチャクラス
class Texture
{
    Microsoft::WRL::ComPtr<ID3D11Texture2D> m_texture;
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_srv; // これがhslslのtexture2Dになる
    Microsoft::WRL::ComPtr<ID3D11SamplerState> m_sampler; // これがhlslのSamplerStateになる

public:
    bool Initialize(const Microsoft::WRL::ComPtr<ID3D11Device> &device
            , const std::shared_ptr<imageutil::Image> &image)
    {
        D3D11_TEXTURE2D_DESC desc;
        desc.Width = image->Width();
        desc.Height = image->Height();
        desc.MipLevels = 1;
        desc.ArraySize = 1;
        desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        desc.SampleDesc.Count = 1;
        desc.SampleDesc.Quality = 0;
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
        desc.CPUAccessFlags = 0;
        desc.MiscFlags = 0;

        D3D11_SUBRESOURCE_DATA initData;
        initData.pSysMem = image->Pointer();
        initData.SysMemPitch = image->Stride();
        initData.SysMemSlicePitch = image->Size();

        auto hr = device->CreateTexture2D(&desc, &initData, &m_texture);
        if (FAILED(hr)){
            return false;
        }

        D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};
        SRVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
        SRVDesc.Texture2D.MipLevels = 1;

        hr = device->CreateShaderResourceView(m_texture.Get(), &SRVDesc, &m_srv);
        if (FAILED(hr))
        {
            return false;
        }

        D3D11_SAMPLER_DESC samplerDesc;
        samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
        samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.MipLODBias = 0.0f;
        samplerDesc.MaxAnisotropy = 1;
        samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
        samplerDesc.BorderColor[0] = 0;
        samplerDesc.BorderColor[1] = 0;
        samplerDesc.BorderColor[2] = 0;
        samplerDesc.BorderColor[3] = 0;
        samplerDesc.MinLOD = 0;
        samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

        // Create the texture sampler state.
        hr = device->CreateSamplerState(&samplerDesc, &m_sampler);
        if(FAILED(hr))
        {
            return false;
        }

        return true;
    }

    void Set(const Microsoft::WRL::ComPtr<ID3D11DeviceContext> &deviceContext)
    {
        deviceContext->PSSetShaderResources(0, 1, m_srv.GetAddressOf());
        //deviceContext->PSSetSamplers(0, 1, m_sampler.GetAddressOf());
    }
};

WICで画像をロード

imageutil
    #pragma once
    #include <memory>
    #include <string>
    #include <vector>
    #include <wrl/client.h>


    struct IWICImagingFactory;

    namespace imageutil {

    class Image
    {
        std::vector<unsigned char> m_buffer;
        int m_width;
        int m_height;
        int m_pixelBytes;

    public:
        Image(int w, int h, int pixelBytes);
        int Width()const{ return m_width; }
        int Stride()const{ return m_width*m_pixelBytes; }
        int Height()const{ return m_height; }
        unsigned char* Pointer(){ return m_buffer.empty() ? nullptr : &m_buffer[0]; }
        size_t Size()const{ return m_buffer.size(); }
    };

    class Factory
    {
        IWICImagingFactory *m_factory;

    public:
        Factory();
        ~Factory();
        std::shared_ptr<Image> Load(const std::wstring &path);
    };

    }
使う
    auto wicFactory = std::make_shared<imageutil::Factory>();
        // 画像をロードする
        auto image=wicFactory->Load(textureFile);
        if(image){
            // テクスチャを作る
            if (!m_texture->Initialize(pDevice, image)){
                return false;
            }
        }

WICによる画像のロードはCOMプログラミングでございます。

WIC
    IWICImagingFactory *m_factory;
    HRESULT hr = CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&m_factory)
        );

    // decoder作ってファイルを渡す
    Microsoft::WRL::ComPtr<IWICBitmapDecoder> decoder;
    HRESULT hr = m_factory->CreateDecoderFromFilename(path.c_str(), 0
            , GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder);
    if(FAILED(hr)){
        return nullptr;
    }

    // decoderからframeを取得
    Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> frame;
    hr = decoder->GetFrame(0, &frame);
    if(FAILED(hr)){
        return nullptr;
    }

    // フレームからサイズとピクセルフォーマットとデータを得る
    UINT width, height;
    hr = frame->GetSize( &width, &height );
    if(FAILED(hr)){
        return nullptr;
    }

    assert( width > 0 && height > 0 );

    // Determine format
    WICPixelFormatGUID pixelFormat;
    hr = frame->GetPixelFormat(&pixelFormat);
    if (FAILED(hr)){
        return nullptr;
    }

    if (pixelFormat != GUID_WICPixelFormat32bppRGBA){
        // 変換する
        Microsoft::WRL::ComPtr<IWICFormatConverter> FC;
        hr = m_factory->CreateFormatConverter(&FC);
        if(FAILED(hr)){
            return nullptr;
        }

        hr = FC->Initialize(frame.Get(), GUID_WICPixelFormat32bppRGBA
                , WICBitmapDitherTypeErrorDiffusion
                , 0, 0, WICBitmapPaletteTypeCustom);
        if(FAILED(hr)){
            return nullptr;
        }

        // copy
        auto image=std::make_shared<Image>(width, height, 4);
        FC->CopyPixels(0, image->Stride(), image->Size(), image->Pointer());
        return image;
    }
    else{
        // copy
        auto image = std::make_shared<Image>(width, height, 4);
        frame->CopyPixels(0, image->Stride(), image->Size(), image->Pointer());
        return image;
    }

WICで大概の画像(tgaは無かった)はロードできる。
GetPixelFormatで画素のフォーマットを得てID3D11Textureで使いやすい形に変換しましょうということであります。
要するにGUID_WICPixelFormat32bppRGBA以外をDXGI_FORMAT_R8G8B8A8_UNORMに相当するGUID_WICPixelFormat32bppRGBAに変換してやるといいのではないか。

ss.png

次回、賢いコンスタントバッファ。予定。

参考

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10