在前一节中我们简单介绍了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. pip 的高阶玩法

    pip 的高阶玩法 pip 应该是大家最熟悉的 Python 包安装与管理工具了,但是除了pip install 这个最常用的命令,还有很多有用的玩法.这里就介绍几个我平时会用到的,希望对大家有所帮助 ...

  2. nginx: [emerg] unknown directive "?server" in /etc/nginx/conf.d/nginx.conf:1

    问题描述:启动nginx的时候报错 1.nginx: [warn] the "user" directive makes sense only if the master proc ...

  3. KB21N、KB24N作业分配与冲销

    一.KB21N 调用BAPI:BAPI_ACC_ACTIVITY_ALLOC_POST 经测试,分配订单时行项目一次性最多传332条数据 "------------------------- ...

  4. 【HZERO】宏函数

    宏函数配置

  5. 使用Expression代替反射读取IDataReader或IDataRecord给实体类赋值

    ExpressionMapper代码 using System; using System.Collections.Concurrent; using System.Collections.Gener ...

  6. C++岗位面试真题宝典 -- 操作系统篇

    2.1 Linux中查看进程运行状态的指令.查看内存使用情况的指令.tar解压文件的参数. 参考回答 查看进程运行状态的指令:ps命令."ps -aux | grep PID",用 ...

  7. #1016:Prime Ring Problem(经典DFS)

    原题链接 题意:很容易理解,就是让你输出满足相邻的相加是素数的序列(注意不要重复) 思路就是深搜思想把每种情况遍历一次 代码实现: #include<iostream> #include& ...

  8. 安装Amos结构方程模型分析软件的方法

      本文介绍IBM SPSS Amos软件的安装方法.   Amos是IBM公司旗下一款强大的结构方程建模软件.其捆绑在高级版的SPSS Statistics软件中,但其它版本的SPSS Statis ...

  9. SAE 2.0,让容器化应用开发更简单

    云原生容器化应用托管模式的演变 云原生这个概念从提出,到壮大,再到今天的极大普及,始终处于一个不断演进和革新的过程中.云原生体系下应用的托管形态是随着企业应用架构在不断演进的.最早的应用大多是集中式. ...

  10. 处理uniapp(同理小程序)开发中使用rich-text富文本解析,图片未自适应宽度问题(图片显示不全)

    https://www.cnblogs.com/luyaru/p/15538883.html