write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
个人认为《 OpenGL Programming Guide 》的第8章是最让人头晕的一章,讲了很多内容,但是很多东西太偏向于纯理论的概念及众多函数参数的详尽阐述,可能因为这个内容本来就比较难,作者也知道,所以为本章配了全书最密集的图示,可是个人感觉那些图实在是没有任何帮助-_-!。
太多的东西和理论我们学不来,本节只搞定一件非常重要但是此书讲了半天却没有触及的事情,从一个bmp文件中读取数据然后显示出来。当然,这也不怪作者,毕竟读取数据不再是属于OpenGL API的一部分,但是仅仅因为这样,就总是用一堆通过野蛮的数组操作生成的恶心黑白图来做演示和教学,是不是也太过了点?
另外,NEHE教程中有载入图形的相关章节,单还是使用glaux这个现在已经不再推荐的过时库,已经有点不合时宜了,我不想使用这些,最好的办法自然是字节弄明白bitmap文件的格式,直接读取相关数据,然后载入自己的结构使用,这样可以作为完全的跨平台(irrlicht的做法),我又没有这样做的决心,既然是Win32下的OpenGL编程学习,Win32 API永远是我先考虑的,同样的简洁。。。只是别和我讨论跨平台的问题。其实这个说法值得探讨。。。。不关心跨平台为啥要用OpenGL了?-_-!
显示简单的内存中的像素数据
GLubyte rasters
[24] = {
0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00,
0xff, 0x00, 0xff, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00,
0xff, 0xc0, 0xff, 0xc0};
//这里进行所有的绘图工作 void SceneShow ( GLvoid ) { glClear ( GL_COLOR_BUFFER_BIT ); // 清空颜色缓冲区 glColor3f (1.0, 0.0, 0.0); glPixelStorei ( GL_UNPACK_ALIGNMENT , 1); // Pixel Storage Mode (Word Alignment / 1 Bytes) glRasterPos2f (0, 0); glBitmap (10, 12, 0.0, 0.0, 11.0, 0.0, rasters ); glBitmap (10, 12, 0.0, 0.0, 11.0, 0.0, rasters ); glWindowPos2i (0, 0); glBitmap (10, 12, 0.0, 0.0, 11.0, 0.0, rasters ); glBitmap (10, 12, 0.0, 0.0, 11.0, 0.0, rasters ); glBitmap (10, 12, 0.0, 0.0, 11.0, 0.0, rasters ); glFlush (); }
因为实在太简单了,简单的说明一下代码:
此例中主要有4个OpenGL API,但是都非常简单,因为牵涉的相关概念比较少。
glPixelStore*用于指明像素存储的格式,此例中表示1个字节的一个元素。
Name
glPixelStore — set pixel storage modes
C Specification
void glPixelStoref( GLenum pname,
GLfloat param);
void glPixelStorei( GLenum pname,
GLint param);
此函数的参数较多,但是使用起来却较简单,具体的解释大家就自己去查了。
glRasterPos* ,glWindowPos* 用于指定开始绘制像素的位置,同时,上例也说明了指明时两个函数的区别,glRasterPos* 确定位置后绘制了2个F,并且都在窗口的正中间,(0,0)位置,此函数的位置就是以普通的OpenGL坐标系为基准的,因此,也会受到OpenGL坐标变换的影响。glWindowPos2i是个比较特殊的函数,因为它完全不考虑OpenGL坐标变换,永远按窗口的坐标系定位,(这点在绘制界面的时候很有用)虽然实际的坐标定位方式与Windows惯用方式有点区别,原点不是在左上角而是在左下角,直观点说,glWindowPos* 是|_型坐标系,坐标系的方向遵循了OpenGL坐标系的方向(也就是普通笛卡尔坐标系的方向)。
glRasterPos — specify the raster position for pixel operations
glWindowPos — specify the raster position in window coordinates for pixel operations
为了更清楚的看到glWindowPos* 的确定性及glRasterPos* 的可变性,这里我加入glTranslate引入的偏移,(glTranslatef(0.5, 0.0, 0.0);),可以看到glRasterPos* 确定的位置变了,glWindowPos* 的还是老地方。
另外,需要注意的是,上面绘制F的函数调用完全一样,但是绘制并没有重合,因为API中本身就包含了绘制位置的偏移参数。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-12-28/glPixelDraw 目录,获取方式见文章最后关于获取博客完整源代码的说明。
OpenGL中位图文件的显示
以下图片来自于经典的《windows图形编程》一书。
读取bitmap文件的数据是个问题,其实bitmap文件本身很简单,通过固定的结构完全解析读取也不是太难,LaMothe(《Windows游戏编程大师技巧》《3D 游戏编程大师技巧》 )在书中有介绍,但是,我就不这样做了,与OpenGL学习不是一件事。
如一开始描述的一样,这里通过Windows API来完成,以下是Win32的BITMAP结构:
/* Bitmap Header Definition */ typedef struct tagBITMAP { LONG bmType ; LONG bmWidth ; LONG bmHeight ; LONG bmWidthBytes ; WORD bmPlanes ; WORD bmBitsPixel ; LPVOID bmBits ; } BITMAP , * PBITMAP , NEAR * NPBITMAP , FAR * LPBITMAP ;
BITMAP gBmp
;
//OpenGL初始化开始 void SceneInit ( int w , int h ) { GLenum err = glewInit (); if ( err != GLEW_OK ) { MessageBox ( NULL , _T ( "Error" ), _T ( "Glew init failed." ), MB_OK ); exit (-1); } HBITMAP hBmp =( HBITMAP ) LoadImage ( NULL , "tiger.bmp" , IMAGE_BITMAP , 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE ); if (! hBmp ) { exit (3); } GetObject ( hBmp , sizeof ( gBmp ), & gBmp ); glClearColor (0.0, 0.0, 0.0, 0.0); }
MSDN的函数原型:
HANDLE LoadImage(
HINSTANCE hinst , LPCTSTR lpszName , UINT uType , int cxDesired , int cyDesired , UINT fuLoad );
初始化的时候就将图片读出来了,上面就两个Win32 API,一个LoadImage用于加载图片,GetObject用于获取信息,需要特别说明的是,不要看到有个type指定图片类型就感觉LoadImage很强大。。。。
uType
[in] Specifies the type of image to be loaded. This parameter can be one of the following values. IMAGE_BITMAP Loads a bitmap. IMAGE_CURSOR Loads a cursor. IMAGE_ICON Loads an icon.
错觉吧。。。。。。。。。。。。。。毕竟是Win32 API,你以为D3D API啊。。。。。。。。。。呵呵,即使是MFC中的CImage都是弱的不行,还期望这个函数啊。。。。。。。。。。。。。
有了数据就好说了,显示贝。
//这里进行所有的绘图工作 void SceneShow ( GLvoid ) { glClear ( GL_COLOR_BUFFER_BIT ); // 清空颜色缓冲区 glPixelStorei ( GL_UNPACK_ALIGNMENT , 4); // Pixel Storage Mode (Word Alignment / 4 Bytes) glWindowPos2d ( ( WIDTH - gBmp . bmWidth ) / 2, ( HEIGHT - gBmp . bmHeight ) / 2 ); glDrawPixels ( gBmp . bmWidth , gBmp . bmHeight , GL_BGR , GL_UNSIGNED_BYTE , gBmp . bmBits ); glFlush (); }
如图,这里利用glWindowPos2d函数并通过图片长宽的计算,将图片显示在窗口的中间。
哈。。。。。。。。。。学习了这么久的OpenGL,还是第一次利用API实现图片的绘制(的确有点晚),看惯了线框和实心体,再看看图片就是不一样。。。。。。。。。。(事实上,这样的绘制过程通过Win32 API来实现实在再简单不过了,但是谁叫我们学习的是OpenGL呢,呵呵)
这里只多了一个函数:glDrawPixels
glDrawPixels — write a block of pixels to the frame buffer
C Specification
void glDrawPixels( GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
const GLvoid * data);
Parameterswidth, height : Specify the dimensions of the pixel rectangle to be written into the frame buffer.
format:Specifies the format of the pixel data.
type:Specifies the data type for data.
data:Specifies a pointer to the pixel data.
这里都没有什么好奇怪的,我唯一奇怪的是,对于Windows下的图片来说,OpenGL指定绘制的时候type竟然是GL_BGR而不是GL_RGB。。。有高人出来解释一下。
另外,我们还可以通过glPixelZoom函数来缩放图片,比如下列代码就分别将图片横向扩大2倍,纵向扩大1.5倍。
//这里进行所有的绘图工作 void SceneShow ( GLvoid ) { glClear ( GL_COLOR_BUFFER_BIT ); // 清空颜色缓冲区 glPixelZoom (2.0, 1.5); glPixelStorei ( GL_UNPACK_ALIGNMENT , 4); // Pixel Storage Mode (Word Alignment / 4 Bytes) glWindowPos2d ( ( WIDTH - gBmp . bmWidth ) / 2, ( HEIGHT - gBmp . bmHeight ) / 2 ); glDrawPixels ( gBmp . bmWidth , gBmp . bmHeight , GL_BGR , GL_UNSIGNED_BYTE , gBmp . bmBits ); glFlush (); }
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-12-28/glBitmapTest 目录,获取方式见文章最后关于获取博客完整源代码的说明。
本系列其他文章见OpenGL专题 《 Win32 OpenGL系列专题 》
参考资料
1. 《 OpenGL Reference Manual 》,OpenGL参考手册
2. 《OpenGL 编程指南》(《 OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis 著,徐波译,机械工业出版社
3. 《Nehe OpenGL Tutorials》,Nehe著,在 http://nehe.gamedev.net/ 上可以找到教程及相关的代码下载,(有PDF版本教程下载)Nehe自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有 中文版 ,各取所需吧。
4.《 [游戏开发]OpenGL中用bmp图片做纹理贴图的三种方法 》
完整源代码获取说明
由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial使用方法见《 分布式的,新一代版本控制系统Mercurial的介绍及简要入门 》
要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:
http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code
原创文章作者保留版权 转载请注明原作者 并给出链接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie