Skip to content

WPF 使用 SharpDX

Updated: at 12:43,Created: at 08:52

本文告诉大家如何在 WPF 使用 SharpDX 做绘制,本文是入门级博客

本文是一个系列

先介绍一下 SharpDx ,一个底层封装的 DirectX 库,支持 AnyCpu ,支持 Direct3D9, Direct3D11, Direct3D12,Direct2D1。支持 win32 程序和商店程序。

环境

需要 .NET 4.5 和以上的环境才可以使用。

安装

首先安装 SharpDX 的库,需要安装下面几个库

创建工厂

使用 SharpDX 和 DirectX 一样,开始都需要创建工厂,然后创建RenderTarget,之后才可以显示基础图形。

先引用命名

using D2D = SharpDX.Direct2D1;
using WIC = SharpDX.WIC;
using DW = SharpDX.DirectWrite;
using DXGI = SharpDX.DXGI;

需要在 Loaded 之后添加代码

var factory = new D2D.Factory();

创建 RenderTarget

创建 RenderTarget 可以尝试 WindowRenderTarget ,因为是入门博客,我不告诉大家如何使用其他几个 RenderTarget ,如果想知道,请自己多去看博客。

创建 WindowRenderTarget 需要参数 RenderTargetProperties ,HwndRenderTargetProperties。所以需要先创建这两个。

创建 RenderTargetProperties 需要参数 PixelFormat ,请看下面

var pixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Straight);
var renderTargetProperties = new D2D.RenderTargetProperties(D2D.RenderTargetType.Default, pixelFormat,
96, 96, D2D.RenderTargetUsage.None, D2D.FeatureLevel.Level_DEFAULT);

RenderTargetProperties 需要的参数是 RenderTargetType ,PixelFormat,dpiX,dpiY,RenderTargetUsage,FeatureLevel,参数大家看命名就知道是做什么的,在这里就不告诉大家。

创建 HwndRenderTargetProperties 请看下面代码

var hwndRenderTargetProperties = new D2D.HwndRenderTargetProperties();
hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(this).Handle;

现在尝试创建 RenderTarget 请看代码

var renderTarget = new D2D.WindowRenderTarget(factory, renderTargetProperties, hwndRenderTargetProperties);

因为需要拿到 RenderTarget 进行画基础图形,一般把 RenderTarget 放在字段。

public MainWindow()
{
InitializeComponent();
Loaded += (s, e) =>
{
var factory = new D2D.Factory();
var pixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Straight);
var hwndRenderTargetProperties = new D2D.HwndRenderTargetProperties();
hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(this).Handle;
hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(this).Handle;
hwndRenderTargetProperties.PixelSize = new Size2((int)ActualWidth, (int)ActualHeight);
var renderTargetProperties = new D2D.RenderTargetProperties(D2D.RenderTargetType.Default, pixelFormat,
96, 96, D2D.RenderTargetUsage.None, D2D.FeatureLevel.Level_DEFAULT);
_renderTarget = new D2D.WindowRenderTarget(factory, renderTargetProperties, hwndRenderTargetProperties);
};
}
private D2D.RenderTarget _renderTarget;

这里的 PixelFormat 使用 B8G8R8A8_UNorm 的意思是每个元素包含4个8位无符号分量,分量的取值范围在[0,1]区间内的浮点数,因为不是任何类型的数据都能存储到纹理中的,纹理只支持特定格式的数据存储。这里的 BGRA 的意思分别是 蓝色(Blue)、绿色(Green)、红色(Red)和 alpha(透明度),其他可以选的格式

更多概念请看DirectX11 Direct3D基本概念 - CSDN博客

画圈

现在就和 WPF 使用 Direct2D1 画图入门 差不多方法来画圈,如何可以画出来,那么就是成功使用 SharpDX 。不要问我为什么用画圈来判断是否可以使用 SharpDX,因为在所有基础的 draw 只有椭圆最耗性能。

还是使用 CompositionTarget 来知道什么时候刷新,在函数添加下面代码

CompositionTarget.Rendering += CompositionTarget_Rendering;
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
}

因为画椭圆需要三个参数,第一个是 D2D.Ellipse ,第二个是 Brush ,第三个是线条宽度。

因为 Brush 需要使用刚才的工厂创建,如果不使用工厂创建会异常

先创建 SolidColorBrush 然后创建 D2D.Ellipse

var ellipse = new D2D.Ellipse(new RawVector2(100,100),10,10 );
var brush = new D2D.SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1));

Ellipse 的三个参数是圆心和两个半径,上面是画出半径是 10 的圆。

RawColor4 就是 rgba ,颜色是从 0 到 1 ,对应 WPF 的 RGB 从 0 到 255 ,所以需要转换。

