6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Windows FormsでSharpDX

Last updated at Posted at 2018-12-15

簡単なツールを作るには依然としてWindowsFormがお手軽で、3Dのビューを持ったツールを作りたいのです。

C#には ShaprDX があって D3D ができて、SharpDX.Windows.Form でWindowを作れる。
しかし、これだとWindow全部が3Dビューになるのでゲームとかを作るにはいいのだが、
ツリービューやリストビューがあって一部に3DのViewがあるWindowを作れない。

PanelとかPictureBoxみたいな3Dビューを作りたいので、やってみる。

こんな感じのやつ。
sample.jpg

動くコード
https://github.com/ousttrue/SharpDXSample

ユーザーコントロールを作る

user_control.jpg

ユーザーコントロールをビルドすると、プロジェクトのツールボックスに自作のコントロールが出てくる。

toolbox.jpg

貼り付けた。

paste.jpg

NugetでSharpDXをインストールする

SharpDX.Direct3D11 をインストールした。
SharpDXSharpDX.DXGI は依存関係でインストールされた。

nuget.jpg

ユーザーコントロールの実装

SharpDXのサンプルのコード(ビルドの仕方が分からないんだけど)を見ると Form.Handle からCreateDeviceしていると分かった。

UserControlからもHandleが取れるのでこれでやってみる。

using System;
using System.Windows.Forms;


namespace SharpDXSample
{
    public partial class D3D11Panel : UserControl
    {
        D3D11Renderer m_renderer = new D3D11Renderer();
        public D3D11Renderer Renderer
        {
            get { return m_renderer; }
        }

        public D3D11Panel()
        {
            InitializeComponent();
            m_renderer.ClearColor = new SharpDX.Color4(0, 0, 0.5f, 1.0f);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            m_renderer.BeginRendering(Handle); // HWND
            m_renderer.EndRendering();
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            m_renderer.Resize(ClientSize.Width, ClientSize.Height);
            Invalidate(); // 再描画
        }

        public void Close()
        {
            if (m_renderer != null)
            {
                m_renderer.Dispose();
                m_renderer = null;
            }
        }
    }
}

using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System;


namespace SharpDXSample
{
    public class D3D11Renderer : IDisposable
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        Viewport Viewport
        {
            get
            {
                return new Viewport(0, 0, Width, Height, 0.0f, 1.0f);
            }
        }

        public SharpDX.Color4 ClearColor { get; set; }

        SharpDX.Direct3D11.Device m_device;
        public SharpDX.Direct3D11.Device Device
        {
            get { return m_device; }
        }

        DeviceContext m_context;
        public DeviceContext Context
        {
            get { return m_context; }
        }

        DXGISwapChain m_swapChain;

        public void Dispose()
        {
            if (m_swapChain != null)
            {
                m_swapChain.Dispose();
                m_swapChain = null;
            }
            if (m_context != null)
            {
                m_context.Dispose();
                m_context = null;
            }
            if (m_device != null)
            {
                m_device.Dispose();
                m_device = null;
            }
        }

        void CreateDevice(IntPtr hWnd)
        {
            if (m_device != null)
            {
                return;
            }

            SharpDX.Configuration.EnableObjectTracking = true;

            // SwapChain description
            var desc = new SwapChainDescription()
            {
                BufferCount = 1,
                ModeDescription = new ModeDescription(Width, Height,
                    new Rational(60, 1), Format.R8G8B8A8_UNorm),
                IsWindowed = true,
                OutputHandle = hWnd,
                SampleDescription = new SampleDescription(1, 0),
                SwapEffect = SwapEffect.Discard,
                Usage = Usage.RenderTargetOutput
            };

            // Create Device and SwapChain
            SwapChain swapChain;
            SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware,
                DeviceCreationFlags.Debug,
                desc,
                out m_device, out swapChain);
            m_context = m_device.ImmediateContext;

            // Ignore all windows events
            using (var factory = swapChain.GetParent<Factory>())
            {
                factory.MakeWindowAssociation(hWnd, WindowAssociationFlags.IgnoreAll);
            }

            m_swapChain = new DXGISwapChain(swapChain);
        }

        public void Resize(int w, int h)
        {
            if (w == Width && h == Height)
            {
                return;
            }
            Width = w;
            Height = h;
            if (m_swapChain != null)
            {
                m_swapChain.Resize(w, h);
            }
        }

        public void BeginRendering(IntPtr hWnd)
        {
            CreateDevice(hWnd);

            var rtv = m_swapChain.GetRenderTargetView(m_device);
            m_context.ClearRenderTargetView(rtv, ClearColor);

            m_context.OutputMerger.SetTargets(rtv);
            m_context.Rasterizer.SetViewport(Viewport);
        }

        public void EndRendering()
        { 
            m_swapChain.Present();
        }
    }
}

form.jpg
RenderTargetをクリアするところまでできた。

ShaderとVertexBuffer

Shaderを作って、context.Draw をコールする。
要するに下記の一連の呼び出しをする。
必要なリソースを全部作る。
今回の例では、 vertex shader, pixel shader, input layout, buffer の4つだけ。

context.ClearRenderTargetView(m_renderView, clear); // バックバッファのクリア

{ // ここから
context.OutputMerger.SetTargets(renderView); // SwapChainから作成済み
context.Rasterizer.SetViewport(new Viewport(0, 0, form.ClientSize.Width, form.ClientSize.Height, 0.0f, 1.0f));
context.VertexShader.Set(vertexShader); // 作る 1
context.PixelShader.Set(pixelShader); // 作る 2

context.InputAssembler.InputLayout = layout; // 作る 3
context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, 32, 0)); // 作る 4
context.Draw(3, 0);
} // ここまで

swapChain.Present(0, PresentFlags.None); // 画面に反映

nugetで追加インストール

Shaderのコンパイルに必要な SharpDX.D3DCompiler と、Vector4 等の入った SharpDX.Mathematics をインストールした。

VertexShaderとInputLayoutとPixelShader

Devicestring から作成する。リソースの作成には、Device を使う。

Shader

コンパイル

これはDeviceとは関係なくできる。

// compile
m_vsCompiled = ShaderBytecode.Compile(m_source, "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
m_psCompiled = ShaderBytecode.Compile(m_source, "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);

作る

InputLayoutはVertexShaderの入力レイアウトの通りに作るだけなので、Shaderリフレクションで自動化できる。
後で調べる。

m_vs = new VertexShader(device, m_vsCompiled);
m_layout = new InputLayout(
                    device,
                    ShaderSignature.GetInputSignature(m_vsCompiled),
                    new[]
                        {
                        new InputElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 0, 0),
                        new InputElement("COLOR", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 16, 0)
                        });
m_ps = new PixelShader(device, m_psCompiled);

使う

context.VertexShader.Set(m_vs);
context.PixelShader.Set(m_ps);
context.InputAssembler.InputLayout = m_layout;

VertexBuffer

DeviceVector4[] から作成する。

作る

m_buffer = SharpDX.Direct3D11.Buffer.Create(device, BindFlags.VertexBuffer, m_vertices);

使う

context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(m_buffer, s_stride, 0));
context.Draw(m_vertices.Length, 0);

SharpDXでDispose漏れをチェックする

以下のコードでアクティブなオブジェクトの一覧を表示する。

Console.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());

アプリの終了時に呼ぶことで Dispose を忘れていないか確認できる。

    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            SharpDX.Configuration.EnableObjectTracking = true; // Object追跡を有効にする
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            Console.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects()); // 一覧表示
        }
    }

開発中の少し複雑なやつ

6
7
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
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?