MetaHook Plus 是一个GoldSrc引擎(就是的Half-Life、CS1.6的引擎)的客户端插件平台,它可以加载我们自己开发的DLL插件。

首先你需要安装一个 Visual Studio 2005 来编译 MetaHook Plus 本体,也可以用来开发我们自己的插件,这里提供一个镜像文件。

注意:MetaHook Plus 本体必须要用 2005  来编译!

ed2k://|file|cs_vs_2005_pro_dvd.iso|2733268992|9DA1C378BAC22E66A73C9E20EC78CCFB|/

顺便一提,如果VS2005只用来写C++代码的话,这样就行,不用安装多余的工具:

安装完VS之后,你需要下载 MetaHook Plus 的源码,地址在这里,下载ZIP就行。

http://git.oschina.net/Nagist2/MetaHook

把源码解压好,打开里面的 MetaHook.sln 文件,选择 Debug 版本,重新生成解决方案。

稍后你可以在下方的输出窗口里看到编译成功的字样,查看源码目录,多了一个 Debug 文件夹,里面有编译好的 MetaHook.exe,把它复制到你的游戏目录,比如 CS1.6 的,改名成 cstrike.exe

进入 cstrike 文件夹,新建一个 metahook 文件夹,然后在 metahook 文件夹里新建 configs 和 plugins 两个文件夹,在 configs 文件夹里新建一个文本文档,改名为 plugins.lst

至此 MetaHook Plus 已经正确安装,打开 cstrike.exe 可以正常启动游戏。

打开 MetaHook Plus 源码文件夹里的 Plugins\FuckWorld 文件夹,这个项目是一个最简单的 MetaHook 插件(下文简称插件)。

打开 FuckWorld.sln,像编译 MetaHook Plus 一样编译项目,把编译好的 FuckWorld.dll 复制到 plugins 文件夹,打开 plugins.lst 文件,加入一行 FuckWorld.dll(注意不要在文件里留下空行),保存文件。

现在正常启动游戏,并进入游戏,然后打开控制台(按~键),你会发现控制台里多了几行字。这证明你的插件已经正常运行。

终于可以开始讲怎么在游戏里显示一张图片了。

不要关掉 FuckWorld 项目的VS,在左边窗口里双击打开 exportfuncs.cpp 文件,在 #include <metahook.h> 这一行的下方加入一行 #include <gl/gl.h>

#include <metahook.h>
#include <gl/gl.h> cl_enginefunc_t gEngfuncs;

然后打开项目属性,在左边的分支里找到 链接器->输入,然后在右边的 附加依赖项 里添加 opengl32.lib,保存。关闭项目属性。

这样你就可以在插件里使用OpenGL的功能了,什么是OpenGL?这个单词你肯定不陌生,但这里暂时不打算详细介绍。

注意:确保你游戏的视频模式是OpenGL!

那么,现在可以正式开始学习怎么显示一张图片了,首先我们改一下 HUD_Redraw 这个函数,把代码改成如下的样子,注意字母大小写一定要对!后面 // 绿色的部分可以不写。

int HUD_Redraw(float time, int intermission)
{
glDisable(GL_TEXTURE_2D); // 关闭纹理功能 glColor4ub(, , , ); // 设置显示颜色 glBegin(GL_QUADS); // 开始绘制四边形
glVertex2f(20.0f, 20.0f); // 四边形第1个点的坐标
glVertex2f(80.0f, 20.0f); // 四边形第2个点的坐标
glVertex2f(80.0f, 80.0f); // 四边形第3个点的坐标
glVertex2f(20.0f, 80.0f); // 四边形第4个点的坐标
glEnd(); // 结束四边形绘制 glEnable(GL_TEXTURE_2D); // 打开纹理功能 return gExportfuncs.HUD_Redraw(time, intermission);
}

重新编译 FuckWorld,复制DLL,进入游戏,你会发现游戏左上角多了个白色的四边形,这证明我们的代码已经成功运行。

下面讲解一下关于这个四边形的事,首先是坐标。

由此可见,游戏窗口的坐标系是这样的

现在你可以尝试修改 glVertex2f 的代码,改变4个顶点的坐标,让四边形在别的地方显示,或者改变它的大小,或者改变它的形状。

更新时间:2017年6月22日16:06:48

