13.2 外部DirectX绘制实现

在前一节中我们简单介绍了D3D绘制窗体所具备的基本要素,本节将继续探索外部绘制技术的实现细节,并以此实现一些简单的图形绘制功能,首先外部绘制的核心原理是通过动态创建一个新的窗口并设置该窗口属性为透明无边框状态,通过消息循环机制实现对父窗口的动态跟随附着功能,当读者需要绘制新的图形时只需要绘制在透明窗体之上即可实现动态显示的效果。

13.2.1 必要参数定义

首先第一步定义所需要的关键变量如下,代码中包含了DirectX 9DWM的必要库,代码初始化了一些Direct3D 9的变量和指针,包括Direct3D 9设备、呈现参数、Direct3D 线条对象和 Direct3D 字体对象。代码还定义了一个窗口类和一个用于渲染矩形的全局函数指针。

#include <d3dx9.h>
#include <dwmapi.h>#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "dwmapi.lib")static MARGINS Margin;
static LPDIRECT3D9 g_pD3D = NULL;
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static D3DPRESENT_PARAMETERS g_d3dpp = {};
static ID3DXLine* pLine = 0;
static ID3DXFont* Font;// 存储全局窗口信息
static HWND AuxiliaryWindowHandle, GameHwnd;
static RECT WindowRectangle;
static int WindowWidth, WindowHeight;// 注册窗口类
static WNDCLASSEX wClass;// 绘制矩形全局指针
typedef VOID(*Draw)();
static Draw Render;

13.2.2 初始化绘图引擎

初始化绘图引擎InitD3D函数,函数通过Direct3DCreate9创建Direct3D对象,并用g_pD3D指针指向它,并将绘制结构体g_d3dpp中一些参数初始化,例如启用窗口模式、交换方式等等。通过CreateDevice方法创建Direct3D绘图设备,通过D3DXCreateLine方法创建Direct3D线条对象,以便绘制直线段。最后调用D3DXCreateFontW来创建Direct3D字体对象,使得程序可以在绘图中使用特定的字体呈现文字。

// 初始化绘制引擎
BOOL InitD3D()
{// 初始化绘制引擎if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL){return FALSE;}// 填充绘制结构体ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));g_d3dpp.Windowed = TRUE;g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;g_d3dpp.EnableAutoDepthStencil = TRUE;g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;// 创建D3D绘制设备if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, AuxiliaryWindowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0){return FALSE;}// 创建D3D线条if (pLine == NULL){D3DXCreateLine(g_pd3dDevice, &pLine);}// 创建D3D字体D3DXCreateFontW(g_pd3dDevice, 16, 0, FW_DONTCARE, D3DX_DEFAULT, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Vernada", &Font);return TRUE;
}

13.2.3 初始化消息循环

封装实现CreateTransparentWindow函数,该函数用于创建一个透明窗口来显示Direct3D渲染的图形和文本,函数接受两个参数,游戏窗口句柄和绘制函数,其中游戏窗口句柄表示将要在其上绘制图形和文本的窗口句柄,而绘制函数则是指向绘制矩形的全局指针。函数WindowMessageLoop则用于等待消息循环,在该循环内我们通过不间断调用GetWindowRect获取父窗口大小变化或移动位置变化,并通过MoveWindow动态调整,该流程可实现动态跟随窗体移动。

