在上一篇博客里面告诉大家,如何使用 Vortice 从零开始控制台创建 Direct2D1 窗口。上一篇博客采用的是 CreateDxgiSurfaceRenderTarget 的方式拿到了 ID2D1RenderTarget 进行绘制,本文将和大家介绍另一个方式,通过 ID2D1DeviceContext 绘制画面。从底层来说,这两个方式底层都是相同的,只是上层的 API 调用方法不相同而已

本文属于 DirectX 系列博客,更多 DirectX 和 D2D 以及 Vortice 库的博客,请参阅我的 博客导航

上一篇: DirectX 使用 Vortice 从零开始控制台创建 Direct2D1 窗口修改颜色

在上一篇博客里面,咱创建了一个 Win32 空窗口,接着给他挂上了 DirectX 交换链。使用以下代码从交换链里面拿到了 DXGI 平面,拿到的的 DXGI 平面即可被绘制 2D 内容在上面,从而将内容绘制输出到窗口上

        DXGI.IDXGISwapChain1 swapChain = ... // 忽略交换链之前的代码

        D3D11.ID3D11Texture2D backBufferTexture = swapChain.GetBuffer<D3D11.ID3D11Texture2D>(0);

        // 获取到 dxgi 的平面,这个屏幕就约等于窗口渲染内容
DXGI.IDXGISurface dxgiSurface = backBufferTexture.QueryInterface<DXGI.IDXGISurface>();

在上一篇博客里面,在 IDXGISurface 上绘制 2D 内容的方式是通过 CreateDxgiSurfaceRenderTarget 的方式创建 ID2D1RenderTarget 进而进行通过 ID2D1RenderTarget 绘制界面,如代码

        // 对接 D2D 需要创建工厂
D2D.ID2D1Factory1 d2DFactory = D2D.D2D1.D2D1CreateFactory<D2D.ID2D1Factory1>(); // 方法1:
var renderTargetProperties = new D2D.RenderTargetProperties(Vortice.DCommon.PixelFormat.Premultiplied); // 在窗口的 dxgi 的平面上创建 D2D 的画布,如此即可让 D2D 绘制到窗口上
D2D.ID2D1RenderTarget d2D1RenderTarget =
d2DFactory. (dxgiSurface, renderTargetProperties); var renderTarget = d2D1RenderTarget;

本文将替换上面代码的方法1部分,按照官方文档提供的另一个方法,通过创建 ID2D1Device 设备,从 ID2D1Device 设备创建出 ID2D1DeviceContext 对象,设置 ID2D1DeviceContext 的输出 Target 为 DXGI 平面,从而使用 ID2D1DeviceContext 绘制界面

按照上图的描述,从创建出来的 Direct3D Device 设备里面,使用 QueryInterface 获取到 DXGI Device 对象。然后分为两个路线,一个是获取 DXGI 的平面,也就是上一篇博客提供的方法,这一路线已完成。另一个就是通过 DXGI Device 设备在 ID2D1Factory1 工厂里创建出 Direct2D Device 设备。再通过 Direct2D Device 设备创建出 Direct2D Device Context 上下文,将 DXGI 的平面转换为 Direct2D Bitmap 作为 Direct2D Device Context 的输出源。如此即可在 Direct2D Device Context 绘制内容输出到窗口界面上

按照上面描述的方法,咱一步步实现

        // 获取 DXGI 设备,用来创建 D2D 设备
DXGI.IDXGIDevice dxgiDevice = d3D11Device.QueryInterface<DXGI.IDXGIDevice>();

使用工厂创建 D2D 设备

        D2D.ID2D1Factory1 d2DFactory = ...
// 创建 Direct2D Device 设备
ID2D1Device d2dDevice = d2DFactory.CreateDevice(dxgiDevice);

从设备里面创建 Direct2D Device Context 上下文

        // 创建 Direct2D Device Context 上下文
ID2D1DeviceContext d2dDeviceContext = d2dDevice.CreateDeviceContext();

将 DXGI 平面转换为 ID2D1Bitmap1 用于设置到 ID2D1DeviceContext 的输出

        ID2D1Bitmap1 d2dBitmap = d2dDeviceContext.CreateBitmapFromDxgiSurface(dxgiSurface);

设置 ID2D1Bitmap1 为 ID2D1DeviceContext 的输出

        d2dDeviceContext.Target = d2dBitmap;

接着将 ID2D1DeviceContext 当成 RenderTarget 即可进行绘制,如以下代码

                // 开始绘制逻辑
renderTarget.BeginDraw(); // 清空画布
renderTarget.Clear(new Color4(0xFF, 0xFF, 0xFF)); // 随意创建颜色
var color = new Color4((byte) Random.Shared.Next(255), (byte) Random.Shared.Next(255),
(byte) Random.Shared.Next(255));
using var brush = renderTarget.CreateSolidColorBrush(color);
renderTarget.FillEllipse(new Ellipse(new System.Numerics.Vector2(200, 200), 100, 100), brush); renderTarget.EndDraw();

