忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[Windows® API Code Pack for Microsoft® .NET Framework] C#でDirectX11をつかう その3 三角形を表示

前回は背景を青一色に塗りつぶしました
今回は三角形を表示します。


三角形を一つ表示するだけだというのに今回は
やけにやることが増えます。
こういう所がDirectXよりもXNAのほうが
優れているところなのでしょうね

まずはコードから。

using Microsoft.WindowsAPICodePack.DirectX.Direct3D;
using Microsoft.WindowsAPICodePack.DirectX.Direct3D11;
using Microsoft.WindowsAPICodePack.DirectX.Graphics;

using System.IO;
using System.Runtime.InteropServices;

class Program
{
    static void Main()
    {
        using (Game game = new MyGame())
        {
            game.Run();
        }
    }
}

class Game : System.Windows.Forms.Form
{
    protected DeviceContext DeviceContext;
    protected SwapChain SwapChain;
    protected RenderTargetView RenderTargetView;
    
    public void Run()
    {
        D3DDevice device = initDevice();
        LoadGraphicsContent(device);
        Show();

        while (Created)
        {
            Draw();
            System.Windows.Forms.Application.DoEvents();
        }
    }

    private D3DDevice initDevice()
    {
        D3DDevice device = D3DDevice.CreateDeviceAndSwapChain(this.Handle);
        this.SwapChain = device.SwapChain;
        this.DeviceContext = device.ImmediateContext;

        using (Texture2D texture2D = SwapChain.GetBuffer<Texture2D>(0))
        {
            this.RenderTargetView = device.CreateRenderTargetView(texture2D);
            DeviceContext.OM.RenderTargets = new OutputMergerRenderTargets(new[] { RenderTargetView });
        }

        DeviceContext.RS.Viewports = new[]
        {
            new Viewport
            {
                Width = ClientSize.Width,
                Height = ClientSize.Height,
                MaxDepth = 1
            }
        };
        return device;
    }

    protected virtual void LoadGraphicsContent(D3DDevice device) { }
    protected virtual void Draw() { }

    protected unsafe D3DBuffer CreateVertexBuffer(D3DDevice device, Vector3F[] vertices)
    {
        fixed (Vector3F* fixedVertices = vertices)
        {
            return device.CreateBuffer(
                new BufferDescription
                {
                    ByteWidth = (uint)(Marshal.SizeOf(typeof(Vector3F)) * vertices.Length),
                    BindingOptions = BindingOptions.VertexBuffer,
                },
                new SubresourceData
                {
                    SystemMemory = new System.IntPtr(fixedVertices)
                }
                );
        }
    }
}

class MyGame : Game
{
    protected override void Draw()
    {
        this.DeviceContext.ClearRenderTargetView(RenderTargetView, new ColorRgba(0, 0, 1, 1));
        this.DeviceContext.Draw(3, 0);
        this.SwapChain.Present(0, PresentOptions.None);
    }

    protected override void LoadGraphicsContent(D3DDevice device)
    {
        initShadersAndInputLayout(device);
        initTriangleToDraw(device);
    }

    private void initShadersAndInputLayout(D3DDevice device)
    {
        using (Stream vertexShaderBinary = File.Open("MyShader.vs", FileMode.Open))
        {
            this.DeviceContext.VS.Shader = device.CreateVertexShader(vertexShaderBinary);
            vertexShaderBinary.Position = 0;
            this.DeviceContext.IA.InputLayout = createInputLayout(device, vertexShaderBinary);
        }

        using (Stream pixelShaderBinary = System.IO.File.Open("MyShader.ps", FileMode.Open))
        {
            this.DeviceContext.PS.Shader = device.CreatePixelShader(pixelShaderBinary);
        }
    }

    private InputLayout createInputLayout(D3DDevice device, Stream vertexShaderBinary)
    {
        return device.CreateInputLayout(
            new[] {
                    new InputElementDescription
                    {
                        SemanticName = "SV_Position",
                        Format = Format.R32G32B32Float,
                    }
                },
            vertexShaderBinary
            );
    }

