在前一节中我们简单介绍了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;
}

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

13.2 外部DirectX绘制实现的更多相关文章

  1. 13 获取外部数据库 以及数据库游标适配器(SimpleCursorAdapter)

    获取外部数据库 API SQLiteDatabase db = SQLiteDatabase.openDatabase(path, factory, flags); 参数详解: path:数据库路径 ...

  2. 【Python 13】分形树绘制1.0--五角星(turtle库)

    1.案例描述 2.案例分析 引入绘制图形的turtle库,利用库中函数进行编程. 3.turtle库 没有显示的input()和output(),没有赋值语句.调用形式大部分如下: import tu ...

  3. DirectX 绘制

    先上图.后面会描写 ,细节

  4. DirectX实现球面纹理映射

    http://www.cnblogs.com/graphics/archive/2011/09/13/2174022.html DirectX实现球面纹理映射 介绍 球面纹理映射就是将一个平面纹理映射 ...

  5. DirectX 发展历程

    在Windows下开发游戏,大家都会联想到DirectX.实际上,DirectX并不等同于游戏,它也不是写游戏程序的唯一选择.其实,DirectX只是提供了一种更直接的控制硬件的API库而已.当然,它 ...

  6. MATLAB曲线绘制

    一. 二维数据曲线图1.1 绘制 单根二维曲线plot 函数的基本调用 格式为:plot(x,y) 其中x和y为长度相同的向量,分别用于存储x坐标 和y坐标数据. 例1-1 在0≤x≤2p区间内,绘制 ...

  7. [转帖]MATLAB曲线绘制及颜色类型

    信号源产生的方法 来源:http://www.2cto.com/kf/201401/270494.html  matlab的checkerboard说明,GOOD! 来源:http://www.chi ...

  8. iOS 高德自定义坐标轨迹绘制动画 类似与Keep的轨迹绘制

    2. 自定义 线的图片,只需要在 rendererForOverlay 方法中,设置: polylineRenderer.strokeImage = [UIImage imageNamed:@&quo ...

  9. [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)

    上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...

  10. 【转】OpenGL基础图形编程(二)

    原文:http://blog.chinaunix.net/uid-20638550-id-1909184.html  分类: 十一.位图与图像 11.1.位图 11.1.1 位图(Bitmap)与字符 ...

随机推荐

  1. Educational Codeforces Round 96 (Rated for Div. 2) (A - C题个人题解)

    因为火锅导致错过的上分机会,赛后发现人均AC5题 1430A. Number of Apartments 暴力搜索 #include<bits/stdc++.h> using namesp ...

  2. 版本升级 | v3.0.0卷起来了!多种特殊情况解析轻松拿捏!

    在过往发行版的基础上,结合社区用户提供的大量反馈及研发小伙伴的积极探索,项目组对OpenSCA的解析引擎做了全方位的优化,v3.0.0版本正式发布啦~ 感谢所有用户的支持和信任~是很多人的一小步聚在一 ...

  3. 聚焦 AIGC,函数计算为 AI 应用插上腾飞翅膀

    6月1日,2023 阿里云峰会·粤港澳大湾区在广州举办,Serverless 加速创新分论坛如约亮相,阿里云函数计算 FC 聚焦 AIGC 应用开发,GPU 性能体验再升级,让 AIGC 应用开发更简 ...

  4. wireshark 报文颜色

    在使用wireshark抓包分析的过程中,默认会对不同的包进行着色,截图如下: 对不同的颜色有了解,可快速的过滤包或分析请求. 菜单栏选择视图-->着色规则,即可看到不同颜色代表的含义: 大致可 ...

  5. git仓库之gogs安装(docker版/二进制版)

    二进制安装gogs tar zxf gogs_0.11.91_linux_amd64.tar.gz -C /data/gogs chown -R www.www /data/gogs su - www ...

  6. asp.net core之实时应用

    本文将介绍ASP.NET Core SignalR,这是一个强大的实时通信库,用于构建实时.双向通信应用程序.我们将探讨SignalR的基本概念.架构和工作原理,并提供一些示例代码来帮助读者更好地理解 ...

  7. 08-Shell计算命令

    1.expr命令 expr (evaluate expressions 的缩写),译为"表达式求值".Shell expr 是一个功能强大,并且比较复杂的命令,它除了可以实现整数计 ...

  8. PHP安全之道学习笔记6:密码安全

    PHP安全之道:密码安全    最近几年用户数据泄露越发频繁,一些使用PHP技术的大型网站被暴库或者脱库,facebook公司曾因为此类事件股价暴跌.从长远发展来看,用户的数据安全对于网站来说至关重要 ...

  9. ChatGPT-NextWeb部署和调试打造属于自己的GPT

    首先我关注这个项目有一段时间了,不得不说作者和他的社区真的很猛! 首先这个项目截至目前已经有了40.9K的Start了,Fork也已经有了38.1K了,这个数据真的超级牛批了. 那么我们来看一下这款号 ...

  10. [转帖]jmeter必备正则表达式

    元字符 .  注意是一个点号,表示匹配任意单个字符 \d 表示匹配任意单个数字 [0-9] 等价于0-9 [a-zA-Z] 等价于所有的大小写字母 限定符 +  加号,表示匹配至少大于1次(1次或多次 ...