看了上面的教程,你应该能在游戏画面中画出一个纯色的(正)方形,但是这并没有多大卵用。接下来就讨论一下怎麽画一张图片,但真正画图片之前,需要先学一点有关图片的知识。

首先我们要从非常底层的视角去认识图片,比如下图这样:

Photoshop:

系统绘图工具:

把一张普通图片放大很多倍,你会发现图片都是由很多格子组成的,这些格子就叫做像素。(所以一张图片就是由很多像素组成的)

每个像素都有不同的颜色,并且每个像素能显示出255×255×255种颜色。(16,581,375种)

学过初中物理你应该知道,几乎任何可见光的颜色都可以用红绿蓝(Red Green Blue、RGB)三种颜色混合起来得到,红绿蓝三种颜色按照不同的分量混合起来,就能得到多达16多万种颜色。

那么计算机是怎么保存一张图片的呢?

计算机使用一个字节来保存一种颜色的分量,所以一个像素通常需要三个字节来保存(分别保存RGB三种颜色的分量),但一张图片可不止一个像素,而是非常多像素。

一张尺寸为32×32的图片,就包含1024个像素,你可能很快就发现32×32=1024,没错就是这样。为了保存一张尺寸为32×32的图片的所有像素,我们需要一个非常大的数组变量:

BYTE ubData[  *  *  ];

假设我们已经把这张图片的所有像素保存到这个数组里,那么:

ubData[  ];    // 第1个像素的红色分量(R)
ubData[ ]; // 第1个像素的绿色分量(G)
ubData[ ]; // 第1个像素的蓝色分量(B) ubData[ ]; // 第2个像素的红色分量(R)
ubData[ ]; // 第2个像素的绿色分量(G)
ubData[ ]; // 第2个像素的蓝色分量(B) // ...

你可能会问第1个像素到底是图片里哪个地方,答:最左上角的那一个。顺便一提,第2个像素是第1个像素右边的那个。从左往右横向数的。

然后你可能还想知道第2行的第1个像素的成员是哪几个(假设图片大小是32×32,已知每个像素占用3个成员)

像素在数组里是按照横向顺序保存的,保存完第1行的像素,就接着第2行的像素。

uData[ ( *  * ) +  ];    // 第2行第1个像素的红色分量(R)
uData[ ( * * ) + ]; // 第2行第1个像素的绿色分量(G)
uData[ ( * * ) + ]; // 第2行第1个像素的蓝色分量(B)

详细怎麽获取像素的颜色就不多说了,你只要知道图片是这麽一回事就好。

然后是很关键的一步,我们有一堆图片文件(比如TGA格式的),要怎麽把它们的像素弄(加载、读取、载入.. 等等说法都有)到变量里呢?

这其中的步骤是非常复杂的,但是!不用担心,引擎里就有写好的函数,我们只要拿来用就行。

HL引擎有一个叫做 LoadTGA 的函数,可以把TGA图片的像素读取到变量里。

函数原型:

bool LoadTGA(const char *filename, unsigned char *buffer, int bufferSize, int *wide, int *tall);

所以我们只要拿到这个函数的指针就能使用了:

// 3266版本引擎的基址是固定的,所以直接写静态地址就行。
bool (*g_pfnLoadTGA)(const char *filename, unsigned char *buffer, int bufferSize, int *wide, int *tall) = (bool (*)(const char *, unsigned char *, int, int *, int *))0x01D4F8A2;

如果你想直接照搬这个代码,请一定要使用3266版本的引擎,否则会游戏会崩溃。

其它版本引擎就不能这麽写,引擎地址是不固定的。查找其它版本引擎的LoadTGA函数涉及特征码搜索知识,这里不讨论,以后可能会写特征码搜索的讲解。

(什麽?你不知道怎麽看引擎版本号?打开控制台,输入命令 version )

加载一个TGA文件我们可以这样写:

// 3266版本引擎的基址是固定的,所以直接写静态地址就行。
bool (*g_pfnLoadTGA)(const char *filename, unsigned char *buffer, int bufferSize, int *wide, int *tall) = (bool (*)(const char *, unsigned char *, int, int *, int *))0x01D4F8A2; // 因为TGA文件除了RGB三种颜色分量,还有一个Alpha分量,表示像素的透明度,所以每个像素就要占用4个字节(RGBA)。
BYTE ubData[ * * ]; // 用来保存加载的TGA的宽度和高度。
int iWidth, iHeight; void HUD_Init(void)
{
gExportfuncs.HUD_Init(); if (g_pfnLoadTGA("gfx/1.tga", ubData, sizeof(ubData), &iWidth, &iHeight) == false)
{
// 检查一下有没有加载失败。
iWidth = ;
iHeight = ;
} if (iWidth <= || iHeight <= )
{
// 可能是加载失败了。
return;
} // 宽高都不为0,表示加载成功,我们在控制台输出一个提示。
gEngfuncs.Con_Printf("Load TGA successfully !\n");
}

