Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9749

C# WPFでDirect2D描画

$
0
0
やりたいこと WPFのグラフィックスモデルはとにかく遅いので、Direct2Dを使ってUWPの様にさらさら描画したい。そして、プログラム言語はC#を使いたい。巷にはC++でゴリコリ書いたものをInteropで呼び出すサンプルは見かけるのですが、それは面倒なのと敷居も高いのでなんとかしようとした内容です。 使ったもの Visual Studio 2019 Microsoft.Wpf.Interop.Directx-x64 (NuGet Package) Microsoft謹製ですが、ベータ版でソースにはTODOとか残ってます。それでも今回の目的には十分な働きをしてくれます。 SharpDX, SharpDX.DXGI, SharpDX.Direct3D11, SharpDX.Direct2D1, SharpDX.Mathematics (NuGet Package) Sample Code Visual StudioでWPF .NetFrameworkのアプリケーションを作成します。 プロジェクトのNuGetパッケージの管理から上記NuGetパッケージをインストールします。 D3D11Imageのしばりにより、プラットフォームはx64とします。 ディフォルトで作成されるMainWindow.xamlに描画したい領域を作成します。今回はMicrosoftのD3D11Imageのサンプルと同様にGridの中にImageを詰め込み、それを描画対象としました。 MainWindow.xaml <Window x:Class="SampleWpfSharpDX.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:SampleWpfSharpDX" xmlns:DXExtensions="clr-namespace:Microsoft.Wpf.Interop.DirectX;assembly=Microsoft.Wpf.Interop.DirectX" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid x:Name="D3DImgHost"> <Image Stretch="Fill"> <Image.Source> <DXExtensions:D3D11Image x:Name="D3DImg"/> </Image.Source> </Image> </Grid> </Window> そして相互作用ロジックコード側 MainWindow.xaml.cs using SharpDX; using SharpDX.Direct2D1; using SharpDX.Direct3D11; using SharpDX.DXGI; using SharpDX.Mathematics.Interop; using System; using System.Windows; using System.Windows.Interop; namespace SampleWpfSharpDX { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { private SharpDX.Direct3D11.Device _Device3D11; private SwapChain _SwapChain; private SwapChainDescription _SwapChainDescription; private SharpDX.Direct2D1.Factory _Factory2D1; private RenderTarget _RenderTarget; public MainWindow() { InitializeComponent(); D3D11ImgHost.Loaded += new RoutedEventHandler(ImageHost_Loaded); D3D11ImgHost.SizeChanged += new SizeChangedEventHandler(ImageHost_SizeChangedEventHandler); } /// <summary> /// ここに自分が描きたいコードを書きます /// 描画方法はSharpDX.Direct2D1の使い方を調べましょう /// </summary> /// <param name="rt">Direct2DのRenderTarget</param> private void OnRenderD2D(RenderTarget rt) { var width = D3D11ImgHost.ActualWidth; var height = D3D11ImgHost.ActualHeight; var rnd = new Random(); rt.Clear(new RawColor4(0.0f, 0.0f, 0.0f, 1.0f)); for (var i = 0; i < 10000; i++) { float r = (float)rnd.NextDouble(); float g = (float)rnd.NextDouble(); float b = (float)rnd.NextDouble(); float x0 = (float)(rnd.NextDouble() * width); float y0 = (float)(rnd.NextDouble() * height); float x1 = (float)(rnd.NextDouble() * width); float y1 = (float)(rnd.NextDouble() * height); var lineColor = new RawColor4(r, g, b, 1.0f); var brush = new SolidColorBrush(rt, lineColor); rt.DrawLine(new RawVector2(x0, y0), new RawVector2(x1, y1), brush); } } /// <summary> /// 描画領域関連のインスタンスができてHWNDを取得できるようになったので /// DirectXのデバイス等を作成します /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ImageHost_Loaded(object sender, RoutedEventArgs e) { int width = (int)D3D11ImgHost.ActualWidth; int height = (int)D3D11ImgHost.ActualHeight; if (width < 8) { width = 8; } if (height < 8) { height = 8; } var window = GetWindow(this); D3D11Img.WindowOwner = new WindowInteropHelper(window).Handle; D3D11Img.OnRender = D3D11Img_OnRender; HwndSource source = (HwndSource)PresentationSource.FromVisual(this); _SwapChainDescription = new SwapChainDescription() { BufferCount = 1, ModeDescription = new ModeDescription(width, height, new Rational(60, 1), Format.B8G8R8A8_UNorm), IsWindowed = true, OutputHandle = source.Handle, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput }; SharpDX.Direct3D11.Device.CreateWithSwapChain( SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.BgraSupport, new[] { SharpDX.Direct3D.FeatureLevel.Level_11_0 }, _SwapChainDescription, out _Device3D11, out _SwapChain); _Factory2D1 = new SharpDX.Direct2D1.Factory(); D3D11Img.RequestRender(); } /// <summary> /// 描画領域のサイズ変更 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ImageHost_SizeChangedEventHandler(object sender, SizeChangedEventArgs e) { // 最小サイズは適当です。そもそも不要かもしれません。 int width = (int)D3D11ImgHost.ActualWidth; int height = (int)D3D11ImgHost.ActualHeight; if (width < 8) { width = 8; } if (height < 8) { height = 8; } D3D11Img.SetPixelSize(width, height); } /// <summary> /// D3D11Imageからコールバックされるルーチン /// </summary> /// <param name="pIUnknown">D3D11ImageからはIUnknownが渡されます</param> /// <param name="isNewSurface">Surface再作成の要否</param> private void D3D11Img_OnRender(IntPtr pIUnknown, bool isNewSurface) { if (isNewSurface) { // Direct2Dを使うためのDirect3D11のSurfaceを // WPFのDirect3D9と共有させて作成する int width = (int)D3D11ImgHost.ActualWidth; int height = (int)D3D11ImgHost.ActualHeight; if (width < 8) { width = 8; } if (height < 8) { height = 8; } _SwapChainDescription.ModeDescription.Width = width; _SwapChainDescription.ModeDescription.Height = height; var dxgiResource = ComObject.As<SharpDX.DXGI.Resource>(pIUnknown); var tmpResource = _Device3D11.OpenSharedResource<SharpDX.Direct3D11.Resource>(dxgiResource.SharedHandle); var outputResource = tmpResource.QueryInterface<Texture2D>(); var surface = outputResource.QueryInterface<Surface>(); _RenderTarget = new RenderTarget(_Factory2D1, surface, new RenderTargetProperties( new PixelFormat(Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied))); _RenderTarget.AntialiasMode = AntialiasMode.PerPrimitive; _RenderTarget.TextAntialiasMode = TextAntialiasMode.Cleartype; } // SurfaceにDirect2Dで描画 _RenderTarget.BeginDraw(); OnRenderD2D(_RenderTarget); _RenderTarget.EndDraw(); _SwapChain.Present(0, PresentFlags.None); _Device3D11.ImmediateContext.Flush(); } } } 描画量が多すぎると、リサイズした時に真っ黒になったりしますが、通常の量なら大丈夫そうです。

Viewing all articles
Browse latest Browse all 9749

Trending Articles