计算机图形学(4.1)——OpenGL模型视图变换

实验原理

我们生活在一个三维的世界——如果要观察一个物体,我们可以:

1、从不同的位置去观察它(人运动,选定某个位置去看)。(视图变换)

2、移动或者旋转它,当然了,如果它只是计算机里面的物体,我们还可以放大或缩小它(物体运动,让人看它的不同部分)。(模型变换)

3、如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(指定看的范围)。(投影变换)

4、我们可能希望把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部(指定在显示器窗口的那个位置显示)。(视口变换)

这些,都可以在OpenGL中实现。

“相对移动”的观点来看,改变观察点的位置与方向和改变物体本身的位置与方向具有等效性。在OpenGL中,实现这两种功能甚至使用的是同样的函数。

由于模型和视图的变换都通过矩阵运算来实现,在进行变换前,应先设置当前操作的矩阵为“模型视图矩阵”。设置的方法是以GL_MODELVIEW为参数调用glMatrixMode函数,像这样:

glMatrixMode(GL_MODELVIEW);

该语句指定一个4×4的建模矩阵作为当前矩阵。

通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。把当前矩阵设置为单位矩阵的函数为:

glLoadIdentity();

我们在进行矩阵操作时,有可能需要先保存某个矩阵,过一段时间再恢复它。当我们需要保存时,调用glPushMatrix()函数,它相当于把当前矩阵压入堆栈。当需要恢复最近一次的保存时,调用glPopMatrix()函数,它相当于从堆栈栈顶弹出一个矩阵为当前矩阵。OpenGL规定堆栈的容量至少可以容纳32个矩阵,某些OpenGL实现中,堆栈的容量实际上超过了32个。因此不必过于担心矩阵的容量问题。

通常,用这种先保存后恢复的措施,比先变换再逆变换要更方便,更快速。

注意:模型视图矩阵和投影矩阵都有相应的堆栈。使用glMatrixMode来指定当前操作的究竟是模型视图矩阵还是投影矩阵。

在代码中,视图变换必须出现在模型变换之前,但可以在绘图之前的任何时候执行投影变换和视口变换。

1.display()程序中绘图函数潜在的重复性强调了:在指定的视图变换之前,应该使用glLoadIdentity()函数把当前矩阵设置为单位矩阵。

2.在载入单位矩阵之后,使用gluLookAt()函数指定视图变换。如果程序没有调用gluLookAt(),那么照相机会设定为一个默认的位置和方向。在默认的情况下,照相机位于原点,指向Z轴负方向,朝上向量为(0,1,0)。

3.一般而言,display()函数包括:视图变换 + 模型变换 + 绘制图形的函数(如glutWireCube())。display()会在窗口被移动或者原来先遮住这个窗口的东西被一开时,被重复调用,并经过适当变换,保证绘制的图形是按照希望的方式进行绘制。

4.在调用glFrustum()设置投影变换之前,在reshape()函数中有一些准备工作:视口变换 + 投影变换 + 模型视图变换。由于投影变换,视口变换共同决定了场景是如何映射到计算机的屏幕上的,而且它们都与屏幕的宽度,高度密切相关,因此应该放在reshape()中。reshape()会在窗口初次创建,移动或改变时被调用。

OpenGL中矩阵坐标之间的关系<--物理坐标*模型视图矩阵*投影矩阵*透视除法*规范化设备坐标——〉窗口坐标

计算机图形学(4.1)——OpenGL模型视图变换_第1张图片 

1)视图变换函数gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0,)设置照相机的位置把照相机放在(0,0,5),镜头瞄准(0,0,0),朝上向量定为(0,1,0)朝上向量为照相机指定了一个唯一的方向。如果没有调用gluLookAt,照相机就设定一个默认的位置和方向,在默认情况下,照相机位于原点,指向Z轴的负方向,朝上向量为(0,1,0)

glLoadIdentity()函数把当前矩阵设置为单位矩阵。

2)使用模型变换的目的是设置模型的位置和方向

3)投影变换,指定投影变换类似于为照相机选择镜头,可以认为这种变换的目的是确定视野,并因此确定哪些物体位于视野之内以及他们能够被看到的程度。

除了考虑视野之外,投影变换确定物体如何投影到屏幕上,OpenGL提供了两种基本类型的投影,1、透视投影:远大近小;2、正投影:不影响相对大小,一般用于建筑和CAD应用程序中

4)视口变换

视口变换指定一个图象在屏幕上所占的区域

