OpenGL(3) ->窗口,视口,裁剪区,视景体

文章目录

  • 一、先看几个定义
  • 二、两个问题
    • 上次的绘图程序
    • 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 
//显示回调函数
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

这个绘图程序的显示效果是这样的
OpenGL(3) ->窗口,视口,裁剪区,视景体_第1张图片
可以看到,当窗口为默认的正方形时,显示正常;但当窗口尺寸比例变换时,显示的正方形也跟着变了,要是希望不管窗口怎么变化,显示的正方形都保持正方,该怎么做呢?

三、定义视口和裁剪可视区

1、定义视口

  • 利用函数void glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
  • 此函数在已打开的窗口中定义视口,(x,y)= (0,0)为打开窗口和定义视口的左下角位置,宽和高单位为像素
  • 默认状态下,视口和窗口是一样大的

2、定义裁剪可视区

(1) 一共有四个投影函数glFrustumgluPerspectiveglOrthogluOrtho2D,它们将定义不同类型的视景体(裁剪可视区),前两个用于透视投影,后两个用于正射投影。

(2)对于本例这种二维情况,应使用正射投影,关键步骤有以下几个

  1. 重置投影矩阵
    1.进行投影的数学本质是建立投影矩阵进行空间向量运算,上述四个函数都是修改视景体,即创建一个正射投影矩阵,并且用它乘以当前的裁剪区矩阵,所以必须先重置初始矩阵,避免矩阵运算错误。
	glMatrixMode(GL_PROJECTION);	//选定投影矩阵为当前矩阵,告诉OpenGL接下来要进行投影变换
	glLoadIdentity();				//重置当前矩阵
  1. 进行正射投影
    1. 利用函数 glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)修改一个平行视景体如下,在此区域内的物体可以被显示。实际上这个函数的操作是创建一个正射投影矩阵,并且用它乘以当前的裁剪区矩阵
      OpenGL(3) ->窗口,视口,裁剪区,视景体_第2张图片
    2. 正射投影的最大一个特点是无论物体距离相机多远,投影后的物体大小尺寸不变。也就是说,一个处于3D空间中的长方体,人眼看到的应该是近大远小。但使用glOrtho函数进行投影,则远近大小相同。
    3. 如果没有其他变换,正射投影的方向平行于Z轴,且视点朝向Z负轴。这意味着物体在视点前面时far和near都为负值,物体在视点后面时far和near都为正值。

(3)关于视图矩阵

  1. 视图矩阵确定了最后有哪些部分属于可视区域
  2. glOrtho 等函数修改视景体,会导致视图矩阵变化。
  3. 视景体默认值(-1,1,-1,1,-1,1)。这也可以解释上面的问题1,在视景体缺省为(-1,1,-1,1,-1,1)时,正射投影到视口上的坐标为:x从-1到1,y从-1到1,所以定义关键点为glVertex2f(-0.5, 0.5)等,可以使正方形居中显示
  4. 如下代码重置视图矩阵
	glMatrixMode(GL_MODELVIEW);		//选定视图矩阵为当前矩阵
	glLoadIdentity();				//重置当前矩阵

四、窗口放缩回调函数

  • void glutReshapeFunc(void (*func)(int width, int height));注册了一个回调函数,当窗口尺寸发送变化时将会调用此回调函数func
  • 窗口缩放时,除非用glViewport函数设定,否则视口将自动选取到整个窗口大小,而视景体不会变化,正射投影到视口上的坐标也就没有变化。也就是说此时各关键点相对视口的相对位置没变,但由于视口的长宽比例和绝对尺寸都发生变化,显示出的图像也就会随着视口发生长宽比例变化和绝对大小变化。换句话说,相当于直角坐标系中,点坐标没变,但有一条坐标轴的比例尺变了,导致点的绝对位置比例发生变化
  • 要想控制图像的长宽比例不变(正方形保持正方),就需要调整视景体。如果窗口高度比例增加了,视景体y方向也要等比例增大;如果窗口宽度比例增加了,视景体x方向也要等比例增大。换句话说,相当于直角坐标系中,有一条坐标轴的取值范围变了但比例关系没变,点的相对位置变化但绝对位置没变
  • 修改程序如下,可以实现比例保持
#include 
//显示回调函数
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,这种设置如下图所示
    OpenGL(3) ->窗口,视口,裁剪区,视景体_第3张图片

你可能感兴趣的:(#,OpenGL)