以上代码即可在窗口绘制一个有趣颜色的形状

以下是创建 ID2D1DeviceContext 的全部代码

       // 获取到 dxgi 的平面,这个屏幕就约等于窗口渲染内容
DXGI.IDXGISurface dxgiSurface = ... // 对接 D2D 需要创建工厂
D2D.ID2D1Factory1 d2DFactory = D2D.D2D1.D2D1CreateFactory<D2D.ID2D1Factory1>(); // 方法1:
//var renderTargetProperties = new D2D.RenderTargetProperties(PixelFormat.Premultiplied); //// 在窗口的 dxgi 的平面上创建 D2D 的画布,如此即可让 D2D 绘制到窗口上
//D2D.ID2D1RenderTarget d2D1RenderTarget =
// d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties);
//var renderTarget = d2D1RenderTarget; // 方法2:
// 创建 D2D 设备,通过设置 ID2D1DeviceContext 的 Target 输出为 dxgiSurface 从而让 ID2D1DeviceContext 渲染内容渲染到窗口上
// 如 https://learn.microsoft.com/en-us/windows/win32/direct2d/images/devicecontextdiagram.png 图
// 获取 DXGI 设备,用来创建 D2D 设备
DXGI.IDXGIDevice dxgiDevice = d3D11Device.QueryInterface<DXGI.IDXGIDevice>();
ID2D1Device d2dDevice = d2DFactory.CreateDevice(dxgiDevice);
ID2D1DeviceContext d2dDeviceContext = d2dDevice.CreateDeviceContext(); ID2D1Bitmap1 d2dBitmap = d2dDeviceContext.CreateBitmapFromDxgiSurface(dxgiSurface);
d2dDeviceContext.Target = d2dBitmap; var renderTarget = d2dDeviceContext;

本文以上代码放在githubgitee 欢迎访问

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

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

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

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

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

根据官方文档,继承 ID2D1RenderTarget 的 ID2D1DeviceContext 有更多的功能,比如创建特效等

从这个方面上,其实上一篇博客拿到的 ID2D1RenderTarget 其实和本文所拿到的 ID2D1DeviceContext 是相同的东西,至少从底层上是相同的,也就是在上一篇博客的基础上,通过 QueryInterface 即可从 ID2D1RenderTarget 拿到 ID2D1DeviceContext 对象,如以下代码

        var renderTargetProperties = new D2D.RenderTargetProperties(Vortice.DCommon.PixelFormat.Premultiplied);

        // 在窗口的 dxgi 的平面上创建 D2D 的画布,如此即可让 D2D 绘制到窗口上
D2D.ID2D1RenderTarget d2D1RenderTarget =
d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties); var d2dDeviceContext = d2D1RenderTarget.QueryInterface<ID2D1DeviceContext>();

如此可以看到这两篇博客其实只是创建的路线不相同,实际原理是相同的

有伙伴好奇为什么我最近写的是通过 Vortice 调用 DirectX 的博客,而不是通过 SharpDx 或 Silk.NET 调用 DirectX 的博客。这不是我收了 Vortice 的钱或者是和 Vortice 有什么 py 交易哈。其原因是 SharpDx 不维护了,作为 SharpDx 的接任者 Vortice 的行为和 API 都会靠近 SharpDx 许多,我编写起来比较顺手。而 Silk.NET 是对 DirectX 的底层封装,由于是直接底层封装,导致使用 Silk.NET 比较繁琐。尽管使用 Silk.NET 的性能从理论分析上能够比 Vortice 和 SharpDx 更好,但从定量上说,其实好不了多少。我所遇到的几乎所有性能问题,基本都卡在渲染上,而不是调用上,调用上的损耗基本可以忽略。那 Silk.NET 是不是就无用武之地?其实不然,在一些情况下,机器的性能不够业务的需求情况下,能省多少就应该省多少。而且在熟悉整个过程之后,即使将 Vortice 换成 Silk.NET 也只不过是一个体力活而已,将各个 API 进行替换即可。而且有趣的是,可以混合着 Vortice 和 Silk.NET 一起用,只有某些模块才使用 Silk.NET 编写

我创建了专门聊 Vortice 的 QQ 群: 622808968 欢迎加入交流技术