5)绘制场景
//示例程序:
// CG_5_opengl模型视图变换.cpp : 定义控制台应用程序的入口点。
//vs2010

#include "stdafx.h"
#include 
#include 

static int year=0,day=0;

void init(void)
{
	glClearColor(0.0,0.0,0.0,0.0);
	glShadeModel(GL_FLAT);
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(1.0,1.0,1.0);

	glPushMatrix();
	glutWireSphere(1.0,20,16);                //画太阳,参数分别是:球体的半径,以Z轴上线段为直径分布的圆周线的条数,绕在Z轴周围的线的条数
	glRotatef((GLfloat) year,0.0,1.0,0.0);   //公转
	glTranslatef(2.0,0.0,0.0);               //自转
	glRotatef((GLfloat) day,0.0,1.0,0.0);
	glutWireSphere(0.2,10,8);                //画行星
	glPopMatrix();
	glutSwapBuffers();
}

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei) w,(GLsizei) h);
	glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	gluPerspective(60.0,(GLfloat) w/(GLfloat) h,1.0,20.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();                                 //将当前矩阵设置为单位矩阵
	gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);   //指定视图变换,设置照相机的位置
}

void keyboard(unsigned char key,int x,int y)     //键盘按键控制行星运动,旋转一周为360度
{
	switch(key)
	{
	case 'd':
		day=(day+10)%360;
		glutPostRedisplay();
		break;
	case 'D':
		day=(day-10)%360;
		glutPostRedisplay();
		break;
	case 'y':
		year=(year+5)%360;
		glutPostRedisplay();
		break;
	case 'Y':
		year=(year-5)%360;
		glutPostRedisplay();
		break;
	case 27:
		exit(0);
		break;
	default:
		break;
	}
}

int main(int argc,char** argv)
{
	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(500,500);
	glutInitWindowPosition(100,100);
	glutCreateWindow(argv[0]);
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMainLoop();
	return 0;
}
运行结果:
计算机图形学(4.1)——OpenGL模型视图变换_第2张图片

提高篇:

增加一个行星,一个卫星



// CG_5_1_增加卫星行星.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include
#include
static int year = 0,day = 0,moon = 0;

void init(void)
{
	glClearColor(0.0,0.0,0.0,0.0);
	glShadeModel(GL_FLAT);
}
 void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	//glMatrixMode(GL_MODELVIEW);
	//glLoadIdentity();
	glColor3f (1.0,1.0,1.0);
	
	glPushMatrix();
	glutWireSphere(1.0,20,16);   //画太阳
	glRotatef((GLfloat) year,0.0,1.0,0.0);
	glTranslatef(1.5,0.0,0.0);
	glRotatef((GLfloat) day,0.0,1.0,0.0);
	glutWireSphere(0.2,10,8);  //画行星
	glPopMatrix();
	glPushMatrix();
	glRotatef((GLfloat) year,0.0,1.0,0.0);
	glTranslatef(2.5,-1.0,0.0);
	glRotatef((GLfloat) day,0.0,1.0,1.0);
	glutWireSphere(0.2,10,8);//画行星
	glRotatef((GLfloat) year,0.0,1.0,0.0);
	glTranslatef(0.5,0.0,0.0);
	glRotatef((GLfloat) moon,1.0,1.0,1.0);
	glutWireSphere(0.1,4,2);  //画卫星
	glPopMatrix();
	glutSwapBuffers();
}

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei) w, (GLsizei) h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat) w / (GLfloat) h, 1.0, 20.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
void keyboard(unsigned char key, int x, int y){
	switch(key){
	case'd':
		day = (day + 10) % 360;
		moon = (moon + 5) % 360;
		glutPostRedisplay();
		break;
	case'D':
		day = (day - 10) % 360;
		glutPostRedisplay();
		break;
	case'y':
		year = (year + 5) % 360;
		day = (day + 10) % 360;
		moon = (moon + 5) % 360;
		glutPostRedisplay();
		break;
	case'Y':
		year = (year - 5) % 360;
		glutPostRedisplay();
		break;
	case'm':
		moon = (moon + 5) % 360;
		glutPostRedisplay();
		break;
	case 27:
		exit(0);
		break;
	default:
		break;
	}
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(800,500);
	glutInitWindowPosition(100,100);
	glutCreateWindow (argv[0]);
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMainLoop();
	return 0;
}
运行结果:
计算机图形学(4.1)——OpenGL模型视图变换_第3张图片


你可能感兴趣的:(计算机图形学(4.1)——OpenGL模型视图变换)