这样一来我们就已经把 1.tga 的像素读取并且保存到 ubData 里了。然后我们就要考虑怎麽把这些像素画出来了。

在上面的教程里,你已经知道我们需要使用OpenGL提供的函数来绘制东西,当然绘制图片也不例外。

为了让OpenGL能使用刚才加载好的像素数据,我们需要创建一个OpenGL纹理(下文简称纹理),OpenGL纹理能保存许多东西,我们这里只用来绘制一些像素,所以使用2D纹理。

我们可以用 glGenTextures 或者 glBindTexture 来创建出一个新的纹理,但是 glBindTexture 的专职是绑定纹理,而不是创建,所以一般我们用 glGenTextures。

下面的代码创建1个新纹理。

GLuint texid;
glGenTextures(, &texid);

但是由于HL引擎的种种原因,我们决定用 glBindTexture 来创建出指定ID的纹理。

GLuint texid = ;
glBindTexture(GL_TEXTURE_2D, texid);

这样我们就直接创建出了ID为20000的2D纹理。

OpenGL的工作方式类似流水线(状态机),所以同时只能处理一个纹理,为了马上使用刚才创建出来的纹理,我们要把它绑起来,这样接下来的操作就会应用到这个纹理上。

glBindTexture(GL_TEXTURE_2D, texid);

但是你看到我们上面已经有 glBindTexture 了,所以就不用再次绑定了。

绑定好纹理之后,我们就要为这个纹理设定好一些必要的参数:

设置纹理缩放过滤方式(OpenGL纹理过滤方式可以在网上搜索到介绍):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

然后我们就要把加载好的那一堆像素传给这个纹理:

glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, iWidth, iHeight, , GL_RGBA, GL_UNSIGNED_BYTE, ubData);

第一个参数表示纹理类型为2D。对于 glTexImage2D 来说,必须写 GL_TEXTURE_2D。

第二个参数是Mipmap相关的,我们只用来绘制HUD,所以固定写0就行。

第三个参数是OpenGL内部像素格式,决定了像素传到OpenGL里面之后要保存为什么样的格式,我们一般保存为RGBA就行。

第四个参数是图片宽度。

第五个参数是图片高度。

第六个参数固定写0。

第七个参数是像素的格式,也就是 ubData 的格式,根据我们上面教程的介绍,肯定是 RGBA 这样排啦。

第八个参数是像素的数据类型,我们是BYTE的,所以写 GL_UNSIGNED_BYTE。

第九个参数是像素数据的地址,直接写ubData就行。

如果成功调用了 glTexImage2D 那麽像素数据就已经成功传到了OpenGL里(OpenGL程序是在显卡里运行的,所以此时像素数据已经被传到了显存里)。

我们已经处理完自己的纹理了,不要忘了擦屁股,解除纹理绑定(我们会在绘制的时候才绑定需要的纹理):

glBindTexture(GL_TEXTURE_2D, );

现在我们代码是这样的:

// 纹理ID
GLuint texid = ; void HUD_Init(void)
{
// ... gEngfuncs.Con_Printf("Load TGA successfully !\n"); // 绑定纹理
// 绑定一个2D纹理,使用参数:GL_TEXTURE_2D
// 绑定ID号为20000的纹理(GL会先检查有没有ID为20000的纹理,如果没有就自动创建新的,有就直接绑定)
glBindTexture(GL_TEXTURE_2D, texid); // 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 上传像素数据到显卡
glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, iWidth, iHeight, , GL_RGBA, GL_UNSIGNED_BYTE, ubData); // 处理完自己的纹理,就解除绑定。
glBindTexture(GL_TEXTURE_2D, );
}

这样我们就准备好了一个2D纹理,接着就可以开始绘制啦~

2017年7月27日00:48:36 更新

还记得上面我写了4个坐标绘制出一个纯色的正方形吗?如果忘了就赶紧往回看!