准备好几个参数,可以尝试画出来,在画之前需要使用 BeginDraw 。为什么需要调用这个函数,因为实际上调用 Draw 是不会立刻画出来,而是创建绘制命令,如果渲染是 CPU 渲染,那么就会根据命令让 CPU 在内存渲染。如果渲染 GPU 渲染,就会把命令发到 GPU ,让他渲染。

private void CompositionTarget_Rendering(object sender, EventArgs e)
{
var ellipse = new D2D.Ellipse(new RawVector2(100, 100), 10, 10);
var brush = new D2D.SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1));
_renderTarget.BeginDraw();
_renderTarget.DrawEllipse(ellipse, brush, 1);
_renderTarget.EndDraw();
}

重新告诉大家如何画出。首先拿到窗口,在 WPF 能创建的 WindowRenderTarget 最简单是拿到窗口。因为通过几个属性设置如何渲染,在哪渲染,所以还需要多使用几个属性才可以创建 D2D.WindowRenderTarget 。因为需要一个时机对 WindowRenderTarget 画出,所以我就使用 CompositionTarget 对他进行画出。

上面很多参数都没有详细说明,具体请看这位大佬的博客

简化一下的全部代码如下

using PInvoke;
using SharpDX;
using SharpDX.Direct2D1;
using System.Windows;
using System.Windows.Interop;
using SharpDX.Mathematics.Interop;
using D2D = SharpDX.Direct2D1;
using DXGI = SharpDX.DXGI;
using System.Windows.Media;
using System.Reflection;
namespace LifafaheqearNearkairliraywal;
public class Program
{
[STAThread]
public static void Main(string[] args)
{
Application application = new Application();
application.Startup += (s, e) =>
{
application.MainWindow.Show();
};
Window window = new Window();
D2DRender render = new D2DRender();
window.Loaded += (s, e) =>
{
render.Init(window);
};
application.MainWindow=window;
application.Run();
}
}
class D2DRender
{
public void Init(Window window)
{
_window = window;
var factory = new D2D.Factory();
var pixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore);
var renderTargetProperties = new D2D.RenderTargetProperties
(
// 默认的行为就是尝试使用硬件优先,否则再使用软件
D2D.RenderTargetType.Default,
// 像素格式,对于当前大多数显卡来说,选择 B8G8R8A8 是完全能支持的
// 而且也方便和其他框架,如 WPF 交互
pixelFormat,
dpiX: 96,
dpiY: 96,
D2D.RenderTargetUsage.None,
D2D.FeatureLevel.Level_DEFAULT
);
var hwndRenderTargetProperties = new D2D.HwndRenderTargetProperties();
hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(window).Handle;
ActualWidth = (int)window.ActualWidth;
ActualHeight = (int)window.ActualHeight;
hwndRenderTargetProperties.PixelSize = new Size2(ActualWidth, ActualHeight);
var renderTarget = new D2D.WindowRenderTarget(factory, renderTargetProperties, hwndRenderTargetProperties);
_renderTarget = renderTarget;
window.SizeChanged -= Window_SizeChanged;
window.SizeChanged += Window_SizeChanged;
AddRendering();
}
private int ActualWidth { set; get; }
private int ActualHeight { set; get; }
private void AddRendering()
{
CompositionTarget.Rendering -= CompositionTarget_Rendering;
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
public void CompositionTarget_Rendering(object? sender, EventArgs e)
{
Render();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
ArgumentNullException.ThrowIfNull(_window);
ArgumentNullException.ThrowIfNull(_renderTarget);
var window = _window;
ActualWidth = (int) window.ActualWidth;
ActualHeight = (int) window.ActualHeight;
_renderTarget.Resize(new Size2(ActualWidth, ActualHeight));
}
public void Render()
{
var renderTarget = _renderTarget;
if (renderTarget == null)
{
throw new InvalidOperationException();
}
renderTarget.BeginDraw();
renderTarget.Clear(new RawColor4(0.2f,0.5f,0.5f,1));
var width = Random.Shared.Next(100, 200);
var height = width;
var maxWidth = ActualWidth - width;
var maxHeight = ActualHeight - height;
var x = Random.Shared.Next(width, maxWidth);
var y = Random.Shared.Next(height, maxHeight);
var ellipse = new D2D.Ellipse(new RawVector2(x, y), width, height);
using var brush = new D2D.SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1));
renderTarget.FillEllipse(ellipse, brush);
renderTarget.EndDraw();
}
private D2D.WindowRenderTarget? _renderTarget;
private Window? _window;
}

本文的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin a582f328a967af32635cba6d08720341431c9c24

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin a582f328a967af32635cba6d08720341431c9c24

获取代码之后,进入 LifafaheqearNearkairliraywal 文件夹


知识共享许可协议

原文链接: http://blog.lindexi.com/post/WPF-%E4%BD%BF%E7%94%A8-SharpDX

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系