簡単なツールを作るには依然としてWindowsFormがお手軽で、3Dのビューを持ったツールを作りたいのです。
C#には ShaprDX
があって D3D
ができて、SharpDX.Windows.Form
でWindowを作れる。
しかし、これだとWindow全部が3Dビューになるのでゲームとかを作るにはいいのだが、
ツリービューやリストビューがあって一部に3DのViewがあるWindowを作れない。
PanelとかPictureBoxみたいな3Dビューを作りたいので、やってみる。
動くコード
https://github.com/ousttrue/SharpDXSample
ユーザーコントロールを作る
ユーザーコントロールをビルドすると、プロジェクトのツールボックスに自作のコントロールが出てくる。
貼り付けた。
NugetでSharpDXをインストールする
SharpDX.Direct3D11
をインストールした。
SharpDX
と SharpDX.DXGI
は依存関係でインストールされた。
ユーザーコントロールの実装
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();
}
}
}
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
Device
と string
から作成する。リソースの作成には、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
Device
と Vector4[]
から作成する。
作る
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()); // 一覧表示
}
}
開発中の少し複雑なやつ