我们用4个点连成一个形状(面),而我们接下来要做的,就是把一张纹理贴到这个“面”上,这种技术叫做“贴图”。

为了使用贴图,我们要先了解一个新的名词,叫做“纹理坐标”。

纹理坐标就是在纹理上建立的坐标,比方说,我有一个纹理,像这样:

然后建立一个坐标系,横向坐标轴叫做S轴,纵向坐标轴叫做T轴。

如图:

你可以看到图中有4组括号,格式为(S,T),这就是纹理4个角的坐标。

接下来我们要修改一下绘制函数(HUD_Redraw)来实现贴图功能了。

为了使用纹理功能,我们就要先打开它,使用 glEnable 函数

glEnable(GL_TEXTURE_2D);            // 打开纹理功能

接着我们绑定要用的纹理,使用 glBindTexture 函数,就是上面加载好的那个 texid,所以:

glBindTexture(GL_TEXTURE_2D, texid);    // 绑定用于贴图的纹理

为了让贴图表现出Alpha通道透明效果,我们还要设置一下透明混合功能。

(混合功能暂不做详解,照抄即可)

glEnable(GL_BLEND);                    // 开启透明混合功能
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置混合参数

接下来就是我们绘制四边形的代码了,我们要做的就是在调用 glVertex3f 指定四边形坐标的时候,顺便再指定一个纹理坐标,这样 OpenGL 就知道怎麽把纹理贴上去了。

glBegin(GL_QUADS);                    // 开始绘制四边形

glTexCoord2f(0.0f, 0.0f);            // 四边形第1个点的纹理坐标
glVertex2f(20.0f, 20.0f); // 四边形第1个点的坐标 glTexCoord2f(1.0f, 0.0f); // 四边形第2个点的纹理坐标
glVertex2f(80.0f, 20.0f); // 四边形第2个点的坐标 glTexCoord2f(1.0f, 1.0f); // 四边形第3个点的纹理坐标
glVertex2f(80.0f, 80.0f); // 四边形第3个点的坐标 glTexCoord2f(0.0f, 1.0f); // 四边形第4个点的纹理坐标
glVertex2f(20.0f, 80.0f); // 四边形第4个点的坐标 glEnd(); // 结束四边形绘制

如果你忘记了四边形的坐标,请翻回去看看!

看上面的代码,我们就是一如既往地绘制一个四边形而已,顺便指定一下纹理坐标,是不是相当于把一张纹理 “铺上去” 一样呢?

如果你能理解成这样,那么恭喜你。

现在完整的代码是这样的:

int HUD_Redraw(float time, int intermission)
{
// 先绘制原本的HUD,这样我们画的图片就不会被HUD覆盖了。
gExportfuncs.HUD_Redraw(time, intermission); glEnable(GL_TEXTURE_2D); // 打开纹理功能
glBindTexture(GL_TEXTURE_2D, texid); // 绑定用于贴图的纹理 glEnable(GL_BLEND); // 开启透明混合功能
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置混合参数 glColor4ub(, , , ); // 设置显示颜色 glBegin(GL_QUADS); // 开始绘制四边形 glTexCoord2f(0.0f, 0.0f); // 四边形第1个点的纹理坐标
glVertex2f(20.0f, 20.0f); // 四边形第1个点的坐标 glTexCoord2f(1.0f, 0.0f); // 四边形第2个点的纹理坐标
glVertex2f(80.0f, 20.0f); // 四边形第2个点的坐标 glTexCoord2f(1.0f, 1.0f); // 四边形第3个点的纹理坐标
glVertex2f(80.0f, 80.0f); // 四边形第3个点的坐标 glTexCoord2f(0.0f, 1.0f); // 四边形第4个点的纹理坐标
glVertex2f(20.0f, 80.0f); // 四边形第4个点的坐标 glEnd(); // 结束四边形绘制 return ;
}

现在重新编译你的 FuckWorld,复制到 plugins 里,然后打开游戏看看效果。

如果你做对了,你就会看到这样的效果:

现在我们来做个有趣的实验吧,假如,我们指定的4个坐标,连起来不是一个正方形会怎么样呢?

我稍微修改一下第3个顶点的坐标(右下角那个顶点),把坐标改成(100,100),像这样:

glTexCoord2f(1.0f, 1.0f);            // 四边形第3个点的纹理坐标
glVertex2f(100.0f, 100.0f); // 四边形第3个点的坐标

你会得到这样的效果:

是不是更能体现出 “贴图” 的效果呢?

你平时看到的形状复杂的模型,就是由大量的几何图形贴上图组成的,绘制过程同样是:指定顶点坐标、指定纹理坐标…

可能有同学注意到,既然指定纹理4个角的坐标就能把整个纹理“铺上去”,那反过来,如果不指定4个角的坐标,而是指定纹理中间一部分的4个坐标,是不是可以只铺上纹理的一部分呢?

答案是肯定的!

我们现在重新指定4个纹理坐标,比如这样:

如图所示,这4个坐标形成了一个新的区域,这个区域是整个纹理的右下角一部分。

我们把新的纹理坐标写到代码中,看看效果!

glBegin(GL_QUADS);                    // 开始绘制四边形

glTexCoord2f(0.5f, 0.5f);            // 四边形第1个点的纹理坐标
glVertex2f(20.0f, 20.0f); // 四边形第1个点的坐标 glTexCoord2f(1.0f, 0.5f); // 四边形第2个点的纹理坐标
glVertex2f(80.0f, 20.0f); // 四边形第2个点的坐标 glTexCoord2f(1.0f, 1.0f); // 四边形第3个点的纹理坐标
glVertex2f(80.0f, 80.0f); // 四边形第3个点的坐标 glTexCoord2f(0.5f, 1.0f); // 四边形第4个点的纹理坐标
glVertex2f(20.0f, 80.0f); // 四边形第4个点的坐标 glEnd(); // 结束四边形绘制

结果如下图:

你可以看到,确实只铺上了整个纹理的右下角一部分!

认识了纹理坐标,你就可以做很多骚操作啦!

比方说把一堆小图标拼在一张图片里,然后绘制的时候指定其中一个图标的纹理坐标来绘制~

这样就不需要加载许多个尺寸很小的图片,而只加载一个尺寸大点的图片就行了。(美工制作起来也方便)

至此在HUD上绘制一个图片的过程已经详细解释,但是你看上面写的代码只绘制一个图片,我们实际上制作游戏肯定不止要绘制一个图片而已。

为了更方便地绘制图片,我们把上面的代码整理出来,分别放到两个函数里,一个 R_LoadTextureFromTGA 用于加载TGA图片文件并且上传到纹理,一个 R_DrawTextureRect2D 用于绘制一个贴图的长方形。

以下就是整理过的代码:

