从前面的最简单例子可以看出,我们几乎没有进行任何关于颜色和坐标系的配置,OpenGL就已经能够实施渲染了。这是因为OpenGL本身管理了很多渲染时需要的状态数据,并且在初始化时自动设置了合理的默认值。例如,默认的清屏颜色就是黑色,这才是我们看到窗口客户区呈现黑色的原因。
OpenGL的渲染需要很多的状态数据来供其使用,如果把所有的这些数据都作为参数传递给渲染函数的话,那么此函数的样子大概是这样的。
glXXX(渲染上下文,颜色,坐标,视口,光照,雾,.....);这对于客户端使用来说,简直就是场噩梦啊!由于大多数渲染行为都需要这些状态数据,所以O彭GL干脆把这些大家都使用的数据做成了全局变量,供所有渲染函数使用。这样就可以把渲染函数简化成了
glXXX(坐标);当然了,全局变量也带来了渲染函数不可重入的问题,还好在OpenGL中,函数重入不算重要。人们习惯称这种使用了大量全局状态数据的程序为状态机。
其实好多库都采取了状态机的机制,如Windows GDI。
2 视口与视口坐标系概念计算机图形学的本质就是创建三维物体的二维图像,涉及到多个坐标系间的转化。本文从最简单的视口坐标系开始。视口(Viewport)就是最终渲染结果显示的目的地。它是一个矩形的区域,长度单位是像素,视口的位置和大小在视口坐标系中定义。视口坐标系是标准的笛卡尔直角坐标系,其原点位于渲染环境窗口客户区的左下角,横轴(x)向右为正,纵轴(y)向上为正。如下图所示:
注意:视口坐标系与Windows的窗口坐标系是不同的。
OpenGL用来设置视口的函数是:
函数参数都是针对视口坐标系的。例如
glViewport(100,50, 200, 150);设置的视口位置及大小如下图所示:
每一次渲染的执行(glBegin()和glEnd()中间的代码)都是把当前视口作为最终输出目的地。视口也是OpenGL内部维护的状态变量之一,它可以在一帧的渲染中多次改变,OpenGL在执行渲染时都是使用当前的视口。窗口客户区可以被分割为多个视口,但是同一时刻只有一个视口生效。
在初始化OpenGL窗口环境时,视口被设置为布满整个客户区。
3 测试视口设置 3.1 移动视口我们在WM_LBUTTONDOWN事件的响应中,把视口的左下角坐标设置为鼠标的位置,宽度设置为200像素,高度设置为150像素。然后重新进行OpenGL渲染。
void OnLeftButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { RECT clientRect; ::GetClientRect(hWnd, &clientRect); int xPos = GET_X_LPARAM(lParam); /* 需要包含 <windowsx.h> */ int yPos = GET_Y_LPARAM(lParam); int x = xPos; /* 把窗口坐标系坐标转换为视口坐标系坐标 */ int y = clientRect.bottom - yPos; glViewport(x, y, 200, 150); InvalidateRect(hWnd, NULL, TRUE); }在WM_PAINT的事件处理中,通过绘制布满整个可视空间的矩形来标注出当前的视口位置与大小。注意:我们绘制时使用了默认的颜色状态(白色)。
void OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); glVertex2d(-1, -1); glVertex2d(1, -1); glVertex2d(1, 1); glVertex2d(-1, 1); glEnd(); HDC hdc = ::GetDC(hWnd); ::SwapBuffers(hdc); ::ReleaseDC(hWnd, hdc); }这样,每当在客户区点击一下鼠标,当前的视口区域就会被标注出来。
源码下载 3.2 多视口
OpenGL的视口也是其内部维护的众多状态变量之一,在某一次OpenGL的渲染中(glBegin()和glEnd()之间的渲染代码),只能使用当前视口进行输出。但是在一帧图像可以包含任意多次OpenGL渲染,也就是说,程序中可以多次调用glBengin()和glEnd()。例如如下伪代码:
glViewport(A); glBegin(); 渲染在房子里面看到的场景 glEnd(); glViewport(B); glBegin(); 渲染在外面看房子的场景 glEnd();这样我们就得到了两个不同的视口A,B。最终A和B组成了一帧完整的图像。
我们的程序现在还不能渲染出这么复杂的场景,那么就用简单的颜色矩形来表示吧。
修改渲染函数如下:
源码下载 4 视口小结