给我一个画点函数,我能描绘出整个世界!
时间:2010-04-03 来源:superyongzhe
如题,可以知道一个画点函数是多么的基本和重要。我将在这篇文章里向你介绍如何在使用各种图形库来画一个点,这个实验就像”Hello world”那个程序一样基础,但它能让你对要使用的库有个基本的认识,比如如何配置,如何初始化,如何下手去调用函数等。
[注]所谓的画点是指绘制单个像素。
[另注]这是一个入门级的帖子,希望能对初哥们的迅速上手能有所帮助。
《Graphics篇》
Graphics库通常可以被包含在TC/TC++/BC++等编译环境。我假设你的用是TC,首先你要确定菜单 ”Options” | “Linker” 下面的 “Graphics library”开关的状态是 “on”,也就是处于打开状态;而在TC++和BC++中又稍有不同,同样是 ”Options” | “Linker”,此时你能看到有一个Libraries选项,选择后弹出一个对话框,勾选其中的 ” Graphics library” 即可;当然你也可能使用的是WIN-TC,那就更简单,以1.8版为例:“运行”|“编译配置”中,勾选“扩展库信息”下面的“Graphics.lib”就OK了(但如果你的LIB文件夹下没Graphics.lib就不会有这一项),到这里准备工作就做完了,我们开始编码:
#include <conio.h>
#include <graphics.h>
int main()
{
int gd=DETECT,gm=0; /* 在VGA以上的显卡中和gd=VGA,gm=VGAHI是同样效果 */
initgraph(&gd,&gm,""); /* BGI初始化 */
putpixel(320, 200, WHITE); /* 在屏幕的(320,200)的坐标位置用白色(WHITE)画一个点 */
getch(); /* 暂停一下,看看前面绘图代码的运行结果 */
closegraph(); /* 恢复TEXT屏幕模式 */
return 0;
}
怎么样?不是很难吧,以上使用的都是库函数,更多库函数请参考相关资料。
《NEO SDK篇》
首先你得下载一个NEO的压缩包,NEO资源站:http://www.ds0101.com/neosdk,编程中国的下载页面地址是:http://www.bc-cn.net/Soft/kfyy/c/200605/302.html。解压后你会得到一个类似” NEO_V2.1.90”名称的文件夹,打开它还会有 ”docs”、”examples”、”include”、”tools”,每一个具体的作用参见里面的” Readme!.txt”,现在我们只需要将”include”文件夹中的所有内容都复制到你的编译环境中的include 文件夹中去,这个编译环境可以是TC,也可以是TC++、BC++,当然包括诸如WIN-TC或TC4U等外壳环境,TC4U甚至还集成了NEO,但版本不是最新的。好了,开始编码,我以WIN-TC为例:
#include "neo.h"
int main()
{
neo_init(); /*NEO初始化*/
set_vbe_mode(VBE640X480X256); /*设置图形模式*/
install_keyboard(); /* 安装键盘事件处理模块 */
dot(320, 200, _WHITE); /* 在屏幕坐标(320,200)处以白色(_WHITE)画一个点 */
readkey (); /* 暂停一下,看看前面绘图代码的运行结果 */
return 0;
}
一样是非常简单的,这里用的也全是NEO库中的函数,实现的结果和前面那个例子是一样的,不过虽然分辨率一样,但色深可不一样哦,前面Graphics库的结果是在640X480、16色图形模式下输出的,这个例子则是在640X480、256色模式下输出的,当然这么简单的例子是看不出什么区别的了,另:如果你把VBE640X480X256改为VBE640X480X64K则会采用16位色深(共65536种颜色可用)。更多的库函数用法请参考docs文件夹下的用户手册。
《Allegro篇》
首先你也必须下载Allegro,不过国内有爱好者将Allegro同编译环境DEV-C++ 5打包在一起提供下载了,比如:http://www.8623.com就是个好去处,里面还可以下载到Allegro + DJGPP的捆绑包。下载这种包的好处就是你不用亲自去编译安装Allegro,当然这也意味着你不能使用最新的Allegro版本了,如果你比较了解MinGW和makefile的话,自己下载最新版本手动编译安装将是个不错的主意。如果你用的是DEV-CPP的话,还可以下载最新的DevPak包,再用自带工具Packman安装也是一个非常方便快捷的办法,这样安装的Allegro可以DEV-CPP的“新建”|“工程”的工程向导中找到相应的模板,就不需做更多的参数配置了。不过如果安装的是捆绑版本,就需要在DEV的“工具”|“编译选项”|“在连接器命令行加入以下命令”栏中加入参数”-lalleg”(不要引号),如此一来在你就可以新建一个源程序并编码,编译时DEV将知道需要接连Allegro库了。另外需要注意的是,这样编译出来的执行文件,需要alleg4x.dll动态链接库的支持,通常最简单的做法是将它们放在一个文件夹里。也许你用的是DJGPP,那么也是上面的下载页面,先下载,然后参考里面的“无法编译帮助.txt”文件进行编译连接,不过由于是精简版,RHIDE似乎运行不了。好了,开始编译:
#include <allegro.h>
int main()
{
int white;
allegro_init(); /*Allegro 初始化*/
install_keyboard(); /*安装键盘处理例程*/
set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0); /*设置图形模式*/
set_color_depth(16); /*设置色深*/
white = makecol(255, 255, 255); /*计算白色值*/
putpixel(screen, 320, 200, white); /*在屏幕坐标(320,200)处用白色画点*/
readkey(); /*暂停一下,观看结果*/
return 0;
}
END_OF_MAIN(); /*魔术宏,用于支持跨平台,在DJGPP下可以去掉以避免产生警告*/
同样的结果,其中makecol()是用来转换像素格式颜色的,示例中是将R、G、B分量均设置为255(即白色)然后赋值给变量white,NEO SDK中也有这个函数,我在NEO示例中的那个_WHITE其实就是makecol(255, 255, 255)的宏定义。在这个示例中我们使用了16位高彩。更多的函数说明请参考Allegro用户手册。
《GDI/GDI+篇》
GDI是位于应用程序与不同硬件之间的中间层,这种结构让程序员从直接处理不同硬件的工作中解放出来,把硬件间的差异交给了GDI处理。GDI+是GDI的下一个版本,它进行了很好的改进,并且易用性更好。GDI的一个好处就是你不必知道任何关于数据怎样在设备上渲染的细节,GDI+更好的实现了这个优点,也就是说,GDI是一个中低层API,你还可能要知道设备,而GDI+是一个高层的API,你不必知道设备。由于GDI是Windows的一个组成部分,所以只要你手头上有能够用于Windows应用程序开发的编译环境就能够直接使用它,这样环境包括VC++,DEV-C++,C++Builder,C-Free等等。而且使用的过程大概都相同,都是新建一个包含有” Windows Application”字样的空白工程,然后向工程加入一个源码文件,将下面编码复制进去:
#include <windows.h>
long WINAPI WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam);
BOOL InitWindowsClass(HINSTANCE hInstance);
BOOL InitWindows(HINSTANCE hInstance,int nCmdShow);
HWND hWndMain;
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpszCmdLine,int nCmdShow)
{
MSG msg;
if(!InitWindowsClass(hInstance))
return FALSE;
if(!InitWindows(hInstance,nCmdShow))
return FALSE;
/* 消息循环核心 */
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
long WINAPI WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT PtStr;
switch(iMessage)
{
case WM_PAINT:
hDC=BeginPaint(hWnd,&PtStr);
SetPixel(hDC, 320, 200, RGB(0, 0, 0)); /* 在窗口工作区坐标(320,200)处以黑色画点 */
EndPaint(hWnd,&PtStr);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd,iMessage,wParam,lParam);
}
}
/*初始化WINDOWS窗口并显示它*/
BOOL InitWindows(HINSTANCE hInstance,int nCmdShow)
{
HWND hWnd;
hWnd=CreateWindow("WinGDI", /* 窗口类的名称 */
"使用GDI画点示例", /* 窗口标题 */
WS_OVERLAPPEDWINDOW, /* 窗口风格 */
0, /* 窗口位置:X轴*/
0, /* 窗口位置:Y轴*/
640, /* 窗口的高 */
480, /* 窗口的宽 */
NULL, /* 指向父窗口的指针 */
NULL, /* 指向菜单的指针 */
hInstance, /* 窗口实例句柄 */
NULL); /* 附加信息 */
if(!hWnd)
return FALSE;
hWndMain=hWnd;
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd); /* 显示并刷新窗口 */
return TRUE;
}
BOOL InitWindowsClass(HINSTANCE hInstance)
{
WNDCLASS wndClass;
wndClass.cbClsExtra=0;
wndClass.cbWndExtra=0;
wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndClass.hIcon=LoadIcon(NULL,"END");
wndClass.hInstance=hInstance;
wndClass.lpfnWndProc=WndProc;
wndClass.lpszClassName="WinGDI";
wndClass.lpszMenuName=NULL;
wndClass.style=CS_HREDRAW|CS_VREDRAW;
return RegisterClass(&wndClass); /* 注册窗口类 */
}
些时的你也许会有些沮丧,上面有差不多90行代码,也就是说为了画这个点,我们需要八十多行的外围代码,这些代码分别被用来创建并注册窗口,显示和刷新窗口,接收及处理消息,在其它库的支持下画点不过十几行的代码量一下子增加了五六倍…… 这些都是事实,但是也没有关系,在Windows下面写程序是需要这样子的,这也是初学者一般都从控制台程序学起的原因,就是为了避开这些繁杂的东西而专业掌握语言本身的特性。不要因为这样而紧张或不知所措,其实不管你要写个多大的Windows程序,也只需要这么多的准备代码了,也就是说画一个点需要这么多,画一万个也只需要这么多,而且这些准备代码都是些公式化的东西,我在初学者应该了解的代码行中都加上了注释,记住了就行了,以后再写别的程序这个框架都一直能用的上,最后建议去了解一下CreateWindowEx()和相应的RegisterClassEx(),虽然功能相似,但这两个是扩展版本,现在更加常用。
《DDRAW篇》
DDRAW全称是DirectDraw,是微软DirectX的一部分。DirectX(以下简称DX)是一种应用程序接口,目前的版本是9.0c,它可让以Windows为平台的游戏或多媒体程序获得更高的执行效率,加强3d图形和声音效果,并提供设计人员一个共同的硬件驱动标准,让游戏开发者不必为每一品牌的硬件来写不同的驱动程序,也降低用户安装及设置硬件的复杂度。DX不是建立在消息机制上的,它可以绕过消息机制直接与硬件打交道,所以在制作对性能要求比较高的Windows图形程序时需要用到它的接口。DX是由很多API组成的,按照性质分类,可以分为四大部分,显示部分、声音部分、输入部分和网络部分,我们要讨论的DDRAW就是显示部分中针对2D编程的模块(其实目前版本的DX已经将原来分离的DDRAW和D3D合在一块了统称为Direct Graphics),DDRAW在DX 7.0中就已经趋于完善,之后的DX版本更新主要是增强D3D的功能。在DDRAW中,并未提供专用的画点函数,我们有几个可以解决这个问题的方案:一是使用它的画线函数DrawLine()画一段只有一个像素长的“线”,也就是一个点了;二是使用成员函数Lock()将页面锁定,就可以得到页面的首地址,然后通过一定的偏移量计算,就可以得到任意一个坐标的内/显存地址,再将点的颜色信息写入到该地址,解锁Unlock()即可;三可以创建一个1*1的surface再blt到页面中等等。这里只列举三种,其它的方法大家可以自行讨论。要使用DX进行开发,需要下载相应的包将库文件安装到你的编译环境中去,VC++的可以到微软官方下载页面获得,DEV-C++的根据之前提到的链接下载到相应的捆绑版中除了Allegro还有DX 8的必要文件,至于DX 9的DEVPAK包则可以到VIRX的主页http://vrixpworld.rjdown.com中下载的到,那里还有Allegro4.2.0及SDL的DEVPAK。另外为了告诉编译器我们需要使用DirectDraw,我们要在程序文件中#include <ddraw.h>,并把"ddraw.lib"和"dxguid.lib"加入工程。记住,做完了这些工作后DirectDraw程序才能被正常编译。由于能工作的源码同样会比较长,今天由于时间问题就不写了,大家也可以到网上查查相关的资料。
《OpenGL篇》
对于很多人来讲,说起OpenGL,首先便会想到游戏,特别是经典的FPS游戏Quake(雷神之锤)系列,它是有史以来最受欢迎的3D游戏之一,它的作者John Carmack 用一个星期使用OpenGL将其重新编写之后,引起了游戏界的一片喧哗。然而,游戏只是OpenGL应用领域的冰山一角。
严格的讲, OpenGL被定义为"图形硬件的一种软件接口",它是一种功能强大,精巧复杂的3D图形API(应用程序编程接口).OpenGL并不象C/C++一样是一门编程语言,从本质上说,它是一个3D图形和模型库,具有高度的可移植性,并且有非常快的速度.它拥有的命令超过300个,覆盖了从设置材料颜色和反射属性到执行旋转和复杂的坐标转换等功能.它具有比Direct3D更简洁的代码和更高的效率,但这也使向Direct3D投入了大量资金的微软公司感到利益受到威胁,而在初期大力打压OpenGL.然而,OpenGL强大的功能和易于使用的特点使得它得到了越来越多的开发人员的认可和支持,面对越来越多的受GpenGL支持的软件,硬件商们不得不开发更好的OpenGL硬件和高质量的驱动程序.今天,OpenGL已经得到了广泛的承认和接受,成为实时3D图形的行业标准API.
下面我们就来看看怎样用OpenGL来画一个点
考虑到很多人没有安装BC++6.0, 下面这个程序是用VC++6.0编译的。
以下是源代码
上传时间:2006-5-27 20:25
下载次数:329
#include "glut.h"
//#include<gl/glut.h>
//绘制场景
void RenderScene(void)
{
// 用当前清除颜色清除窗口
glClear(GL_COLOR_BUFFER_BIT);
// 首先设置画图用的颜色,这里设置成了红色
// 红 绿 蓝
glColor3f(1.0f, 0.0f, 0.0f);
//这个函数的所有参数都在0.0到1.0之间,后缀f是表示参数是浮点型的
//有时为了表现象玻璃一样的透明效果,会用glColor4f(1.0f, 0.0f, 0.0f,1.0f);
//最后的那个1.0f是透明度,0.0f表示全透明,1.0f是完全不透明
// 开始画图了,应斑竹大人的要求画一个点,不过一个点也太难找了吧,所以要画大点
//设置点的大小,默认大小是0,画出来的点很小,这里大小设置成3
glPointSize(3);
//设置成画点的模式
glBegin(GL_POINTS);
//glVertex3f(x,y,z),在坐标(x,y,z)处画一个点,多画一个点就多加个函数,自己加个点或改改坐标试试
//因为窗口大小为800x600,所以点的坐标范围为(-400,-300,z)到(400,300,z)
glVertex3f(0.0f,0.0f,0.0f);
// glVertex3f(25,5,0);
//把上面函数前面的//去掉看看加一个点是什么情况
//结束绘画
glEnd();
// 刷新绘图命令,强制完成所有绘画,这句话表示全部图形绘制完成
glFlush();
}
// 设置渲染状态(听起来满下人,实际上很简单)
void SetupRC(void)
{
//清除颜色(这里为黑色,为了方便找画的那个点),可以理解成背景颜色
//和glColor4f(1.0f, 0.0f, 0.0f,1.0f)一样,所有参数都在0.0到1.0之间,后缀f是表示参数是浮点型的
//最后的那个1.0f是透明度,0.0f表示全透明,1.0f是完全不透明
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
}
// 当绘制的窗口大小改变时重新绘制,使绘制的图形同比例变化,
//几乎所有OpenGL程序中的这个函数都是一样的,所以,放心大胆的拷贝吧
void ChangeSize(int w, int h)
{
GLfloat aspectRatio;
// 防止被0除
if(h == 0)
h = 1;
// 设置观察视野为窗口大小(用FLASH里面的话来说应该叫设置摄象机视野)
glViewport(0, 0, w, h);
// 重置坐标系统
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 建立裁剪区域 (左, 右, 底, 顶, 近, 远)
aspectRatio = (GLfloat)w / (GLfloat)h;
if (w <= h)
glOrtho (-100.0, 100.0, -100 / aspectRatio, 100.0 / aspectRatio, 1.0, -1.0);
else
glOrtho (-100.0 * aspectRatio, 100.0 * aspectRatio, -100.0, 100.0, 1.0, -1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 主函数
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
//设置显示模式
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
//设置窗口大小为800x600像素
glutInitWindowSize(800, 600);
//建立一个叫OpenGL的窗口
glutCreateWindow("OpenGL");
//调用函数RenderScene进行绘制
glutDisplayFunc(RenderScene);
//如果窗口大小改变则调用函数ChangeSize重新进行绘制
glutReshapeFunc(ChangeSize);
//清屏
SetupRC();
//循环绘制
glutMainLoop();
return 0;
}
画完了,还比较简单吧,画图的语言很简洁,就5句而已,
glColor3f(1.0f, 0.0f, 0.0f);
glPointSize(3);
glBegin(GL_POINTS);
glVertex3f(0.0f,0.0f,0.0f);
glEnd();
其余的全在为作图作准备。
是不是觉得OpenGL的函数名称很变态,又臭又长?
实际上,绝大部分OpenGL函数都遵循一种命名约定,它可以告诉你这个函数来自哪个库,并且还常常提示你这个函数将接受的参数个数和类型。所有函数都有个根名称,表示这个函数对应的OpenGL命令。例如:glColor3f 的根名称是Color,前缀gl表示gl库函数,后缀3f表示这个函数接受3个浮点型参数。所有的OpenGL函数都采用了下面的格式:
<函数库前缀><根命令><可选参数数量><可选参数类型>
现在你是不是又觉得OpenGL的函数很方便,很容易记忆了呢?