// 窗口消息处理函数
LRESULT WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{switch (Message){case WM_PAINT:if (g_pd3dDevice)Render();break;case WM_CREATE:DwmExtendFrameIntoClientArea(hWnd, &Margin);break;case WM_DESTROY:{g_pD3D->Release();g_pd3dDevice->Release();exit(1);return 0;}default:return DefWindowProc(hWnd, Message, wParam, lParam);break;}return 0;
}// 创建透明窗口过程
VOID CreateTransparentWindow(HWND 游戏窗口句柄, Draw 绘制函数)
{if (绘制函数 == NULL || 游戏窗口句柄 == 0){return;}GameHwnd = 游戏窗口句柄;Render = 绘制函数;// 初始化窗口类wClass.cbClsExtra = NULL;wClass.cbSize = sizeof(WNDCLASSEX);wClass.cbWndExtra = NULL;wClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));wClass.hCursor = LoadCursor(0, IDC_ARROW);wClass.hIcon = LoadIcon(0, IDI_APPLICATION);wClass.hIconSm = LoadIcon(0, IDI_APPLICATION);wClass.hInstance = GetModuleHandle(NULL);wClass.lpfnWndProc = (WNDPROC)WinProc;wClass.lpszClassName = L" ";wClass.lpszMenuName = L" ";wClass.style = CS_VREDRAW | CS_HREDRAW;// 注册窗口if (RegisterClassEx(&wClass) == 0){exit(1);}//创建窗口GetWindowRect(GameHwnd, &WindowRectangle);WindowWidth = WindowRectangle.right - WindowRectangle.left;WindowHeight = WindowRectangle.bottom - WindowRectangle.top;AuxiliaryWindowHandle = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, L" ", L" ", WS_POPUP, 1, 1, WindowWidth, WindowHeight, 0, 0, 0, 0);//显示窗口SetLayeredWindowAttributes(AuxiliaryWindowHandle, 0, RGB(0, 0, 0), LWA_COLORKEY);ShowWindow(AuxiliaryWindowHandle, SW_SHOW);// 初始化D3D引擎InitD3D();
}// 设置窗体消息循环
VOID WindowMessageLoop()
{while (1){// 动态附着if (GameHwnd){// 得到窗口大小GetWindowRect(GameHwnd, &WindowRectangle);WindowWidth = WindowRectangle.right - WindowRectangle.left;WindowHeight = WindowRectangle.bottom - WindowRectangle.top;DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE);if (dwStyle & WS_BORDER){WindowRectangle.top += 23;WindowHeight -= 23;}// 动态附着跟随窗体MoveWindow(AuxiliaryWindowHandle, WindowRectangle.left, WindowRectangle.top, WindowWidth, WindowHeight, true);}// 处理窗口消息MSG Message;ZeroMemory(&Message, sizeof(Message));if (PeekMessage(&Message, 0, 0, 0, PM_REMOVE)){DispatchMessage(&Message);TranslateMessage(&Message);}Sleep(1);}if (g_pd3dDevice){g_pd3dDevice->Release();g_pd3dDevice = NULL;}if (g_pD3D){g_pD3D->Release();g_pD3D = NULL;}CloseWindow(AuxiliaryWindowHandle);UnregisterClass(wClass.lpszClassName, wClass.hInstance);
}

13.2.4 封装实现绘图函数

DrawLine,用于绘制线条该函数接受四个参数,分别为线段的起始坐标X1Y1,线段的终止坐标X2Y2,以及颜色Color。该函数使用D3DXVECTOR2结构体初始化两个点型变量Vertex,然后调用pLineSetWidth方法设置绘制线段的宽度为 1,最后调用Draw方法在屏幕上绘制出一条线段。

DrawTextString,用于绘制文本该函数接受四个参数,分别为文本字符串的起始坐标XY,需要显示的文本字符串Str,以及文本颜色Color。该函数首先使用Font对象的DrawTextA方法来测量文本字符串的大小,并将其存储在一个RECT结构体变量Rect中,然后再次使用Font对象的DrawTextA方法来将字符串绘制在屏幕上。

DrawBox,用于绘制矩形该函数接受五个参数,分别为矩形的左上角坐标XY,矩形的宽度W和高度H,以及矩形线条的宽度Width,以及颜色C。该函数使用D3DXVECTOR2结构体初始化5个点型变量Vertex,依次为左上角、右上角、右下角、左下角和左上角。然后调用pLine对象的SetWidth方法,设置绘制线段的宽度为Width,最后调用Draw方法在屏幕上绘制出整个矩形。

// 屏幕画线
VOID DrawLine(float X1, float Y1, float X2, float Y2, D3DCOLOR Color)
{D3DXVECTOR2 Vertex[2] = { { X1, Y1 }, { X2, Y2 } };pLine->SetWidth(1);pLine->Draw(Vertex, 2, Color);
}// 绘制文本字符串
VOID DrawTextString(float X, float Y, const char* Str, D3DCOLOR Color)
{RECT Rect = { (LONG)X, (LONG)Y };Font->DrawTextA(NULL, Str, -1, &Rect, DT_CALCRECT, Color);Font->DrawTextA(NULL, Str, -1, &Rect, DT_LEFT, Color);
}// 屏幕画方框
VOID DrawBox(float X, float Y, float W, float H, float Width, D3DCOLOR Color)
{D3DXVECTOR2 Vertex[5] = { { X, Y }, { X + W, Y }, { X + W, Y + H }, { X, Y + H }, { X, Y } };pLine->SetWidth(Width);pLine->Draw(Vertex, 5, Color);
}

13.2.5 实现屏幕绘制功能

在最后读者可将上述功能整合在一起,实现动态绘制功能,首先我们需要得到所需绘制进程的窗口句柄,在VS中的工具类自带了一个Spy++读者可使用该工具得到指定窗体的句柄信息,如下图所示;

当得到句柄后则可填充之如下所示GameHandle变量内,当我们需要绘制图形时只需要在GlobalDrawFunction函数内部编写流程即可,该函数内通过BeginScene设置开始绘制,在绘制代码区读者可自行使用上述封装函数实现自定义绘制,当绘制结束后需要通过EndScene结束本次绘制操作,完整绘制代码如下图所示;

// 调用全局绘制
VOID GlobalDrawFunction()
{// 开始绘制g_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0);g_pd3dDevice->BeginScene();// 绘制文本字符串DrawTextString(100, 200, "Hello LyShark", D3DCOLOR_ARGB(56, 0, 52, 0));DrawTextString(200, 300, "PowerBy LyShark", D3DCOLOR_ARGB(255, 0, 255, 0));// 屏幕画线DrawLine(110, 300, 20,  30, D3DCOLOR_ARGB(56, 0, 52, 0));// 屏幕画方框DrawBox(40, 40, 50, 100, 1, D3DCOLOR_ARGB(255, 0, 52, 0));DrawBox(140, 38, 50, 90, 1, D3DCOLOR_ARGB(255, 0, 255, 0));// 结束绘制g_pd3dDevice->EndScene();g_pd3dDevice->Present(0, 0, 0, 0);
}int main(int argc, char *argv[])
{// 获取窗口句柄HWND GameHandle = (HWND)0x00506E6;while (1){// 循环绘制CreateTransparentWindow(GameHandle, GlobalDrawFunction);WindowMessageLoop();}return 0;
}

运行上述代码片段,此时读者可看到如下图所示的输出信息,其中包括两个矩形,两个文本字符串,以及一条直线;

注释:仅用于技术研究学习,请勿用于其他用途。
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/990795ac.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/133428.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL案例详解 三:MMM高可用架构及其故障切换

1. MMM高可用概述 1.1 简介 MMM&#xff08;Master-Master replication manager for MvSQL&#xff0c;MySQL主主复制管理器&#xff09;是一套支持双主故障切换和双主日常管理的脚本程序。 MMM提供了自动和手动两种方式移除一组服务器中复制延迟较高的服务器的虚拟ip&#xf…

Node.js 正在逐渐被淘汰!Bun 1.0 正在改变 JavaScript 的游戏规则

在深入讨论之前&#xff0c;我们需要解释什么是 JavaScript 运行时以及为什么我们应该关心其速度。 想象一下&#xff0c;你用 JavaScript 写了一个故事&#xff0c;需要有人大声读出来。JavaScript 运行时就像是那个友好的叙述者&#xff0c;为你的故事赋予生命&#xff01;它…

nSoftware IPWorks IoT 2022 Java 22.0.8 Crack

物联网库&#xff0c;使用这个轻量级组件库&#xff0c;可以在任何平台上的应用程序中轻松实现物联网 (IoT) 通信协议。 nSoftware IPWorks IoT 最新的 IPWorks IoT 现已推出&#xff01;最新版本的 IPWorks IoT 具有现代化和简化的体验&#xff0c;包括 .NET 中的异步和跨平台…

Android+Appium自动化测试环境搭建及实操

1、Appium简介1.1 Appium概念1.2 Appium工作原理 2、Appium Server环境搭建2.1 Java JDK2.1.1 下载JDK2.1.2 运行exe安装JDK&#xff0c;设置安装路径2.1.3 设置环境变量2.1.4 验证安装结果 2.2 Android SDK2.2.1 下载安装Android SDK安装包2.2.2 下载platform-tools&#xff0…

Android Studio展示Activty生命周期

前言 本文章以及之后文章的程序版本使用Android Studio 2022.3.1 Patch 1 版本编辑&#xff0c;使用语言为java&#xff0c;最低支持API 27 Android 8.1&#xff0c;构建工具版本如下&#xff1a; 本文章主要是介绍Activty跳转和删除&#xff0c;以备后续使用&#xff0c;所以就…

微信页面公众号页面 安全键盘收起后页面空白

微信浏览器打开H5页面和公众号页面&#xff0c;输入密码时调起安全键盘&#xff0c;键盘收起后 键盘下方页面留白 解决办法&#xff1a; 1、&#xff08;简单&#xff09;只有在调起安全键盘&#xff08;输入密码&#xff09;的时候会出现这种情况&#xff0c;将input属性改为n…

Vuex的基础使用存值及异步

目录 一、概述 ( 1 ) 讲述 ( 2 ) 概念 ( 3 ) 作用 二、取值 1. 安装 2. 菜单栏 3. 模块 4. 引用 三、改值 四、异步&后台请求 带来的获取 一、概述 ( 1 ) 讲述 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的…

【将文本编码为图像灰度级别】以 ASCII 编码并与灰度级别位混合将文本字符串隐藏到图像像素的最低位中,使其不明显研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

轻松搭建个人web站点:OpenWRT教程结合内网穿透技术实现公网远程访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦 &#x1f516;系列专栏&#xff1a; C语言、Linux &#x1f325;️每日语录&#xff1a;山不让尘&#xff0c;川不辞盈。 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前言 uhttpd 是 OpenWrt/LuCI 开发者从零开始编写的 Web …

Stable Diffusion 动画SD-Animatediff V2

AI不仅可以生成令人惊叹的图片,还能给这些图片注入生命,让它们动起来。 这就是AnimateDiff要做的事情,一个神奇的工具,能将静态的AI生成图像转换成动画。 本次介绍基于SD如何实现这个神奇的方法。 文章目录 插件安装使用方法参数调整文生动图/视频Controlnet方法SD API方…

Java实现B树

1.介绍 B树是一种自平衡的搜索树数据结构&#xff0c;常用于数据库和文件系统中的索引结构。它具有以下好处和功能&#xff1a; 高效的查找操作&#xff1a;B树的特点是每个节点可以存储多个关键字&#xff0c;并且保持有序。通过在节点上进行二分查找&#xff0c;可以快速定位…

客户机操作系统已禁用 CPU。请关闭或重置虚拟机(解决)

解决&#xff1a; 关闭虚拟机进入设置点击处理器给虚拟化引擎两个勾上确认后重新即可