#include <metahook.h>
#include <gl/gl.h> cl_enginefunc_t gEngfuncs; int Initialize(struct cl_enginefuncs_s *pEnginefuncs, int iVersion)
{
memcpy(&gEngfuncs, pEnginefuncs, sizeof(gEngfuncs));
return gExportfuncs.Initialize(pEnginefuncs, iVersion);
} // 3266版本引擎的基址是固定的,所以直接写静态地址就行。
bool (*g_pfnLoadTGA)(const char *filename, unsigned char *buffer, int bufferSize, int *wide, int *tall) = (bool (*)(const char *, unsigned char *, int, int *, int *))0x01D4F8A2; // 纹理ID
GLuint texid; // 此函数用于兼容3266版本引擎(仅用于测试)
GLuint R_GenTexture(void)
{
static GLuint texnum = ;
return texnum++;
} GLuint R_LoadTextureFromTGA(const char *filename)
{
// 局部变量不能太大,所以用静态变量
static BYTE ubData[ * * ];
// 宽度和高度
int iWidth, iHeight;
// 纹理ID
GLuint iTexID; if (g_pfnLoadTGA(filename, ubData, sizeof(ubData), &iWidth, &iHeight) == false)
{
// 失败返回0
return ;
} // 生成一个新纹理
iTexID = R_GenTexture(); // 绑定纹理
glBindTexture(GL_TEXTURE_2D, iTexID); // 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 上传像素数据到显卡
glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, iWidth, iHeight, , GL_RGBA, GL_UNSIGNED_BYTE, ubData); // 处理完自己的纹理,就解除绑定。
glBindTexture(GL_TEXTURE_2D, ); // 返回纹理ID
return iTexID;
} void R_DrawTextureRect2D(GLuint tex, int x, int y, int width, int height)
{
glEnable(GL_TEXTURE_2D); // 打开纹理功能
glBindTexture(GL_TEXTURE_2D, tex); // 绑定用于贴图的纹理 glEnable(GL_BLEND); // 开启透明混合功能
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置混合参数 glColor4ub(, , , ); // 设置显示颜色 // 我们没有分别指定4个顶点的坐标,而是给出XY和要绘制的四边形的宽度和高度。
// 所以这4个点的坐标分别这样计算:
// 左上角(X,Y)
// 右上角(X+宽度,Y)
// 右下角(X+宽度,Y+高度)
// 左下角(X,Y+高度) // 如果你觉得难理解,建议自己画个窗口坐标系来看看 glBegin(GL_QUADS); // 开始绘制四边形 glTexCoord2f(0.0f, 0.0f); // 四边形第1个点的纹理坐标
glVertex2f(x, y); // 四边形第1个点的坐标 glTexCoord2f(1.0f, 0.0f); // 四边形第2个点的纹理坐标
glVertex2f(x + width, y); // 四边形第2个点的坐标 glTexCoord2f(1.0f, 1.0f); // 四边形第3个点的纹理坐标
glVertex2f(x + width, y + height); // 四边形第3个点的坐标 glTexCoord2f(0.0f, 1.0f); // 四边形第4个点的纹理坐标
glVertex2f(x, y + height); // 四边形第4个点的坐标 glEnd(); // 结束四边形绘制
} void HUD_Init(void)
{
gExportfuncs.HUD_Init(); // 加载TGA
texid = R_LoadTextureFromTGA("gfx/1.tga");
} int HUD_Redraw(float time, int intermission)
{
// 先绘制原本的HUD,这样我们画的图片就不会被HUD覆盖了。
gExportfuncs.HUD_Redraw(time, intermission); // 绘制纹理
R_DrawTextureRect2D(texid, , , , ); R_DrawTextureRect2D(texid, , , 1, 1); return ;
} void V_CalcRefdef(struct ref_params_s *pparams)
{
gExportfuncs.V_CalcRefdef(pparams);
}

你可以看到我加载 1.tga 只调用了一次 R_LoadTextureFromTGA,并且,我调用两次 R_DrawTextureRect2D 就在不同的位置绘制了两个图片,而不需要重复一大堆代码。

按照这个方向,我相信你能打造出非常方便的函数用来绘制你的HUD。

既然是绘制HUD,就不免有些特殊要求,比方说绘制一个半透明的图片(透明度可以控制的),或者让原本的图片变色之类的操作。

要实现这样的效果是简单的,OpenGL 会自动把纹理中的颜色和 glColor 指定的颜色相乘,至于怎麽乘就懒得介绍了~

我们要做的就是稍微修改一下 R_DrawTextureRect2D 函数。

void R_DrawTextureRect2D(GLuint tex, int x, int y, int width, int height, int r, int g, int b, int alpha)
{
// ... glColor4ub(r, g, b, alpha); // 设置显示颜色 // ...
}

然后需要注意一下,glColor4ub 这个函数的参数只接受 0~255 的值,如果想用 0.0~1.0 这样的小数值来表示颜色,还有一个 glColor4f 函数可以用。

这里为了更贴近我们日常使用的 RGB 就采用 glColor4ub 了。

然后我们修改一下绘制时候使用的参数:

int HUD_Redraw(float time, int intermission)
{
// ... // 绘制纹理
R_DrawTextureRect2D(texid, , , , , , , , ); R_DrawTextureRect2D(texid, , , , , , , , ); // ...
}

注意参数的变化。

效果如下:

至此,在HUD上绘制图片的基本过程就解释完毕了,你可以看到实际上要写的代码并不多!

祝你成功!

