OpenGL 中对 glViewport 和 gluOrtho2D 的理解

OpenGL 中有俩API让我初学时特别懵逼:

这俩函数之间到底有啥区别和联系呢?本文将以(个人认为)很直观的方式为大家解读,给自己也存个档。

介绍上述俩函数前,先讲一讲OpenGL中使用Glut创建 窗口 的相关内容,下面是三个相关API:


glViewport(GLint x, GLint y, GLsizei width, GLsizei height)

该函数表示设置视口(viewport),何为视口?就是眼睛能看到的区域。

参数中的x,y所在坐标系是以窗口的左下角为坐标原点(0, 0),向右是x轴正轴,向上是y轴正轴,后两个参数width,height指定的就是视口范围,在范围之外绘制的内容我们都是看不到的。

我们暂停先不继续说gluOrtho2D。代码是理解问题的最好方式,我们先来一段代码直观感受下上述函数的作用(下述代码可粘贴至编辑器直接运行哦):

#include "GL/glut.h"
void mydisplay(void) {
	glClearColor(1, 1, 1, 0);
	glViewport(0, 0, 200, 200);
	glClear(GL_COLOR_BUFFER_BIT);
	
	glColor3f(1, 1, 0);
	glRectf(-0.5, -0.5, 0, 0);
	glFlush();
}
void main() {
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(400, 400);
	glutCreateWindow("opengl");
	glutDisplayFunc(mydisplay);
	glutMainLoop();
}

运行结果如下图1.1:其中的窗口大小为400 *400,视口区域(viewport)为窗口分成四份后的左下的一份,即左下200 *200的一块区域。

1.1 运行结果 有人肯定疑惑这个黄色正方形的坐标到底是怎么决定的呢,它为何会绘制到如图所示的位置呢?

解释:上述代码段中绘制矩形的代码是glRectf(-0.5, -0.5, 0, 0),该函数的参数格式是(x1, y1, x2, y2),即指定要绘制矩形的左下和右上坐标,从而绘制一个矩形,f表示参数类型是浮点型,也就是说可以写小数。重点来袭:当我们没有使用gluOrtho2D函数进行配置时,此时图像绘制函数(比如此处的glRectf)的参考坐标系是这样的:

2.2 坐标系说明

其中蓝色框表示的是400 *400的窗口,黑色框表示200 *200的视口,两条红线则是图像绘制函数所参考的坐标系,o为坐标原点(0,0)。知道了这个,第二个疑问就来了:glRectf(-0.5, -0.5, 0, 0)的两个坐标点是(-0.5,-0.5)和(0,0)啊,视口大小不是200 *200么,怎么会画出图里所说位置和大小的矩形呢?

因为当没有设置gluOrtho2D时,OpenGL会默认为视口区域安排一个如图中两条红线表示的坐标系,这个坐标系的的xy轴的坐标范围并不等于 视口的宽高所表示的范围,而是这个样子的:x轴坐标范围是[-1, 1], y轴坐标范围也是[-1,1]。按照这么理解,各位应该就能明白这个黄色矩形为啥会这么绘制出来了吧(有些小懒,没有配图了)。

如果我们不想使用这种默认的-1,1的坐标范围呢?此时就需要gluOrtho2D函数登场了

gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);

它的四个参数(left, right, bottom, top)就正好代表了坐标系的范围,即x轴范围设为[left, right],y轴范围设为[bottom, top]。继续上代码:

#include "GL/glut.h"
void mydisplay(void) {
	glClearColor(1, 1, 1, 0);
	glViewport(0, 0, 200, 200);
	glClear(GL_COLOR_BUFFER_BIT);

	gluOrtho2D(-200, 200, -200, 200); // 新增

	glColor3f(1, 1, 0);
	// glRectf(-0.5, -0.5, 0, 0); // 注释掉
	glRectf(-100, -100, 0, 0); // 新增
	glFlush();
}
void main() {
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(400, 400);
	glutCreateWindow("opengl");
	glutDisplayFunc(mydisplay);
	glutMainLoop();
}

运行结果和第一次代码的结果是一样的,不同的地方就是我们在代码里设置了gluOrtho2D(-200, 200, -200, 200);,把坐标系范围进行了改变,因此之前的glRectf(-0.5, -0.5, 0, 0);的运行结果就变成了一个非常非常非常非常小的黄色矩形以至于肉眼根看不见,因此我们把它注释掉并新增了一句glRectf(-100, -100, 0, 0)

多敲码多实践,什么都会懂的 😄