dotnet DirectX 通过 Vortice 控制台使用 ID2D1DeviceContext 绘制画面的更多相关文章

  1. DirectX 使用 Vortice 从零开始控制台创建 Direct2D1 窗口修改颜色

    本文将告诉大家如何使用 Vortice 底层库从零开始,从一个控制台项目,开始搭建一个最简单的使用 Direct2D1 的 DirectX 应用.本文属于入门级博客,期望本文能让大家了解 Vortic ...

  2. Directx 3D编程实例:随机绘制的立体图案旋转

    最近朋友建议我写一些关于微软云技术的博客留给学校下一届的学生们看,怕下一届的MSTC断档.于是我也觉的有这个必要. 写了几篇博客之后,我觉得也有必要把这一年的学习内容放在博客做个纪念,就这样写了本篇博 ...

  3. Docker制作dotnet core控制台程序镜像

    (1)首先我们到某个目录下,然后在此目录下打开visual studio code. 2.编辑docker file文件如下: 3.使用dotnet new console创建控制台程序; 4.使用d ...

  4. dotnet run是如何启动asp.net core站点的

    在曾经的 asp.net 5 过渡时期,运行 asp.net 5 站点的命令是dnx web:在如今即将到来的 asp.net core 时代,运行 asp.net core 站点的命令是dotnet ...

  5. DirectX API 编程起步 #02 窗口的诞生

    在这篇文章里我们先用 windows API 制作一个窗口出来,以后再用 DirectX API 渲染的东西就会显示在这里,控制台那黑白的画面肯定是没法用的. 每次的代码都会更新到Github 首先贴 ...

  6. EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    前言 终于踏出第一步探索EF Core原理和本质,过程虽然比较漫长且枯燥乏味还得反复论证,其中滋味自知,EF Core的强大想必不用我再过多废话,有时候我们是否思考过背后到底做了些什么,到底怎么实现的 ...

  7. ASP.NET Core知多少(7):对重复编译说NO -- dotnet watch

    ASP.NET Core知多少系列:总体介绍及目录 1. 引言 我们一般的开发过程,就是编码-->编译-->运行-->调试-->定位问题--->修改代码-->编译- ...

  8. Cookies 初识 Dotnetspider EF 6.x、EF Core实现dynamic动态查询和EF Core注入多个上下文实例池你知道有什么问题? EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    Cookies   1.创建HttpCookies Cookie=new HttpCookies("CookieName");2.添加内容Cookie.Values.Add(&qu ...

  9. 2019-8-31-dotnet-core-隐藏控制台

    title author date CreateTime categories dotnet core 隐藏控制台 lindexi 2019-08-31 16:55:58 +0800 2019-2-1 ...

  10. Shell、命令行界面、控制台什么区别

    Shell 是什么?Shell 是一个命令解释器,它为用户提供了一个向 操作系统内核发送请求以便运行程序界面系统级程序,它的作用就是遵循一定的语法将输入的命令加以解释并传给系统,他大意是指对系统的操控 ...

随机推荐

  1. 记录--用js如何实现将手机号中间的几位数字变成****

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 今天,我们要实现一个很常见并且简单的功能:将手机号中间的几位数变成**** 这个功能其实很常见,比如我们微信的账号安全里面显示的手机号.掘 ...

  2. JavaScript知识总结 异步编程篇

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 1. 异步编程的实现方式? JavaScript中的异步机制可以分为以下几种: 回调函数 的方式,使用回调函数的方式有一个缺点是,多个回调 ...

  3. 记录--uniapp中生成二维码并展示

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 uniapp生成二维码并展示 1.下载weapp-qrcode.js文件并放在utils文件中链接: https://pan.baidu. ...

  4. C++ pieces

    standard lib fmax double fmax (double x, double y); float fmax (float x, float y); long double fmax ...

  5. 开发必会系列:hibernate事务

    一  事务定义及特性 1.数据库事务的定义:数据库事务(Database Transaction) 是指由一个或多个SQL语句组成的工作单元,这个工作单元中的SQL语句相互依赖,如果有一个SQL语句执 ...

  6. 快速上手系列:webservice

    不同公司的系统交互,用webservice可以起到安全的作用. Webservice使用方法如下:   客户端: 这个是要打成jar包,给别人的用的,里面只是接口,没有实现.   用Webservic ...

  7. Vue入门笔记二

    <Vue.js项目实战> 开发所需的包称为开发依赖,应该使用--save-dev标志进行安装 应用运行需要的直接依赖应该使用--save标志进行安装 模板 使用Pug Pug(以前称为Ja ...

  8. #ST表,并查集#洛谷 3295 [SCOI2016]萌萌哒

    题目 分析 可以发现除了最高位只能填 1 到 9,其它位置还可以填 0. 直接用并查集找连通块会超时,如果将这些区间的合并可以下传到子区间的合并那样就可以了. 考虑ST表的逆操作,合并时直接合并两个极 ...

  9. C++ 编程入门指南:深入了解 C++ 语言及其应用领域

    C++ 简介 什么是 C++? C++ 是一种跨平台的编程语言,可用于创建高性能应用程序. C++ 是由 Bjarne Stroustrup 开发的,作为 C 语言的扩展. C++ 为程序员提供了对系 ...

  10. 如何通过 Makefile 优化加速编译过程提高开发效率

    在软件开发中,编译是一个必不可少的过程.但是,当代码规模变得越来越大时,编译时间也会变得越来越长,这会严重影响开发效率.在这种情况下,优化Makefile可以帮助我们加速编译过程,以下是一些Makef ...