Direct2D 独自Effectの作成(PixelShader利用)
下記記事にて、独自のEffectを作成しました。
Direct2D 独自Effectの作成 - 何でもプログラミング
Effectの効果自体は、組み込みのID2D1OffsetTransformを利用していたので、今回は独自の効果(PixelShader)を定義してみたいと思います。
下図のように、青と赤を入れ替える効果を作成してみます。
本記事は上記記事をベースとしていますので、特に説明のない部分は上記記事を参照してみてください。
シェーダコンパイラ
コンパイル済みのcsoを準備するために、今回は動的にコンパイルする関数を用意しました。
#include <d3dcompiler.h> #pragma comment(lib, "d3dcompiler.lib") ComPtr<ID3DBlob> Compile(std::string code, std::string target) { #ifdef _DEBUG UINT flags1 = D3DCOMPILE_DEBUG | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS; #else UINT flags1 = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS; #endif ComPtr<ID3DBlob> compiled, errorMessage; D3DCompile( code.c_str(), code.size(), NULL, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", target.c_str(), flags1, 0, &compiled, &errorMessage); if (errorMessage) throw std::exception((char*)errorMessage->GetBufferPointer()); return compiled; }
独自Transformクラス
ID2D1DrawTransformを継承して独自のTransformを作成します。
関数がたくさんありますが、重要なのはSetDrawInfoでSetPixelShaderを呼んでいるところくらいです。
PixelShaderの作成自体はEffectクラスのInitializeで行います。
DEFINE_GUID(GUID_MyTransformPS, ....); // 独自のGUIDを定義。 class MyTransform : public ID2D1DrawTransform { public: MyTransform() : m_refCount(1) {} // ここから5つの関数は、ID2D1DrawTransformに必要な関数です。 IFACEMETHODIMP SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo) { return pDrawInfo->SetPixelShader(GUID_MyTransformPS); } IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; } IFACEMETHODIMP MapInputRectsToOutputRect( _In_reads_(inputRectCount) const D2D1_RECT_L* pInputRects, _In_reads_(inputRectCount) const D2D1_RECT_L* pInputOpaqueSubRects, UINT32 inputRectCount, _Out_ D2D1_RECT_L* pOutputRect, _Out_ D2D1_RECT_L* pOutputOpaqueSubRect ) { if (inputRectCount != 1) return E_INVALIDARG; *pOutputRect = pInputRects[0]; *pOutputOpaqueSubRect = pInputOpaqueSubRects[0]; return S_OK; } IFACEMETHODIMP MapOutputRectToInputRects( _In_ const D2D1_RECT_L* pOutputRect, _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects, UINT32 inputRectCount ) const { if (inputRectCount != 1) return E_INVALIDARG; pInputRects[0] = *pOutputRect; return S_OK; } IFACEMETHODIMP MapInvalidRect( UINT32 inputIndex, D2D1_RECT_L invalidInputRect, _Out_ D2D1_RECT_L* pInvalidOutputRect ) const { if (inputIndex != 0) return E_INVALIDARG; *pInvalidOutputRect = invalidInputRect; return S_OK; } // ここからはCOMに必要な関数です。特別な実装はありません。 IFACEMETHODIMP_(ULONG) AddRef() { m_refCount++; return m_refCount; } IFACEMETHODIMP_(ULONG) Release() { m_refCount--; if (m_refCount == 0) delete this; return m_refCount; } IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput) { *ppOutput = nullptr; HRESULT hr = S_OK; if (riid == __uuidof(ID2D1DrawTransform)) *ppOutput = (ID2D1DrawTransform*)this; else if (riid == __uuidof(ID2D1Transform)) *ppOutput = (ID2D1Transform*)this; else if (riid == __uuidof(ID2D1TransformNode)) *ppOutput = (ID2D1TransformNode*)this; else if (riid == __uuidof(IUnknown)) *ppOutput = this; else hr = E_NOINTERFACE; if (*ppOutput != nullptr) AddRef(); return hr; } private: LONG m_refCount; };
EffectクラスのInitialize
前回の記事で作成したMyEffectのInitialize関数を、下記で置き換えます。
PixelShaderの入力とTextureは下記の値がDirect2Dから渡されますので、独自の出力を作成します。(VertexShaderを独自で作成した場合は入力を変更できます。)
IFACEMETHODIMP Initialize(_In_ ID2D1EffectContext* pContextInternal, _In_ ID2D1TransformGraph* pTransformGraph) { std::string psCode = "Texture2D InputTexture : register(t0); \n" "SamplerState InputSampler : register(s0); \n" " \n" "float4 main( \n" " float4 clipSpaceOutput : SV_POSITION, \n" " float4 sceneSpaceOutput : SCENE_POSITION, \n" " float4 texelSpaceInput0 : TEXCOORD0 \n" ") : SV_Target \n" "{ \n" " float4 color = InputTexture.Sample( \n" " InputSampler, \n" " texelSpaceInput0.xy \n" " ); \n" " return float4(color.b, color.g, color.r, color.a); \n" "} \n" ; auto cso = Compile(psCode, "ps_4_0_level_9_1"); auto hr = pContextInternal->LoadPixelShader(GUID_MyTransformPS, (BYTE*)cso->GetBufferPointer(), cso->GetBufferSize()); if (FAILED(hr)) return hr; ComPtr<MyTransform> transform; transform.Attach(new MyTransform()); return pTransformGraph->SetSingleTransformNode(transform.Get()); }