カテゴリー「SharpDX」の14件の投稿

2018年7月 7日 (土)

スクリーンショット・・・?

前回の書き込みから半年以上が経過してしまいました?
 
 
久しぶりのSharpDXの書き込みネタは、画面のハードコピー(スクリーンショット)です。
方法としては、SharpDX.Direct2D1.Texture2Dを
いったん、System.Drawing.Bitmapにコピーしてから、ファイル保存します。
 
 
 
 
 まずは、FormなりComponentのKeyUpイベントで、PrintScreenキーを拾います。
どうやら、KeyDownイベントは起きないようなので、KeyUpにて。
 
PrintScreenが押されたタイミングで、スクリーンショットフラグを立てます。
//キーアップ設定
this.KeyUp += (sender, e) => {
    if (e.KeyCode == Keys.PrintScreen) {
        screenShotFlag = true;
    }
};

 

 描画ルーチンからスクリーンショット呼び出し。 backBufferを丸投げして、スクリーンショットを取ります。

//描画終了
d2dRenderTarget.EndDraw();
swapChain.Present(0, PresentFlags.None);
if (screenShotFlag == true) {
    screenShotFlag = false;
    SaveScreenShot(backBuffer);
}

 

 ここからスクリーンショット実行。 まずは、backBufferのままだと使えないので、新しいTexture2DにbackBufferをコピーします。

準備で作成している4つのインスタンスは、外だしにして永続化したほうがいいかもしれません。(でないと、2回目のスクリーンショットでエラーになります)

public void SaveScreenShot(Texture2D srcTexture) {
    //準備
    var ssFactory = new Factory1();
    var ssAdapter = ssFactory.GetAdapter1(0);
    var ssOutput = ssAdapter.GetOutput(0);
    var ssOutput1 = ssOutput.QueryInterface();

    //Textureコピー準備     var desc = new Texture2DDescription() {         Width = srcTexture.Description.Width,         Height = srcTexture.Description.Height,         MipLevels = 1,         ArraySize = 1,         Format = srcTexture.Description.Format,         Usage = ResourceUsage.Staging,         SampleDescription = new SampleDescription(1, 0),         BindFlags = BindFlags.None,         CpuAccessFlags = CpuAccessFlags.Read,         OptionFlags = ResourceOptionFlags.None,     };     using (var copyTexture = new Texture2D(Device, desc)) {         //Textureコピー         DeviceContext.CopyResource(srcTexture, copyTexture);         var mapSource = Device.ImmediateContext.MapSubresource(
            copyTexture, 0, MapMode.Read,
            SharpDX.Direct3D11.MapFlags.None);         var sourcePtr = mapSource.DataPointer;//コピー元ポインタ

 

 次に、コピー先のSystem.Drawing.Bitmapに、Texture2Dの内容をコピーします。

Bitmapの色深度を32bitにしようと思ったのですが、なかなか上手くいかなかったので
24bitカラーにしています。

Bitmapは32bitカラー(つまりアルファ)は対応していないので、それが理由かと思います。

        //System.Drawing.Bitmapの作成
        using (var gdiBitmap = new System.Drawing.Bitmap(desc.Width, desc.Height,
            System.Drawing.Imaging.PixelFormat.Format24bppRgb)) {             //コピー先             var boundsRect = new System.Drawing.Rectangle(0, 0, desc.Width, desc.Height);             var mapDest = gdiBitmap.LockBits(
                boundsRect,
                System.Drawing.Imaging.ImageLockMode.WriteOnly,
                gdiBitmap.PixelFormat);             var destPtr = mapDest.Scan0;//コピー先ポインタ

            //ここから、byte単位でコピー             for (int y = 0; y < desc.Height; y++) {                 for (int x = 0; x < desc.Width; x++) {                     Utilities.CopyMemory(destPtr, sourcePtr, 3);//B, G, Rをコピー                     destPtr = IntPtr.Add(destPtr, 3);                     sourcePtr = IntPtr.Add(sourcePtr, 4);//Aは無視                 }             }             //LockBitsの解除             gdiBitmap.UnlockBits(mapDest);

 
 最後にBitmapをファイルに保存。

            //JPEG保存
            gdiBitmap.Save("Sample.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
        }
        //Resource解除
        DeviceContext.UnmapSubresource(copyTexture, 0);
    }
}

 

 

細かい凡ミスもあったことで、思ったより時間がかかりましたが、
とりあえず実装できてめでたしめでたし。

