13.2 外部DirectX绘制实现
在前一节中我们简单介绍了D3D绘制窗体所具备的基本要素,本节将继续探索外部绘制技术的实现细节,并以此实现一些简单的图形绘制功能,首先外部绘制的核心原理是通过动态创建一个新的窗口并设置该窗口属性为透明无边框状态,通过消息循环机制实现对父窗口的动态跟随附着功能,当读者需要绘制新的图形时只需要绘制在透明窗体之上即可实现动态显示的效果。
13.2.1 必要参数定义
首先第一步定义所需要的关键变量如下,代码中包含了DirectX 9和DWM的必要库,代码初始化了一些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,用于绘制线条该函数接受四个参数,分别为线段的起始坐标X1和Y1,线段的终止坐标X2和Y2,以及颜色Color。该函数使用D3DXVECTOR2结构体初始化两个点型变量Vertex,然后调用pLine的SetWidth方法设置绘制线段的宽度为 1,最后调用Draw方法在屏幕上绘制出一条线段。
DrawTextString,用于绘制文本该函数接受四个参数,分别为文本字符串的起始坐标X和Y,需要显示的文本字符串Str,以及文本颜色Color。该函数首先使用Font对象的DrawTextA方法来测量文本字符串的大小,并将其存储在一个RECT结构体变量Rect中,然后再次使用Font对象的DrawTextA方法来将字符串绘制在屏幕上。
DrawBox,用于绘制矩形该函数接受五个参数,分别为矩形的左上角坐标X和Y,矩形的宽度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绘制实现的更多相关文章
- 13 获取外部数据库 以及数据库游标适配器(SimpleCursorAdapter)
获取外部数据库 API SQLiteDatabase db = SQLiteDatabase.openDatabase(path, factory, flags); 参数详解: path:数据库路径 ...
- 【Python 13】分形树绘制1.0--五角星(turtle库)
1.案例描述 2.案例分析 引入绘制图形的turtle库,利用库中函数进行编程. 3.turtle库 没有显示的input()和output(),没有赋值语句.调用形式大部分如下: import tu ...
- DirectX 绘制
先上图.后面会描写 ,细节
- DirectX实现球面纹理映射
http://www.cnblogs.com/graphics/archive/2011/09/13/2174022.html DirectX实现球面纹理映射 介绍 球面纹理映射就是将一个平面纹理映射 ...
- DirectX 发展历程
在Windows下开发游戏,大家都会联想到DirectX.实际上,DirectX并不等同于游戏,它也不是写游戏程序的唯一选择.其实,DirectX只是提供了一种更直接的控制硬件的API库而已.当然,它 ...
- MATLAB曲线绘制
一. 二维数据曲线图1.1 绘制 单根二维曲线plot 函数的基本调用 格式为:plot(x,y) 其中x和y为长度相同的向量,分别用于存储x坐标 和y坐标数据. 例1-1 在0≤x≤2p区间内,绘制 ...
- [转帖]MATLAB曲线绘制及颜色类型
信号源产生的方法 来源:http://www.2cto.com/kf/201401/270494.html matlab的checkerboard说明,GOOD! 来源:http://www.chi ...
- iOS 高德自定义坐标轨迹绘制动画 类似与Keep的轨迹绘制
2. 自定义 线的图片,只需要在 rendererForOverlay 方法中,设置: polylineRenderer.strokeImage = [UIImage imageNamed:@&quo ...
- [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)
上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...
- 【转】OpenGL基础图形编程(二)
原文:http://blog.chinaunix.net/uid-20638550-id-1909184.html 分类: 十一.位图与图像 11.1.位图 11.1.1 位图(Bitmap)与字符 ...
随机推荐
- expect --批量传递公钥
#!/binbash ! rpm -qa |grep expect &> /dev/null && yum -y install expect #判断是否已下载expec ...
- HDU 4027 Can you answer these queries?(线段树 区间不等更新)
https://vjudge.net/problem/HDU-4027#author=SUDA2019 题意 输入n个数 然后有两种操作 输入0时将给定区间所有数都变为自己的开方 输入1输出给定区间所 ...
- Codeforces 670C (离散化入门题)
原题链接:https://codeforces.com/problemset/problem/670/C 题目大意: 有 n 个人,每人会且仅会一种语言. (n ≤ 2e5) 语言有各自的编号(≤ 1 ...
- 阿里云 Serverless 应用引擎(SAE)2
8月7日,阿里云 Serverless 应用引擎(SAE)2.0正式公测上线!全面升级后的SAE 2.0具备极简体验.标准开放.极致弹性三大优势,应用冷启动全面提效,秒级完成创建发布应用,应用成本下降 ...
- JVM简单概述
一.内存模型&分区 Java虚拟机在运行Java程序时,会管理着一块内存区域:运行时数据区 在运行时数据区里,会根据用途进行划分为以下模块: 1.Java虚拟机栈 2.本地方法栈 3.Java ...
- 一文看完String的前世今生,内容有点多,请耐心看完!
写在开头 String字符串作为一种引用类型,在Java中的地位举足轻重,也是代码中出现频率最高的一种数据结构,因此,我们需要像分析Object一样,将String作为一个topic,单独拿出来总结, ...
- 物联网浏览器(IoTBrowser)-简单介绍
物联网浏览器(IoTBrowser)是用于开发人机界面(HMI)或数据采集与监督控制系统(SCADA) 的工具,使用HTML或Vue前端技术开发物联网终端用户界面,支持串口.RFID.电子秤等硬件协议 ...
- Linux-文件压缩-tar-gzip
- TLS简单理解
TLS简单理解 TLS的历史 From GTP3.5 TLS(传输层安全)是一种加密协议,旨在确保 Internet 通信的安全性和隐私保护.下面是 TLS 的历史概述: SSL(安全套接层):TLS ...
- [转帖]jmeter编写测试脚本大全
目录 一.背景 二.按照功能划分 2.1 加密处理.验签处理 2.2 jmeter 使用beanshell 编写脚本 2.3 jmeter脚本报错大全 2.4 jmeter打印log 2.5 jmet ...