    private void initTriangleToDraw(D3DDevice device)
    {
        Vector3F[] vertices = new Vector3F[]
        {
            new Vector3F(0, 0.5f, 0),
            new Vector3F(0.5f, 0, 0),
            new Vector3F(-0.5f, 0, 0)
        };

        D3DBuffer vertexBuffer = CreateVertexBuffer(device, vertices);

        this.DeviceContext.IA.SetVertexBuffers(
            0,
            new D3DBuffer[] { vertexBuffer },
            new uint[] { (uint)Marshal.SizeOf(typeof(Vector3F)) },
            new uint[] { 0 }
            );

        this.DeviceContext.IA.PrimitiveTopology = PrimitiveTopology.TriangleList;
    }
}
あまりにも長いのでクラスを2分割しました。
ある程度一般的な機能(Game)と、今回のデモに特有な部分(MyGame)とにです。
使うメソッド名などもなるべくXNAに似せます。

前回から付け加わったのはおおまかにいって
1.シェーダーの作成
2.インプットレイアウトの作成
3.頂点バッファの作成
4.ビューポートの作成

です。
もちろん生成したものはセットしてから描画します。

  説明 XNAで言うと
シェーダー(頂点シェーダとピクセルシェーダ) 頂点データがどのような処理をされて描画されるかを制御します。このコードではMyShader.vsとMyShader.psというファイルから読み込んでいます。 Effect
インプットレイアウト 頂点データの意味を表します。(何バイト目が色のデータだ、など) VertexDeclaration
頂点バッファ 描画する図形の頂点データを保持します。 VertexBuffer
ビューポート 描画先の領域です。 Viewport


このうち4.ビューポートの作成以外は描画するものに影響し
あまり抽象化するとかえってまずい気がするので、
MyGameクラスの方に持ってきます。
(ビューポートは大抵のゲームでウィンドウ全体に設定することを選ぶでしょう。)

シェーダーファイル

さらに、DirectXにはXNAのBasicEffectのような気の利いたものは無いので
シェーダーファイルも必要です。

MyShader.fx

float4 MyVertexShader(float4 position : SV_Position) : SV_Position
{
    return position;
}

float4 MyPixelShader() : SV_Target
{
    return float4(1, 1, 1, 1);
}
この意味は、ポリゴンの「頂点はそのままなにも動かさず、色は白にせよ」
です

fxc.exeを使ってシェーダーをコンパイル

さて、このエフェクト(.fx)ファイルはどうやらそのままではプログラムからは使えません。
残念ながら、このファイルをコンパイルするメソッドが
まだWindows API Code Packには実装されていないようなのです。
(DirectX10のほうはまだマシなようですが)

よってDirectX SDKのツール
fxc.exe
を使って手作業でコンパイルすることにします。
最新のDirectX SDKをダウンロードしましょう。

上のMyShader.fxを適当なフォルダに置いてください。
そしてそれと同じフォルダ内に以下のような.batファイルを作ります。
"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Utilities\bin\x86\fxc.exe" /T vs_4_0 /E MyVertexShader /Fo MyShader.vs MyShader.fx
@pause
"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Utilities\bin\x86\fxc.exe" /T ps_4_0 /E MyPixelShader /Fo MyShader.ps MyShader.fx
@pause
意味は
「MyShader.fxをコンパイルして、頂点シェーダMyShader.vsとピクセルシェーダMyShader.psを作成せよ」
です。

   
/T 対象とするプロファイル。例えばvs_4_0やps_4_0です。これを入れ違えたり間違ってもfx_4_0などにしないでください。C#側からArgumentExceptionがスローされます。
/E エントリーポイント。頂点シェーダなら頂点シェーダのメソッド名、ピクセルシェーダならピクセルシェーダのメソッド名です。
/Fo 出力ファイル名

.batを実行すると
MyShader.vsとMyShader.psが出来ます。

後はそれをVisual C#のプロジェクトに追加して、
それぞれのプロパティを[Copy if newer]にします。

実行

実行するとこうなります。

directX11TutorialDrawWhiteTriangle.jpg


























拍手[0回]

PR