(AddIntptrの戻り値を受けるの忘れてたり
Windowsに入っている画像ビューワ(つまりフォト)が壊れていたのか
画像が見れなかったのを、プログラム側のミスと思いこんでたり

 

まだSharpDX 3.1.0を使っているんですよね・・・ 4.0.0に上げるのは、まぁそのうち

 

 

 

 

2017年8月25日 (金)

シェーダーとコンパイルオプション・・・?

竜王戦を見ながらこんばんは。

羽生さんの永世7冠が見たいです。


下のソースコードの赤字のところで、どうしてもエラーになる・・・

SharpDX.D3DCompiler.ShaderBytecode bytecode = null;
bytecode = ShaderBytecode.CompileFromFile(filename, "fx_5_0", ShaderFlags.None, EffectFlags.None);
effect = new SharpDX.Direct3D11.Effect(d3dDevice, bytecode);

基幹ソース以外は全部コメントアウトしたのに

INIFILEも参照オブジェクトも、他のプロジェクトと同じにしたのに

 

 

迷って迷って、なんとなくコンパイルオプションを見たら

Photo

この設定が、他の(ちゃんと動いている)プロジェクトと違っていました

 

32ビットを優先にチェックしたら、ちゃんと動くように

 

以上、あまり汎用性が無いネタでした

 

 

 

2017年4月16日 (日)

Texture2DArray・・・?

前々回は、2Dスプライトのインスタンシングを行いました。

それを3Dに拡張して記事にしよう・・・と思ったのですが、

 

gameBox.DeviceContext.Draw(3 * 2, 0);

gameBox.DeviceContext.Draw(indexCount, indexFrom);

のように変えるだけ(三角形2枚描画から、三角形3枚以上描画に変えるだけ)なので、
記事にしようがありませんでした・・・

 

ちなみに、インスタンシング前と後。。。見た目変わらぬ

BeforeAfter1_4

インスタンシング前は描画に24~25msかかって、40FPSを切るくらいのスピードです。
インスタンシング後だと描画に6ms前後、110FPS程度のスピードになります。

なお、16,705個のオブジェクトを描画しています。
(1回Drawで512個描画しているので、Drawを33回行っているハズです)

 
 
 

ということで、今回は、インスタンシングした3Dオブジェクトにテクスチャを貼ってみようかと思います。

 

各オブジェクトに同じテクスチャを貼るだけなら、Texture2Dを使えばよいので、

After2

これは簡単

 

今回はTexture2DArrayを使って、ヘクスごとに違うテクスチャを貼ってみることにします。

・・・と思ったのですが、いくらネット情報通りにやってもできない!

 

下がネット通りにやった結果。

After3

緑色の部分がTexture2Dを使った描画です。

その間に、Texture2DArrayを使って森とか山とかを描画しているはずなのですが、
いくら設定を見直しても、隙間になってしまいます・・・


できない無理なので今回はギブアップ

 

 

 

・・・だと悲しいので、情報整理と代替方法を記事にしてみます。

 

まずはシェーダー側の設定。

Arrayの何番を表示するかは、コンスタントバッファに番号を設定して
シェーダーに渡すだけなので割愛。

今回は、instNoに応じてテクスチャーを変えます。(手抜き

//テクスチャーバッファー
Texture2D picture : register(t0);
Texture2D picture2 : register(t1);
Texture2DArray<float4> pictureArray : register(t2);
//ピクセルシェーダー(抜粋)
float2 tex = input.tex;
float4 tc = (float4)0;
if (input.instID % 2 == 0) {
    tc = pictureArray.Sample(pictureSampler, float3(tex, input.instID % 3)) * input.color;
}
else {
    tc = picture.Sample(pictureSampler, tex) * input.color;
}
return tc;

 

instNoが奇数のときはTexture2Dを、偶数の時はTexture2DArrayを描画しています。

Texture2Dの場合はSampleの第2引数はfloat2になりますが、

Texture2DArrayの場合は第2引数がfloat3になります。
float3のz成分に、配列の何番を使用するか指定します。

 

 

次にTexture2DArrayの設定・・・は簡単で、Texture2Dと同様

ShaderResourceView multiTexture;
d3dContext.PixelShader.SetShaderResource(2, multiTexture);

とするだけです。(第1引数の2は、テクスチャーバッファーのregister(t2)に対応します)

 

次はShaderResourceViewの作成。。。の前に、Texture2Dを作成する部分

byte[] array = BitmapToByteArray(bitmap, transparentColor);
//同じサイズのTexture2Dを作成する
var desc = new Texture2DDescription() {
    Width = bitmap.Width,
    Height = bitmap.Height,
    Format = Format.B8G8R8A8_UNorm,
    MipLevels = 1,
    SampleDescription = new SampleDescription(1, 0),
    ArraySize = 1,
    BindFlags = BindFlags.None,
    Usage = ResourceUsage.Staging,
    OptionFlags = ResourceOptionFlags.None,
    CpuAccessFlags = CpuAccessFlags.Read,
};
Texture2D texture = new Texture2D(d3dDevice, desc);
d3dDevice.ImmediateContext.UpdateSubresource<byte>(array, texture, 0, bitmap.Width * 4);

BindFlagsをNone、UsageをStagingとしないと、下のMapSubresourceの際に実行時エラーになります。

BitmapToByteArrayは、System.Drawing.Bitmapからbyte[]を取得する自作関数です。
詳細は以前のブログ記事参照。  ビットマップ・・・? XNA・・・? 

 
 

読み込んだTexture2Dを、Texture2DArrayのSubresourceとして設定します。

Texture2DArrayといいつつ実際のデータ型はTexture2Dで、desc.ArraySizeに2以上の値を設定すると配列になります。

//TextureDescription
var desc = new Texture2DDescription {
    ArraySize = filenames.Count,
    Width = width,
    Height = height,
    BindFlags = BindFlags.ShaderResource,
    CpuAccessFlags = CpuAccessFlags.None,
    Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm,
    MipLevels = 1,
    SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),
    Usage = ResourceUsage.Default,
    OptionFlags = ResourceOptionFlags.None,
};
//Texture2Dの配列を作成
Texture2D textureArray = new Texture2D(d3dDevice, desc);
//Texture2Dをファイルから読み込んでArrayに格納
int n = 0;
foreach (string filename in filenames) {
    using (Texture2D texture = さっき読み込んだTexture2D) {
        DataBox mapped = d3dDevice.ImmediateContext.MapSubresource(
            texture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
        d3dDevice.ImmediateContext.UpdateSubresource(mapped, textureArray, n++);
        d3dDevice.ImmediateContext.UnmapSubresource(texture, 0);
    }
}
//Texture2DArrayからShaderResourceViewを作成
return new ShaderResourceView(gb.Device, textureArray);

とすればいいはずなのですが・・・

どうしても

After3_2

のようになっています

 

 

調べたところ、

DataBox mapped = d3dDevice.ImmediateContext.MapSubresource(
      texture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);

のときに、DataBox mappedの中身がオールゼロになっていました

つまり、完全透明なヘクスを描画していたため、歯抜けに見えてしまったようです。

 

ここで、ShaderResourceViewを作るまでの流れを整理

 ファイルからSytem.Drawing.Bitmapを作成
Bitmapからbyte[]を作成
byte[]からTexture2Dを作成
Texture2DからDataBoxを作成
DataBoxをTexture2D配列に設定
Texture2D配列からShaderResourceViewを作成

こので失敗しているのですが・・・

 

 

 

あれ、が無駄じゃね?

 

ということで、下のように組み替えてみました。

 ファイルからSytem.Drawing.Bitmapを作成
Bitmapからbyte[]を作成
' byte[]からDataBoxを作成
DataBoxをTexture2D配列に設定
Texture2D配列からShaderResourceViewを作成

 

’の、System.Drawing.BitmapからDataBoxの作成は、下のようになります。

private static DataBox BitmapToDataBox(System.Drawing.Bitmap bitmap, GBColor transparentColor) {
    byte[] array = BitmapToByteArray(bitmap, transparentColor);
    IntPtr p = Marshal.AllocCoTaskMem(array.Length);
    Marshal.Copy(array, 0, p, array.Length);
    return new DataBox(p, bitmap.Width * 4, bitmap.Width * bitmap.Height * 4);
    //DataBoxを使い終わるまで、pをFreeすることはできません。
}

Bitmapから取得したbyte[]を、アンマネージドのIntPtrにコピーして、IntPtrからDataBoxを作成します。

 

作成したDataBoxをDataBox[]に格納して、まとめてTexture2D配列を作成。

DataBox[] boxes = new DataBox[filenames.Count];
int n = 0;
foreach(var filename in filenames) {
    using (var bitmap = new System.Drawing.Bitmap(filename)) {
        boxes[n++] = BitmapToDataBox(bitmap, GBColor.Transparent0);
    }
}
Texture2D textureArray = new Texture2D(d3dDevice, desc, boxes);
foreach (var b in boxes) {
    Marshal.FreeCoTaskMem(b.DataPointer);//ここでアンマネージドメモリ解放
}
return new ShaderResourceView(d3dDevice, textureArray);

After5

 

無事完成

今回はなかなか難産でした・・・

 

 

 

2017年4月 1日 (土)

DepthBuffer・・・?

DepthBufferとは、

「3次元コンピュータグラフィックスにおいて、深度情報を用いて物体の描画処理を省略し高速化するための技術、およびその深度情報を格納するメモリ領域のことを指す」

とのことです。 (Wikipediaより)

 

ものすごくいまさらな記事です。

過去ログを見返したところ、2011年に記事にしていました。(ZBuffer・・・?

 

ですが、プログラムをいろいろと弄っていたところ、
いつのまにかDepthBufferが効いていない ・・・ という状態に・・・

 

昔の記事はSlimDX時代でしたので、改めてSharpDXのDepthBuffer作成部分を抜き出して
記事にしたいと思います。

ま、ソースを貼り付けるだけなのですけどね



まずは初期設定から。深度情報を書き込み先の作成です。

floatの2次元配列となる、Texture2D depthBufferを作成して、そこからdepthViewを作成します。
depthViewを作成したら、depthBufferは破棄しても構わないようです。

//DepthBuffer作成
private void CreateDepthBuffer(int width, int height) {
    Texture2DDescription depthDesc = new Texture2DDescription() {
        Width = width,
        Height = height,
        MipLevels = 1,
        ArraySize = 1,
        Format = Format.D32_Float,
        SampleDescription = new SampleDescription(1, 0),
        Usage = ResourceUsage.Default,
        BindFlags = BindFlags.DepthStencil,
        OptionFlags = ResourceOptionFlags.None,
    };
    depthBuffer = new Texture2D(d3dDevice, depthDesc);
    //Depth View作成
    SharpDX.Direct3D11.DepthStencilViewDescription dsvDesc = new DepthStencilViewDescription() {
        Format = depthDesc.Format,
        Dimension = DepthStencilViewDimension.Texture2D,
        Flags = DepthStencilViewFlags.None,
    };
    depthView = new DepthStencilView(d3dDevice, depthBuffer, dsvDesc);
    depthBuffer.Dispose();
}

以上で初期設定終わり。

 

 

次は、DepthBufferを使ったり使わなかったりします。

Direct3Dで2Dスプライトを表示する場合など、
DepthBufferを使いたくない場合もあるので、
描画の際に、DepthBufferを有効にしたり無効化したりしています。

public void DepthBufferOn() {
    DepthStencilStateDescription stateDesc = new DepthStencilStateDescription() {
        IsDepthEnabled = true,
        IsStencilEnabled = false,
        DepthWriteMask = DepthWriteMask.All,
        DepthComparison = Comparison.LessEqual,
    };
    DepthStencilState depthState = new DepthStencilState(d3dDevice, stateDesc);
    d3dContext.OutputMerger.SetDepthStencilState(depthState);
    d3dContext.OutputMerger.SetTargets(depthView, renderView);
}
public void DepthBufferOff() {
    DepthBufferOn();
    DepthStencilStateDescription stateDesc = new DepthStencilStateDescription() {
        IsDepthEnabled = false,
        IsStencilEnabled = false,
        DepthWriteMask = DepthWriteMask.All,
        DepthComparison = Comparison.Always,
    };
    DepthStencilState depthState = new DepthStencilState(d3dDevice, stateDesc);
    d3dContext.OutputMerger.SetDepthStencilState(depthState);
    d3dContext.OutputMerger.SetTargets(renderView);
}

 

描画の前に、DepthBufferをクリアしましょう。

public void ClearDepthBuffer() {
    d3dContext.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
}

以上でできるはず

Ng

あれれ・・・?

 

陰影処理がうまく動いていません・・・

※手前から奥に向かって書いていますが、手前に書いた絵を奥の絵が上書きしてしまっています。

 

 

というのが今の状態です


ネットで調べても、上記の設定で良さそうなのですが・・・

 

 

そこで、ふと目に留まったのが、下記のコード

var vp = new SharpDX.Mathematics.Interop.RawViewportF() {
    X = 0,
    Y = 0,
    Width = width,
    Height = height,
};
d3dContext.Rasterizer.SetViewports(new[] { vp });

 

Viewportとは、MSDN曰く、

「ビューポートは、3D シーンが射影されるレンダリング サーフェイスのサイズを定義する 2D の矩形です」

とのこと。

 

Viewportに、Depthの範囲も指定できるじゃないですか

 

どうやら、SharpDX 2.6.3から3.1.0に変更した際に、MinDepth と MaxDepthの指定が漏れてしまっていたようです。。。

 

SVNによると2か月ほど前にコミットしたコードなので、2か月間こんな状態だったということか。

んー・・・誠に遺憾である

 

ここを

d3dContext.Rasterizer.SetViewport(0, 0, width, height, 0f, 1f);

(0 と1 は、デフォルトで設定されるので省略可)

と書き換えて、、、


Good

治った

 

 

結論としては、DepthBufferを使う際はViewport設定も忘れずに・・・

 

ということではなく、

ちゃんとリグレッションテストを行いましょうね、、、ということでした

 

 

ちゃんちゃん

 

 

今回の画像は、ジュエルセイバー様からお借りしたものです。

全コンテンツ公開二次利用フリー

私のような、絵が描けない趣味プログラマーには、大変ありがたいです

 

 

そういやエイプリルフールなのにウソネタ忘れてた・・・

このブログの記事はすべて間違っています

(間違いが含まれることは否定しません

 

 

2017年3月26日 (日)

インスタンシング・・・?

そしてインスタンシング。

前回の記事の続きです。

 

インスタンシングとは、同じ形の3D(2Dでもいいですが)モデルを、異なるWorld座標にたくさん表示するテクニックです。

調べたところ、DirectX9だと難しそうでしたが、DirectX11ならDrawInstancedメソッドを使えばよいみたいで、思ったより簡単そうです。

 

今回は2Dモデル(スプライト)を大量に表示してみます。



普通の描画の際は、シェーダーにWorldViewProj行列を渡して、頂点座標にWorldViewProj行列を掛けて画面上の座標を求めます。

インスタンシングの場合は、ViewProj行列を1つと、World行列を配列でたくさん渡して、
頂点座標にViewProj行列とWorld行列(たくさん)という感じで頂点の画面座標を計算し、同じモデルを複数の場所に表示することになります。

 

おおざっぱに言うと、

 シェーダーにWorld座標の配列用エリアを設ける。

 シェーダーにWorld座標の配列を渡す。

 描画

 頂点座標にViewProj行列とWorld行列を掛けて、画面上の座標を求める。

といった流れになります。

 

 

 

 シェーダーにWorld座標の配列用エリアを設ける。

struct InstancingMatrix {
    float4 Position;
    float4 TexturePosition;
    float4 Color;
    float4 Dummy;
};
cbuffer SpriteInstancingBuffer : register(b9) {
    InstancingMatrix SpriteInstancingMatrix[1024];
}

単純にWorld行列を渡せば簡単なのですが、World行列は64バイトも使ってしまいます。

1つのコンスタントバッファは最大4096ベクターにですので、1つの要素あたりのサイズは
大きくしたくありません。

ですので、Positionを
 x : 表示するX座標
 y : 表示するY座標
 z : Width
 w : Height
のように設定して、行列を渡さないようにしています。

それに、World行列をシェーダー外で計算すると重そうなので・・・


 シェーダーにWorld座標の配列を渡す。

まずは、の構造体に対応する構造体をC#で定義。

[StructLayout(LayoutKind.Explicit, Size = 64)]
public struct SpriteInstancingData {
    [FieldOffset(16 * 0)]
    public Vector4 SpriteInstancingVector;
    [FieldOffset(16 * 1)]
    public Vector4 SpriteInstancingTexture;
    [FieldOffset(16 * 2)]
    public GBColor SpriteInstancingColor;
    [FieldOffset(16 * 3)]
    public Vector4 SpriteInstancingDummy;
    //Constructor
    public SpriteInstancingData(SpriteAlign align, float x, float y, float width, float height, GBColor color) {
        switch (align) {
        case SpriteAlign.TopLeft:                                       break;
        case SpriteAlign.Top:        x -= width / 2f;                   break;
        case SpriteAlign.TopRight:   x -= width;                        break;
        case SpriteAlign.Left:                        y -= height / 2f; break;
        case SpriteAlign.Center:     x -= width / 2f; y -= height / 2f; break;
        case SpriteAlign.Right:      x -= width;      y -= height / 2f; break;
        case SpriteAlign.FloorLeft:                   y -= height;      break;
        case SpriteAlign.Floor:      x -= width / 2f; y -= height;      break;
        case SpriteAlign.FloorRight: x -= width;      y -= height;      break;
        }
        SpriteInstancingVector = new Vector4(x, -y, width, height);
        SpriteInstancingTexture = new Vector4(0f, 0f, 1f, 1f);
        SpriteInstancingColor = color;
        SpriteInstancingDummy = Vector4.Zero;
    }
}

 

次に、構造体データを格納するコンスタントバッファの確保。

const int BufferCount = 1024になります。

spriteInstancingBuffer = new SharpDX.Direct3D11.Buffer(d3dDevice,
    Utilities.SizeOf<SpriteInstancingData>() * BufferCount,
    ResourceUsage.Default,
    BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

それをコンスタントバッファをシェーダーに渡します。

public void SetShaderModeSpriteInstancing() {
    if (shaderMode == ShaderMode.SpriteInstancing) {
        return;
    }
    shaderMode = ShaderMode.SpriteInstancing;
    //Pass設定
    SetEffectPass("SpriteInstancing");
    //ConstantBuffer設定
    d3dContext.VertexShader.SetConstantBuffer(8, viewProjBuffer);
    d3dContext.VertexShader.SetConstantBuffer(9, spriteInstancingBuffer);
    d3dContext.PixelShader.SetConstantBuffer(9, spriteInstancingBuffer);
}

 

次に、描画前の準備と、
 描画

public void DrawSpriteInstancing(ref Matrix viewProj, ShaderResourceView texture, SpriteInstancingData[] array, int count) {
    d3dContext.UpdateSubresource(ref viewProj, viewProjBuffer);
    d3dContext.PixelShader.SetShaderResource(0, texture);
    int n = (count - 1) / BufferCount + 1;
    SpriteInstancingData[] tempArray = new SpriteInstancingData[BufferCount];
    for (int i = 0; i < n; i++) {
        int m = (i < n - 1 ? BufferCount : count - i * BufferCount);
        Array.Copy(array, i * BufferCount, tempArray, 0, m);
        d3dContext.UpdateSubresource(tempArray, spriteInstancingBuffer);
        d3dContext.DrawInstanced(6, m, 0, 0);
    }
}

1つめのUpdateSubresourceでViewProj配列を渡し、
2つめのUpdateSubresource(ループ内)でSpriteInstancingData構造体の配列を渡して、
DrawInstancedで描画します。

1024を超えても描画できるように、
1024個ずつ区切って処理しています。


 頂点座標にViewProj行列とWorld行列を掛けて、画面上の座標を求める。

最後にシェーダー側の処理。

まずはバーテックスシェーダー

PS_IN SpriteInstancingVertexShader(VS_IN input, uint instID : SV_InstanceID) {
    int n = instID % 1024;
    float4 pos = SpriteInstancingMatrix[n].Position;
    float4x4 world = {    pos.z, 0, 0, pos.x,
                        0, pos.w, 0, pos.y,
                        0, 0, 1, 0,
                        0, 0, 0, 1 };
    float4x4 mat = mul(viewProjMatrix, world);
    //
    PS_IN output = (PS_IN)0;
    output.pos = mul(mat, float4(input.pos, 1));
    output.wpos = input.pos;
    output.wnorm = input.norm;
    output.tex = input.tex;
    output.color = input.color;
    output.instID = n;
    return output;
}

青字のところで、配列から1要素だけ取り出せます。
そこからWorld行列を作って、ViewProjに掛けて、頂点座標に掛けています。

若干蛇足ですが、ピクセルシェーダー側の処理。

float4 SpriteInstancingPixelShader(PS_IN input) : SV_Target{
    float4 c = SpriteInstancingMatrix[input.instID].Color;
    float4 v = SpriteInstancingMatrix[input.instID].TexturePosition;

    float2 tex = {
        v.x + input.tex.x * v.z,
        v.y + input.tex.y * v.w
    };
    return picture.Sample(pictureSampler, tex) * c * input.color;
}

構造体から色とテクスチャ座標を取り出して使用。
デバッグしていないので期待通りに動くかどうかは不明・・・



これで・・・

インスタンシング未使用(100×100 = 10,000スプライト = 20,000ポリゴンを描画)

1フレームあたり13ms~15ms使用するので、65FPS前後になっています。

Before

 

 

インスタンシング使用後(100×100 = 10,000スプライトを描画)

1フレームあたり2ms程度、自作ゲーム基盤では120FPSを上限にウェイトを入れているので、
ウェイトが効いて120FPS前後になっています。

After1

 

 

インスタンシング使用後その2(400×400 = 160,000スプライトを描画)

文字が見にくい・・・
1フレームあたり11ms~12ms程度、75FPS程度になりました。

After2

2Dスプライトを10万個も描画する必要はほとんどないので、
実用上問題なさそうです。

 

これで2Dのインスタンシング終わり

次は3Dに拡張か (新しい情報はなさそうなので記事にはなりません

 

おまけ。

ピクセルシェーダー側の処理を確認するため、
200×200 = 40,000スプライト ( = 80,000ポリゴン)で、それぞれにテクスチャの小領域をマッピング。

10行ごとに色も設定してみました。

ピクセルシェーダー側の処理も、うまく動いているようです。

いままで小さくて判らなかったですが、瑞鳳さんが今回のゲストでした

Omake

 

 

 

 

シェーダーパス変更・・・?

SharpDXで、1回のDrawで複数のインスタンスを描画する、インスタンシングというものをやってみようかと思います。

 

その前準備として、まずはシェーダーのパス変更。

 

インターネット上の情報はそれなりにあったのですが・・・

非常に苦労しました

 

 

苦労した点は、、、

SharpDX 2.6.3で未対応だったことでした

 

いくらネット上のサンプルの通りに書いても、EffectクラスもEffectTechniqueクラスもEffectPassクラスも存在しないといわれるはずです。

 

急遽、SharpDX 3.1.0にアップデート。

どうにかパス変更できるようになりました。

 
 

まずはシェーダー側のコード。

technique11 Render {
    pass P0 {
        SetBlendState(BlendDesc, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
        SetVertexShader(CompileShader(vs_4_0, VS()));
        SetGeometryShader(0);
        SetPixelShader(CompileShader(ps_4_0, PS()));
    }
    pass P1 {
        SetBlendState(BlendDesc, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
        SetVertexShader(CompileShader(vs_4_0, VS()));
        SetGeometryShader(0);
        SetPixelShader(CompileShader(ps_4_0, PS1()));
    }
    pass P2 {
        SetBlendState(BlendDesc, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
        SetVertexShader(CompileShader(vs_4_0, VS2()));
        SetGeometryShader(0);
        SetPixelShader(CompileShader(ps_4_0, PS()));
    }
}

特に説明するところはないですね。

テクニックRenderの中に、パスを3つ(P0,P1,P2)を作りました。


次に呼び出し側。
青字にした4行で、Effect, Technique, Passを設定しています。

シェーダーtechnique11を使うためには、fx_5_0にする必要があります。
(・・・と思います。試行錯誤していたところなので、断言できませんが

Effect effect;
EffectTechnique technique;
string passID; private void SetShader(string filename) {
    string shaderFilename = StdUtil.ReplaceFilenameByInifile(filename);
    if (System.IO.File.Exists(shaderFilename) == false) {
        throw new SuxGamesException("Shader file not found [" + shaderFilename + "]");
    }
    //シェーダーコードの読み込み。fx_5_0でないとエラー
    SharpDX.D3DCompiler.ShaderBytecode bytecode
              = ShaderBytecode.CompileFromFile(shaderFilename, "fx_5_0", ShaderFlags.None, EffectFlags.None);
    //エフェクト、テクニック、パスの設定
    effect = new SharpDX.Direct3D11.Effect(d3dDevice, bytecode);
    technique = effect.GetTechniqueByName("Render");
    var pass = technique.GetPassByName(passID = "SpriteFont");
    pass.Apply(d3dContext);

    //VertexShaderレイアウト設定
    d3dContext.InputAssembler.InputLayout = new InputLayout(
        d3dDevice,
        pass.Description.Signature,
        new[] {
                new SharpDX.Direct3D11.InputElement("POSITION", 0, Format.R32G32B32_Float, 0, 0),
                new SharpDX.Direct3D11.InputElement("NORMAL", 0, Format.R32G32B32_Float, 12, 0),
                new SharpDX.Direct3D11.InputElement("TEXCOORD", 0, Format.R32G32_Float, 24, 0),
                new SharpDX.Direct3D11.InputElement("COLOR", 0, Format.R32G32B32A32_Float, 32, 0),
            });
}

 

そしてパス変更。簡単ですね。

public void SetEffectPass(string id) {
    if (passID == id) {
        return;
    }
    passID = id;
    var pass = technique.GetPassByName(id);
    pass.Apply(d3dContext);

}

 

パスを変更した後は、ConstantBufferを再設定しなければいけないようです。

public void SetShaderModeSpriteFont() {
    if (shaderMode == ShaderMode.SpriteFont) {
        return;
    }
    shaderMode = ShaderMode.SpriteFont;
    //Pass設定
    SetEffectPass("SpriteFont");

    //ConstantBuffer設定
    d3dContext.VertexShader.SetConstantBuffer(6, spriteFontBuffer);
    d3dContext.PixelShader.SetConstantBuffer(6, spriteFontBuffer);

}


いままでは、1つのパスしか使っていませんでした。
VertexShaderはVS()、PixelShaderはPS()の、それぞれ1つだけになります。

ですので、VS()の中で2次元スプライト処理も3次元モデル処理も行っていましたし、
PS()の中でスプライト色変換したり3Dライティング処理をしたりしていました。

パス変更ができるようになったのがいい機会ですので、
処理内容ごとにシェーダーを切り替えて、ああすっきり

 

 

 

2016年3月 6日 (日)

ぶさかわ・・・?

せっかくのうるう年なので投稿しようと思ったのですが、
いつのまにか過ぎてしまっていました・・・

 

Direct3D11の記事は書いているものの、自主開発はなかなかDirect3D9から
Direct3D11への移行に踏ん切りがつきませんでした。

というのは、StackOverflowのこの記事。StackOverflow

 

Has anyone figured out how to change the color of bitmaps
when rendering with Direct2D?
With Direct3D it's a simple matter of specifying different vertex colors or
, when using the sprite batch, providing a color.
I would like to do this using Direct2D.
Thanks in advance for any help.

(Direct2DでBitmapの色を変更する方法を知りたいです。
Direct3DだったらVertexColorを変えるとか
SpriteBatchとか使えば簡単にできたのに・・・とかそんな感じ)

 
 

その回答がこちら

I have tried using the CLSID_D2D1ColorMatrix effect to tint the bitmap
and it does work but the performance is pretty terrible
(if you need to do it every frame).
(D2DColorMatrix Effect使ってみたけど、pretty terrible・・・ぶさかわ?だよね?・・・
ぶさかわってなんだーーー

I could cache the output if the color is going to change a lot
but I like to do animations over the tint on my sprites
to do flashing/damage effects and so caching wouldn't help.
(色を付けたやつをキャッシュしておいて表示する方法が考えられるけど
アニメーションさせたいから、キャッシュじゃ使いにくい)

My next course of action is to switch my renderer to D3D
and just implement the SpriteBatch shader including tinting
(or base it off of DirectX Tool Kit implementation):
(なので次の手段。Direct2DじゃなくDirect3Dを使って、
SpriteBatchを実装すればいいんじゃない?
それか、DirectX Tool Kitに実装されてるやつを使いなさい))

 

って感じでしょうか。

辞書ひいてないので、間違っているかも。。。
というか、tintの意味が解らないまま翻訳しております


ということで、Direct2Dは諦めて、Direct3D+TextureMapping+Shaderで色変更を試してみました

 

まずは、Direct3D9のSpriteの場合

Direct3d9

1983Frame/34.522sec = 57.44FPSです。

なお、横32個、縦18個のスプライトを1Frameあたり10回描画しているので、
1Frameあたり合計5,760個のスプライトを描画しています。

 

 

次はDirect2Dですが、まずは色変換なし

Direct2d_normal

1762Frame/43.099sec = 40.88FPS ※1Frameあたり5,760スプライト

Direct3D9より微妙に遅い・・・

 

 

そして、これを色変換しようとすると・・・

Direct2D + ColorMatrixEffect

なお、遅いので、1Frameあたりの描画数は576スプライトにしています。

Direct2d_effects

241Frame/42.390sec = 5.69FPS・・・かなり遅い!

 

Direct2D + LinearEffect

こちらも1Frameあたり576スプライト

Direct2d_linear

303Frame/43.095sec = 7.03FPS・・・やはり遅い!

うーん、、、ぶさかわ、もといPretty Terribleどころじゃない気がしますが・・・

 
 

Direct3D11 + TextureMapping + Shader

Direct3D11版の自作ゲーム基盤が、ようやく形になりました。

Spriteを表示するだけなら2/29に間に合ったのですが、シェーダにライトとか
実装していたので投稿間に合わず・・・)

Direct3d11

522Frame/44.010sec = 11.86FPSです。 1Frameあたり、と同じ5,760スプライト。

・・・思っていたよりスピード出ていなかった

実装したときは、申し分ないスピードがでていたと思ったのですが、
まぁ、ゲーム内ではせいぜい1,000スプライトなので、許容範囲内としておきます。

 

 

しかし、やはりDirect2Dは遅い

Direct2DにPrettyTerribleじゃない色変換機能、追加にならないかなぁ・・・

 

 

 

2016年2月26日 (金)

Texture2D・・・?

Texture2DをFromMemoryで作成したら、単色にしか表示できない~~~~




・・・

PixelShaderのほうで色を変えていたのを忘れていました


明けましておめ^h^h^h

お久しぶりです量子猫です。

いきなり2月末になってしまいました・・・
そしてしょっぱなからオオボケ・・・


今回は、SharpDX.Direct3D11におけるTexture2Dの作り方について。

ネットを探しても、なかなかまとまっているサイトが見つかりませんでした。

ので、断片的な情報を寄せ集めて、どうにか成功したので、メモメモ。

 

 

ファイルから作る

SharpDX.Direct3D11.Texture2D tex = Texture2D.FromFile<Texture2D>(d3dDevice, "xxx.png");
SharpDX.Direct3D11.ShaderResourceView res = new ShaderResourceView(d3dDevice, tex);

これは簡単。


System.Drawing.Bitmapから作る by FromMemory

//System.Drawing.Bitmapからbyteデータを作成します。
System.Drawing.ImageConverter converter = new ImageConverter();
byte[] array = (byte[])converter.ConvertTo(bitmap, typeof(byte[]));
//byteデータからTexture2Dの作成
SharpDX.Direct3D11.Texture2D tex = Texture2D.FromMemory<Texture2D>(d3dDevice, array);
SharpDX.Direct3D11.ShaderResourceView res = new ShaderResourceView(d3dDevice, tex);

byteデータは、無圧縮のbyte列ではなく、pngフォーマットのようなファイルフォーマットのようです。

StackOverflowによると、
  The method Texture2D.FromMemory is expecting the same kind of texture supported by Texture2D.FromFile,
  (Texture2D.FromMemoryは、Texture2D.FromFileと同じ形式しかサポートしませんよ)
とのこと。

http://stackoverflow.com/questions/24720340/exception-of-texture2d-frommemory-in-sharpdx-code


System.Drawing.Bitmapから作る From rawなbyteデータ

//Bitmapからrawなbyteデータを作成する
System.Drawing.Imaging.BitmapData bitmapData = null;
byte[] array;
try {
    bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height)
            , System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
    int numBytes = bitmapData.Stride * bitmapData.Height;
    array = new byte[numBytes];
    Marshal.Copy(bitmapData.Scan0, array, 0, numBytes);
} finally {
    if (bitmapData != null) {
        bitmap.UnlockBits(bitmapData);
    }
}
//同じサイズのTexture2Dを作成する
var desc = new Texture2DDescription() {
    Width = bitmap.Width,
    Height = bitmap.Height,
    Format = Format.B8G8R8A8_UNorm,
    MipLevels = 1,
    SampleDescription = new SampleDescription(1, 0),
    ArraySize = 1,
    BindFlags = BindFlags.ShaderResource,
};
SharpDX.Direct3D11.Texture2D tex = new Texture2D(d3dDevice, desc);
//Texture2DにbitmapDataを設定する
d3dContext.UpdateSubresource<byte>(array, tex, 0, desc.Width * 4);//4がかっこわるい
//リソースを作成する
SharpDX.Direct3D11.ShaderResourceView res = new ShaderResourceView(d3dDevice, tex);

rawなbyteデータはBitmapから作る必要はありません。

なので、直接byteデータを作ってTexture2Dにすることができます。

 

Direct2D.Bitmapの設定方法は前に書いたっけ・・・?

 

 

2015年11月10日 (火)

SharpDX 2.6.3導入編・・・?

Direct2DとかDirectEffectを使うための準備メモです。

 

1 using宣言
dll参照先は、2.6.3_DirectX11_2-net40フォルダのものを使っています。

using SharpDX;
using SharpDX.DXGI;
using SharpDX.Direct3D11;
using SharpDX.Direct2D1;
using SharpDX.D3DCompiler;
 

2 Direct3D11.Device作成

SharpDX.Direct3D11.Device d3dDevice = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware
    , SharpDX.Direct3D11.DeviceCreationFlags.Debug | SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport);

 

3 DXGI.Device作成
d3dDeviceからQueryInterfaceで変換します。

SharpDX.DXGI.Device dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device>();
 

4 DXGI.Factory作成

SharpDX.DXGI.Factory dxgiFactory = new SharpDX.DXGI.Factory();
 

5 Direct2D1.Factory1作成

SharpDX.Direct2D1.Factory1 d2dFactory1 = new SharpDX.Direct2D1.Factory1(FactoryType.SingleThreaded);
 

6 DXGI.SwapChain作成

SharpDX.DXGI.SwapChainDescription desc = new SharpDX.DXGI.SwapChainDescription() {
      BufferCount = 1
    , ModeDescription = new SharpDX.DXGI.ModeDescription(GraphicsWidth
                      , GraphicsHeight, new SharpDX.DXGI.Rational(60, 1)
                      , SharpDX.DXGI.Format.R8G8B8A8_UNorm)
    , IsWindowed = true
    , OutputHandle = formMain.Handle
    , SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0)
    , SwapEffect = SharpDX.DXGI.SwapEffect.Discard
    , Usage = SharpDX.DXGI.Usage.RenderTargetOutput
};
SharpDX.DXGI.SwapChain dxgiSwapChain = new SwapChain(dxgiFactory, dxgiDevice, desc);
 

7 Direct2D1.Device作成

SharpDX.Direct2D1.Device d2dDevice = new SharpDX.Direct2D1.Device(d2dFactory1, dxgiDevice);
 

8 Direct2D1.DeviceContext作成

SharpDX.Direct2D1.DeviceContext d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice
    , DeviceContextOptions.None);
 

9 バックバッファの作成

SharpDX.Direct3D11.Texture2D d3dBackBuffer = Texture2D.FromSwapChain<Texture2D>(dxgiSwapChain, 0);
 

10 DXGI.Surfaceの作成
バックバッファからQueryInterfaceで変換します。

SharpDX.DXGI.Surface dxgiSurface = d3dBackBuffer.QueryInterface<Surface>();
 

11 Direct2D1.DeviceContext.Target設定

SharpDX.Direct2D1.BitmapProperties1 properties = new BitmapProperties1(
        new PixelFormat(SharpDX.DXGI.Format.Unknown, SharpDX.Direct2D1.AlphaMode.Premultiplied)
      , 1280, 720
      , BitmapOptions.Target | BitmapOptions.CannotDraw);
d2dContext.Target = new Bitmap1(d2dContext, dxgiSurface, properties);
 

12 Direct3D11.DeviceContextの作成

SharpDX.Direct3D11.DeviceContext d3dContext = d3dDevice.ImmediateContext;
 

13 Direct3D11.RenderTargetViewの作成

SharpDX.Direct3D11.RenderTargetView d3dRenderView = new RenderTargetView(d3dDevice, d3dBackBuffer);
 

14 シェーダーの設定

//Shader準備(VertexShader)
SharpDX.D3DCompiler.ShaderBytecode vertexShaderByteCode = ShaderBytecode.CompileFromFile("ShaderPositionColor.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
SharpDX.Direct3D11.VertexShader vertexShader = new VertexShader(d3dDevice, vertexShaderByteCode); //Shader準備(PixelShader)
SharpDX.D3DCompiler.ShaderBytecode pixelShaderByteCode = ShaderBytecode.CompileFromFile("ShaderPositionColor.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
SharpDX.Direct3D11.PixelShader pixelShader = new PixelShader(d3dDevice, pixelShaderByteCode); //VertexShader Layout設定
SharpDX.Direct3D11.InputLayout layout = new InputLayout(
    d3dDevice,
    ShaderSignature.GetInputSignature(vertexShaderByteCode),
    new[] {
        new SharpDX.Direct3D11.InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
        new SharpDX.Direct3D11.InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0),
    }); //ShaderのConstantBuffer設定
d3dConstantBuffer = new SharpDX.Direct3D11.Buffer(d3dDevice
    , Utilities.SizeOf<Matrix>()
    , ResourceUsage.Default
    , BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); //Shaderステージ準備
d3dContext.InputAssembler.InputLayout = layout;
d3dContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleStrip;
d3dContext.VertexShader.Set(vertexShader);
d3dContext.VertexShader.SetConstantBuffer(0, d3dConstantBuffer);
d3dContext.Rasterizer.SetViewports(new[] { new ViewportF(0, 0, 1280, 720, 0.0f, 1.0f) });
d3dContext.PixelShader.Set(pixelShader);
d3dContext.OutputMerger.SetTargets(d3dRenderView);

 

15 αブレンド設定

SharpDX.Direct3D11.BlendStateDescription blendDescription = new SharpDX.Direct3D11.BlendStateDescription();
blendDescription.RenderTarget[0] = new RenderTargetBlendDescription() {
      IsBlendEnabled = true
    , SourceBlend = BlendOption.SourceAlpha
    , DestinationBlend = BlendOption.InverseSourceAlpha
    , BlendOperation = SharpDX.Direct3D11.BlendOperation.Add
    , SourceAlphaBlend = BlendOption.One
    , DestinationAlphaBlend = BlendOption.Zero
    , AlphaBlendOperation = SharpDX.Direct3D11.BlendOperation.Add
    , RenderTargetWriteMask = ColorWriteMaskFlags.All
};
SharpDX.Direct3D11.BlendState blendState = new BlendState(d3dDevice, blendDescription);
d3dContext.OutputMerger.SetBlendState(blendState);

 

16 DriectWrite.Factoryの作成

SharpDX.DirectWrite.Factory dwFactory = new SharpDX.DirectWrite.Factory(SharpDX.DirectWrite.FactoryType.Isolated);



2015年10月24日 (土)

Direct2D・・・?

VisualStudioが新しくなったついでに、SharpDXも新しくしてみる。

いままでは、SharpDX 2.4.2を使っていましたが、SharpDX 2.6.3にしてみました。

さらについでに、DirectX9からDirectX11にして、昔勘違いしていたDirect2Dにリベンジ

 

※2.4.2に付いていたSamples\Direct2D1\MiniRectを参考にしたものです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SharpDX;
using SharpDX.DXGI;
using SharpDX.Direct2D1;
using SharpDX.Direct3D11;

namespace Direct2D_Sample {
    public partial class Form1 : Form {
        SharpDX.Direct3D11.Device device;
        SharpDX.DXGI.SwapChain swapChain;
        SharpDX.Direct3D11.Texture2D backBuffer;
        SharpDX.Direct2D1.Factory d2dFactory;
        SharpDX.Direct2D1.RenderTarget d2dRenderTarget;
        public Form1() {
            InitializeComponent();
            //Form Initialize
            int width = 1280;
            int height = 720;
            this.Width = width + this.Width - this.ClientRectangle.Width;
            this.Height = height + this.Height - this.ClientRectangle.Height;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.StartPosition = FormStartPosition.CenterScreen;
            //SwapChain description
            SharpDX.DXGI.SwapChainDescription desc = new SharpDX.DXGI.SwapChainDescription() {
                BufferCount = 1,
                ModeDescription = new SharpDX.DXGI.ModeDescription(this.ClientSize.Width
                    , this.ClientSize.Height, new SharpDX.DXGI.Rational(60, 1), SharpDX.DXGI.Format.R8G8B8A8_UNorm),
                IsWindowed = true,
                OutputHandle = this.Handle,
                SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),
                SwapEffect = SharpDX.DXGI.SwapEffect.Discard,
                Usage = SharpDX.DXGI.Usage.RenderTargetOutput
            };
            //D3Dデバイスとスワップチェーンの作成
            SharpDX.Direct3D11.Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.Hardware
                , DeviceCreationFlags.BgraSupport, desc
                , out device, out swapChain);
            //バックバッファの作成
            backBuffer = Texture2D.FromSwapChain

(swapChain, 0);             //D2Dレンダーターゲットの作成             d2dFactory = new SharpDX.Direct2D1.Factory(FactoryType.SingleThreaded);             SharpDX.DXGI.Surface surface = backBuffer.QueryInterface

();             SharpDX.Direct2D1.PixelFormat pixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied);             SharpDX.Direct2D1.RenderTargetProperties targetProp = new RenderTargetProperties(pixelFormat);             d2dRenderTarget = new RenderTarget(d2dFactory, surface, targetProp);             //イベント設定             this.Paint += (sender, e) => Paint2D();             this.Disposed += (sender, e) => DisposeDevice();         }         public void Paint2D() {             d2dRenderTarget.BeginDraw();             d2dRenderTarget.Clear(SharpDX.Color.CornflowerBlue);             d2dRenderTarget.EndDraw();             swapChain.Present(0, PresentFlags.None);         }         public void DisposeDevice() {             d2dRenderTarget.Dispose();             d2dFactory.Dispose();             backBuffer.Dispose();             swapChain.Dispose();             device.Dispose();         }     } }

とりあえずこれで、水色のウインドウが表示されました。

 
 

より以前の記事一覧