OpenGL从1.0开始--二维观察

正本清源,这一节我们重新来给大家解释下OpenGL如何实现图元在屏幕上的显示功能。

#include class wcPt2D { public: GLfloat x, y; }; void init() { glClearColor(0.0, 0.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); gluOrtho2D(-100.0, 100.0, -100.0, 100.0); //创建裁剪窗口 glMatrixMode(GL_MODELVIEW); } void triangle(wcPt2D *verts) { GLint k; glBegin(GL_TRIANGLES); for (k = 0; k < 3; k++) { glVertex2f(verts[k].x, verts[k].y); } glEnd(); } void lineSegment() { wcPt2D verts[3] = { { -50.0, -25.0 }, { 50.0, -25.0 }, { 0.0, 50.0 } }; glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0, 0.0, 1.0); glViewport(0, 0, 300, 300); //创建视口 triangle(verts); glColor3f(1.0, 0.0, 0.0); glViewport(300, 0, 300, 300); glRotatef(90.0, 0.0, 0.0, 1.0); triangle(verts); glFlush(); } void main(int argc, char**argv) { glutInit(&argc, argv); //初始化GLUT glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowPosition(50, 100); glutInitWindowSize(800, 600); glutCreateWindow("An Example OpenGL Program"); //创建显示窗口 init(); glutDisplayFunc(lineSegment); glutMainLoop(); }

OpenGL从1.0开始--二维观察
文章图片

我们知道,函数glutCreateWindow是用来创建显示窗口的。对这个窗口我们可以进一步细分,使用函数glViewport划定一个视口,指定在输出设备的什么位置进行观察。而窗口选择要看什么,则由裁剪窗口函数gluOrtho2D控制。OpenGL总是裁剪裁剪窗口中的图元映射到视口之中,并放置到显示窗口中显示。
上述过程即是二维观察流水线,我们可以通过下面的流程图建立更专业的认识。
OpenGL从1.0开始--二维观察
文章图片

场景的描述从二维世界坐标系到设备坐标系的映射称为二维观察变换。二维观察变换和三维观察变换相仿,为了便于理解,我们重点在三维观察篇给大家详细讲解。我们继续来分析上面的代码。
我们在创建显示窗口后,创建了一个裁剪窗口
gluOrtho2D(-100.0, 100.0, -100.0, 100.0);

接着创建了一个三角形图元。在二维观察坐标系中,建模坐标系、世界坐标系和观察坐标系通常是一致的。我们裁剪该三角形图元并映射到视口中,显示第一个蓝色三角形;接着我们绘制同样的一个红色三角形,绕观察坐标系原点逆时针旋转90度并显示在第二个视口。不过我们要注意的是这里的旋转并不是和上一篇一样的二维几何变换,它不是直接对图元进行变换,而是对观察坐标系变换。在OpenGL中,变换是指的对当前矩阵的变换,而我们在这段代码中指定的当前矩阵为观察矩阵GL_PROJECTION(正射投影)。
其实,这些窗口都不是一定要进行设置。如果没有为应用程序指定裁剪窗口,默认的裁剪窗口是以坐标系原点为中心、边长为2的规范化正方形。如果我们没有在视口中指定视口,则默认的视口大小及位置与显示窗口一样。如果没有指定显示窗口的尺寸和位置,则默认的尺寸为300*300而默认位置是(-1,-1),即窗口位置的决定权交给窗口管理系统。
如示例所见,一个显示窗口可以创建多个视口。如果我们要获取当前活动视口参数,可以使用查询函数
glGetIntergerv(GL_VIEWPORT,vpArray);

该函数将视口的参数赋值给一个单下标、四元素的矩阵vpArray。
同样的,一个应用也可以创建多个显示窗口并依次赋予显示窗口标识,从1开始。
我们可以记录它的标识:
windowID=glutCreateWindow("An Example OpenGL Program");

利用该标识我们可以做很多事情:
glutDestroyWindow(windowID); //删除GLUT显示窗口

windowID=glutGetWindow(); //询问系统确定当前显示窗口 glutSetWindow(windowID); //指定当前显示窗口

修改GLUT显示窗口的位置和大小:
glutPositionWindow(xNewTopLeft,yNewTopLeft); //改变显示窗口位置 glutReshapewindow(dwNewWidth,dwNewHeight); //改变显示窗口大小 glutFullScreen(); //全屏显示 glutReshapefunction(winReshapeFcn);//指定回调函数响应窗口变化

【OpenGL从1.0开始--二维观察】管理GLUT显示窗口
glutIconifyWindow(); //将当前显示窗口变为一个图符,即最小化 glutSetIconTitle("Icon Name"); //改变图符的名字 glutSetWindowTitle("New Window Name"); //改变窗口的名字 glutPopWindow(); //设置当前窗口为最顶层窗口 glutPushWindow(); //设置当前窗口为最底层窗口 glutHideWindow(); //让当前窗口从屏幕消失 glutShowWindow(); //重新显示

我们可以在一个选中的显示窗口中建立任意数量的二级显示窗口,称为子窗口。
glutCreateSubWindow(windowID,xBottomLeft,yBottomLeft,width,height);

windowID指父窗口ID,其余参数指定窗口位置和大小,我们可以对子窗口进行一些列管理操作,但是不能将其变为图符。
我们甚至可以为当前窗口选择屏幕光标的形状:
glutSetCursor(shape);

shape可以指定的形状包括上下箭头(GLUT_CURSOR_UP_DOWM),旋转箭头(GLUT_CURSORR_CYCLE)等。
使用glutDisplayFunc函数指定窗口显示内容我们已经再熟悉不过了,我们还可以用函数glutPostRedisplay()函数指出当前显示窗口内容应该更新。
最后一个GLUT命令是glutMainLoop(),该函数启动程序执行。此时,显示窗口和其中的图形送到屏幕上。程序同时进入GLUT处理循环,反复查询从鼠标或数据板传来的交互输入等新事件。
在没有其他事件需要处理时可以调用函数glutIdlefunc()指定一个函数来运行,该函数参数可以是一个背景函数或者更新一个动画参数的过程。

    推荐阅读