【入门向】使用 MetaHook Plus 绘制 HUD的更多相关文章

  1. Canvas入门(1):绘制矩形、圆、直线、曲线等基本图形

    来源:http://www.ido321.com/968.html 一.Canvas的基础知识 Canvas是HTML 5中新增的元素,专门用于绘制图形.canvas元素就相当于一块“画布”,一块无色 ...

  2. 【webGL入门2】点线面的绘制

    用js绘制webGL的点: THREE.Vector3 = function ( x, y, z ) { //用THREE声明的变量都是全局变量.this.x = x || 0;this.y = y ...

  3. OpenGL入门程序五:三维绘制

    1.现实世界观察一个物体的时候,可能涉及到的三维变化: 1>视图变化------从不同的角度观察. 2>模型变化------移动.旋转物体,计算机中当然还可以对物体进行缩放. 3>投 ...

  4. Canvas入门07- 自定义实现虚线的绘制

    预备知识 直线的斜率 一条直线与某平面直角坐标系x轴正半轴方向的夹角的正切值即该直线相对于该坐标系的斜率. 对于一条直线 y = kx +b,k就是直线的斜率. 斜率的计算 对于一条已知的线段,求斜率 ...

  5. Python数据分析入门(十七):绘制条形图

    条形图的绘制方式跟折线图非常的类似,只不过是换成了plt.bar方法.plt.bar方法有以下常用参数: x:一个数组或者列表,代表需要绘制的条形图的x轴的坐标点. height:一个数组或者列表,代 ...

  6. Android系统编程入门系列之界面Activity绘制展示

    上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...

  7. WPF 使用 Direct2D1 画图入门

    本文来告诉大家如何在 WPF 使用 D2D 画图. 本文是一个系列 WPF 使用 Direct2D1 画图入门 WPF 使用 Direct2D1 画图 绘制基本图形 WPF 使用 SharpDX WP ...

  8. WPF 使用 Direct2D1 画图 绘制基本图形

    本文来告诉大家如何在 Direct2D1 绘制基本图形,包括线段.矩形.椭圆 本文是一个系列 WPF 使用 Direct2D1 画图入门 WPF 使用 Direct2D1 画图 绘制基本图形 本文的组 ...

  9. ChemDraw Prime 15怎么绘制立体化学结构

    众所周知,ChemDraw化学工具的最新版本是ChemOffice 15,其下还有三个适合不同用户的版本,下文详细指导如何使用入门版本ChemDraw Prime 15绘制立体化学结构. 立体化学结构 ...

随机推荐

  1. 20155338 《Java程序设计》实验一(Java开发环境的熟悉)实验报告

    20155338 <Java程序设计>实验一(Java开发环境的熟悉)实验报告 一.实验内容及步骤 1.用JDK编译.运行简单的java程序 步骤一(新建文件夹): 打开windows下的 ...

  2. 微信小程序点击按钮,修改状态

    WXML中: <view wx:if="{{orderstate}} = '待送检' " data-no="{{orderstate}}" bindtap ...

  3. Hadoop项目结构

    Hadoop是一个由Apache基金会所开发的分布式系统基础架构. 用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储. Hadoop实现了一个分布式文件系统 ...

  4. Keil出错解决方法

    1.安装KEIL5后创建工程后出现这个报错 解决方法:打开下图目录的文件. Keil.STM32F1xx_DFP.pdsc文件是只读文件,必须将只读属性取消. 如下图所示,注释掉红色圆圈的哪一行,保存 ...

  5. Unity编辑器扩展chapter1

    Unity编辑器扩展chapter1 unity通过提供EditorScript API 的方式为我们提供了方便强大的编辑器扩展途径.学好这一部分可以使我们学会编写一些工具来提高效率,甚至可以自制一些 ...

  6. HTTP协议请求信息详解

    通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息.客户端向服务器发送一个请求,请求头包含请求的方法.URI.协议版本.以及包含请求修饰符.客户信息和内容的类似于MIME的消息结构 ...

  7. java之接口开发-初级篇-webservice协议

    webservice协议 客户端: 客户端生成使用soapUI生成 外部提供webservice地址,地址后加?wsdl.选择好目录然后生成,放到项目中实现 服务端: web.xml平级目录下创建se ...

  8. Ubuntu—安装python的第三方包gevent

    今晚花很多时间, 使用 sudo pip3 install gevent 但是始终没有成功. 柳暗花明又一村 sudo apt-get install python3-gevent 搞定!!! 人生如 ...

  9. Fast R-CNN学习总结

    Fast R-CNN是R-CNN的改良版,同时也吸取了SPP-net中的方法.在此做一下总结. 论文中讲到在训练阶段,训练一个深度目标检测网络(VGG16),训练速度要比R-CNN快9倍左右,比SPP ...

  10. C++ 根据图片url 批量 下载图片

    最近需要用到根据图片URL批量下载到本地的操作.查找了相关资料,记录在这儿. 1.首先在CSV文件中提取出url ifstream fin("C:\\Users\\lenovo\\Deskt ...