write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
提要
在前文(系列文章(7),以下简称XO7,系列其他文章类似)中的照相机比喻中提到了4种3D变换,如下:
1.确定照相机的位置的过程对应于“视图变换”(Viewing Transformations)
2.确定物体位置的过程对应于“模型变换”(Modeling Transformations)
3.确定照相机放大倍数的过程对应于“投影变换”(Projection Transformations)
4.确定照片大小的过程对应于“视口变换”(Viewport Transformations)
XO7中我们讲的是第一种变换视图变换,即改变观察者本身的位置,视角等的变换效果,XO8中讲的是第二种变换模型变换,XO9中讲的是投影变换,本文开始讲解最后一个变换,视口变换。
视口变换
此变换应该算是4种变换中最简单的了,在照相机比喻中我说他是确认照片大小,在实际中,确认的是绘制的区域,当然,我们以前没有设定视口变换前,默认是占据整个窗口的客户区的。只有一个关键函数glViewport,而且较易理解:
glViewport — set the viewport
C Specification
void glViewport( GLint x,
GLint y,
GLsizei width,
GLsizei height);
Parametersx, y
Specify the lower left corner of the viewport rectangle,
in pixels. The initial value is (0,0).
width, heightSpecify the width and height
of the viewport.
When a GL context is first attached to a window,
width and height are set to the dimensions of that
window.
无论前面进行了多少处理,最终的图像将映射到这个矩形中,默认情况时占据整个窗口的客户区,在前面的所有例子中,(七巧板的例子除外)我们没有接触到视口变换,那么默认就是窗口创建那一瞬间的大小,我们可以尝试改变窗口大小,发现事实上图形没有变大,并且,位置也不再居中了,这个时候我们就需要进行视口变换,调整视口。这里,因为以前有七巧板的例子了,也有原来没有使用的例子,我就不提供新的此用途的例子了,仅仅看两个例子的区别。
未在窗口改变时重新设定视口的情况:
正常情况:
窗口缩小时:图像偏移了。
窗口放大时:图像不居中。
看七巧板中的例子:
因为有以下代码:
void ReShape ( unsigned auWidth , unsigned auHeight ) { glViewport (0, 0, auWidth , auHeight ); }
// FUNCTIONS ////////////////////////////////////////////// LRESULT CALLBACK WindowProc ( HWND hwnd , UINT msg , WPARAM wparam , LPARAM lparam ) { // this is the main message handler of the system PAINTSTRUCT ps ; // used in WM_PAINT HDC hdc ; // handle to a device context // what is the message switch ( msg ) { case WM_CREATE : { // do initialization stuff here // return success return (0); } break ; case WM_PAINT : { // simply validate the window hdc = BeginPaint ( hwnd ,& ps ); // end painting EndPaint ( hwnd ,& ps ); // return success return (0); } break ; case WM_DESTROY : { // kill the application, this sends a WM_QUIT message PostQuitMessage (0); // return success return (0); } break ; case WM_SIZE : { ReShape ( LOWORD ( lparam ), HIWORD ( lparam )); } default : break ; } // end switch // process any messages that we didn't take care of return ( DefWindowProc ( hwnd , msg , wparam , lparam )); } // end WinProc
注意reshape的作用,此时,窗口大小改变时,会重新设定视口,这样,图形将会随着窗口大小改变而改变(这是大部分情况下我们需要的)
正常情况下:
窗口缩小:图形还是居中,因为纵横比的改变,导致图形纵横比也改变了。
窗口放大时:图形还是居中,因为纵横比的改变,导致图形纵横比也改变了。
上述七巧板的例子中,很好的演示了glViewport的作用,源代码在以前就已经提供了,这里不再说了。但是,我们会发现一个问题,就是窗口纵横比改变的时候,图形实际也改变了纵横比导致变形了,这样不太符合大部分情况下我们的想法,我们可以通过控制窗口的纵横比来控制这一点,(大部分情况下)或者直接通过控制glViewport参数的纵横比也可以达到保证图像不扭曲的目的。(但是图形可能移位)
屏幕分割
玩过真三国无双系列的玩家们不知道在同一台机器上与战友们浴血奋斗过没有,我是有过,显示时,一个玩家在上面,一个在下面,在同一台机器上不需要通过网络就能享受联机的乐趣,的确很有意思,事实上,我们通过视口变换连续绘制图形两次就能达到这样的效果(我不知道真三国是否也是通过这样的技术实现的),比如上述的七巧板的例子吧,我们想在屏幕上绘制4次,让四个人同时玩,进行对战,那么我们就可以这样做:
void ReShape ( unsigned auWidth , unsigned auHeight ) { WindowWidth = auWidth ; WindowHeight = auHeight ; } // All Scene Show code void SceneShow ( GLvoid ) { glClear ( GL_COLOR_BUFFER_BIT ); // left bottom glViewport (0, 0, WindowWidth /2, WindowHeight /2); gTriBTop . Draw (); gTriBRight . Draw (); gTriSLeft . Draw (); gRectangle . Draw (); gTriSMid . Draw (); gTriMLeft . Draw (); gParal . Draw (); // right bottom glViewport ( WindowWidth /2, 0, WindowWidth /2, WindowHeight /2); gTriBTop . Draw (); gTriBRight . Draw (); gTriSLeft . Draw (); gRectangle . Draw (); gTriSMid . Draw (); gTriMLeft . Draw (); gParal . Draw (); // left top glViewport (0 , WindowHeight /2, WindowWidth /2, WindowHeight /2); gTriBTop . Draw (); gTriBRight . Draw (); gTriSLeft . Draw (); gRectangle . Draw (); gTriSMid . Draw (); gTriMLeft . Draw (); gParal . Draw (); // right top glViewport ( WindowWidth /2 , WindowHeight /2, WindowWidth /2, WindowHeight /2); gTriBTop . Draw (); gTriBRight . Draw (); gTriSLeft . Draw (); gRectangle . Draw (); gTriSMid . Draw (); gTriMLeft . Draw (); gParal . Draw (); glFlush (); }
显示效果:
注意上述代码中,我们的具体的显示代码只有一份,显示代码中并不知道自己绘制了几份,绘制在什么地方,这也就是OpenGL这样设计的好处,本身图形的绘制很简单,在原点附近绘制一个标准的图形而已,一种又一种变换后,却可以产生于原来图形千差万别的图形,这一点有点像设计模式中的decorator模式.
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-29/JTFourTangram 目录,获取方式见文章最后关于获取博客完整源代码的说明。
呵呵,相当happy吧,同一台机子,4个人同时玩七巧板,(事实上,什么游戏都可以借鉴),什么?就一个鼠标没有办法操作?晕哪,你不会插四个鼠标?插四个鼠标也没有用?呵呵,推荐你看看我以前写的关于多鼠标的东西,4个人用4个鼠标同时玩,不是什么不可能的^^以前魔兽好像有个4国战争的游戏地图很流行,要是额外设计,我们可以在同一台机器上玩^^发挥大家的创意吧。
参考资料
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入门学习》 ,eastcowboy著,这是我在网上找到的一个比较好的教程,较为完善,而且非常通俗。这是第一篇的地址: http://bbs.pfan.cn/post-184355.html
本OpenGL系列其他文章
1. 《 Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤 》
2. 《 Win32 OpenGL编程(2) 寻找缺失的OpenGL函数 》
3. 《 Win32 OpenGL编程(3) 基本图元(点,直线,多边形)的绘制 》
4. 《 Win32 OpenGL编程(4) 2D图形基础(颜色及坐标体系进阶知识) 》
5. 《 Win32 OpenGL编程(5)顶点数组详细介绍 》
6.《 Win32 OpenGL编程(6) 踏入3D世界 》
7.《 Win32 OpenGL编程(7) 3D视图变换——真3D的关键 》
8.《 Win32 OpenGL编程(8) 3D模型变换及其组合应用 》
9.《 Win32 OpenGL编程(9) 投影变换 》
应用举例:《 Win32 OpenGL编程系列 2D例子 -- 七巧板图形绘制 》
完整源代码获取说明
由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者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