文章目录
- 一、先看几个定义
- 二、两个问题
- 上次的绘图程序
- 1、问题1
- 2、问题2
- 三、定义视口和裁剪可视区
- 1、定义视口
- 2、定义裁剪可视区
- 四、窗口放缩回调函数
- (1)void glutReshapeFunc(void (*func)(int width, int height));
- (2)void glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
- (3)void glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
一、先看几个定义
-
窗口
:即屏幕中的某一个窗口,可放大放小和移动关闭。 -
视口
:即在窗口中可以见到或可以用来绘图的部分。一般设置视口等于窗口。 -
视景体(裁剪可视区)
:是指成像景物所在空间的集合,它是一个空间集合体。可以把它想象成一个房间,在这个可显示区域内的一切物体都可以被显示。也就是说视景体会在三维空间中裁切一部分,允许其中裁下来的部分经过投影变换后在视口内可见。整个视景体的三维坐标将完全映射到整个视口的二维坐标上 -
投影变换
:定义一个视景体,并把它按仿射投影或正射投影形式投射到二维的视口上,投影的方式会影响视景体的类型。裁剪出的可视部分可以在视口上显示出来
参考:OpenGL投影变换:视景体及glFrustum、gluPerspective 与glOrtho、gluOrtho2D
二、两个问题
上次的绘图程序
还记得上次看过的画图程序吗?OpenGL(2) ->第一个程序 这个程序里简单地通过显示回调函数,程序如下
#include <GL/glut.h>
//显示回调函数
void myDisplay()
{
glClearColor(0.0, 0.0, 1.0, 0.0); //设置颜色缓冲为蓝色
glClear(GL_COLOR_BUFFER_BIT); //根据颜色缓冲给整个窗口涂色
glColor3f(1.0, 0.0, 0.0); //绘制颜色Red
glBegin(GL_LINE_LOOP); //开始绘图(封闭线段模式)
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glVertex2f(-0.5, -0.5);
glEnd(); //结束绘图
glFlush(); //立即显示
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv); //对GLUT进行初始化
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); //显示方式为 单缓冲 RGB色彩
glutInitWindowPosition(100, 100); //窗口(左上角)位置
glutInitWindowSize(400, 400); //窗口尺寸(单位为像素)
glutCreateWindow("第一个OpenGL程序"); //窗口标题
glutDisplayFunc(&myDisplay); //显示回调函数
glutMainLoop();
return 0;
}
1、问题1
上次并没有说明关键点的坐标系统,所以大家可能不太清除为什么要把坐标定义为glVertex2f(-0.5, 0.5);
这样的值
2、问题2
这个绘图程序的显示效果是这样的
可以看到,当窗口为默认的正方形时,显示正常;但当窗口尺寸比例变换时,显示的正方形也跟着变了,要是希望不管窗口怎么变化,显示的正方形都保持正方,该怎么做呢?
三、定义视口和裁剪可视区
1、定义视口
- 利用函数void glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
- 此函数在已打开的窗口中定义视口,(x,y)= (0,0)为打开窗口和定义视口的左下角位置,宽和高单位为像素
- 默认状态下,视口和窗口是一样大的
2、定义裁剪可视区
(1) 一共有四个投影函数glFrustum、gluPerspective 与glOrtho、gluOrtho2D,它们将定义不同类型的视景体(裁剪可视区),前两个用于透视投影,后两个用于正射投影。
(2)对于本例这种二维情况,应使用正射投影,关键步骤有以下几个
- 重置投影矩阵:
1.进行投影的数学本质是建立投影矩阵进行空间向量运算,上述四个函数都是修改视景体,即创建一个正射投影矩阵,并且用它乘以当前的裁剪区矩阵,所以必须先重置初始矩阵,避免矩阵运算错误。
glMatrixMode(GL_PROJECTION); //选定投影矩阵为当前矩阵,告诉OpenGL接下来要进行投影变换
glLoadIdentity(); //重置当前矩阵
- 进行正射投影:
- 利用函数 glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) 来修改一个平行视景体如下,在此区域内的物体可以被显示。实际上这个函数的操作是创建一个正射投影矩阵,并且用它乘以当前的裁剪区矩阵
- 正射投影的最大一个特点是无论物体距离相机多远,投影后的物体大小尺寸不变。也就是说,一个处于3D空间中的长方体,人眼看到的应该是近大远小。但使用glOrtho函数进行投影,则远近大小相同。
- 如果没有其他变换,正射投影的方向平行于Z轴,且视点朝向Z负轴。这意味着物体在视点前面时far和near都为负值,物体在视点后面时far和near都为正值。
(3)关于视图矩阵
- 视图矩阵确定了最后有哪些部分属于可视区域
- glOrtho 等函数修改视景体,会导致视图矩阵变化。
- 视景体默认值(-1,1,-1,1,-1,1)。这也可以解释上面的问题1,在视景体缺省为(-1,1,-1,1,-1,1)时,正射投影到视口上的坐标为:x从-1到1,y从-1到1,所以定义关键点为
glVertex2f(-0.5, 0.5)等
,可以使正方形居中显示 - 如下代码重置视图矩阵
glMatrixMode(GL_MODELVIEW); //选定视图矩阵为当前矩阵
glLoadIdentity(); //重置当前矩阵
四、窗口放缩回调函数
- void glutReshapeFunc(void (*func)(int width, int height));注册了一个回调函数,当窗口尺寸发送变化时将会调用此回调函数func
- 窗口缩放时,除非用glViewport函数设定,否则视口将自动选取到整个窗口大小,而视景体不会变化,正射投影到视口上的坐标也就没有变化。也就是说此时各关键点相对视口的相对位置没变,但由于视口的长宽比例和绝对尺寸都发生变化,显示出的图像也就会随着视口发生长宽比例变化和绝对大小变化。换句话说,相当于直角坐标系中,点坐标没变,但有一条坐标轴的比例尺变了,导致点的绝对位置比例发生变化
- 要想控制图像的长宽比例不变(正方形保持正方),就需要调整视景体。如果窗口高度比例增加了,视景体y方向也要等比例增大;如果窗口宽度比例增加了,视景体x方向也要等比例增大。换句话说,相当于直角坐标系中,有一条坐标轴的取值范围变了但比例关系没变,点的相对位置变化但绝对位置没变
- 修改程序如下,可以实现比例保持
#include <GL/glut.h>
//显示回调函数
void myDisplay()
{
glClearColor(0.0, 0.0, 1.0, 0.0); //背景blue
glClear(GL_COLOR_BUFFER_BIT); //根据颜色缓冲涂色
glColor3f(1.0, 0.0, 0.0); //线Red
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); //指定绘图时的坐标系统,分别是x,y,z的最小、最大值
glBegin(GL_LINE_LOOP); //开始绘图(封闭线段模式)
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glVertex2f(-0.5, -0.5);
glEnd(); //结束绘图
glFlush(); //立即显示
}
//缩放回调函数
//当窗口放缩时,要重新进行坐标映射(重新定义剪裁区域),如果没有这个,放缩后图像会变形
void changeSize(GLsizei w,GLsizei h)//GLsziei 看作 int
{
if (h == 0) //防止除0
h = 1;
GLfloat scale = (GLfloat)w /(GLfloat)h; //窗口协调比例
glViewport(0, 0, w, h); //设置视口为整个窗口
//设置坐标系统
glMatrixMode(GL_PROJECTION); //重置投影矩阵,告诉OpenGL接下来做投影变换
glLoadIdentity();
//等比例修改剪裁区域
if (w < h)
glOrtho(-1.0, 1.0, -1.0 / scale, 1.0 / scale, -1.0, 1.0);
else
glOrtho(-1.0*scale, 1.0*scale, -1.0 , 1.0, -1.0, 1.0);
//告诉openGL未来的转换将影响绘制的图形
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv); //对GLUT进行初始化
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); //显示方式为 单缓冲 RGB色彩
glutInitWindowPosition(100, 100); //窗口(左上角)位置
glutInitWindowSize(400, 400); //窗口尺寸(单位为像素)
glutCreateWindow("第一个OpenGL程序"); //窗口标题
glutDisplayFunc(&myDisplay); //显示回调函数
glutReshapeFunc(&changeSize); //窗口放缩回调函数
glutMainLoop();
return 0;
}
再回顾一下几个重要的函数
(1)void glutReshapeFunc(void (*func)(int width, int height));
- 窗口放缩回调函数,这个函数告诉 GLUT :当窗口大小发生化时,func函数将被自动调用
- 如果不在这个函数里重新设置视口并裁剪可视区域(重置坐标映射),在窗口放缩时显示将会变形
(2)void glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
- 在打开的窗口中定义视口,(x,y)= (0,0)为打开窗口和定义视口的左下角位置,宽和高单位为像素
(3)void glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
- 这是一个正投影映射
- 此函数指定了一个平行视景体,体现在确定了绘制最终图像时的坐标系统
- 绘图窗口(像素数量描述其大小)映射到此函数参数值确定的视景体坐标范围内,默认正射投影的方向平行于Z轴,且视点朝向Z负轴,所以x坐标范围[left,right];y坐标范围[bottom,top]
- 视景体参数默认为-1,1,-1,1,-1,1,这种设置如下图所示