OpenGL——基础变换

/*******************************************************************/

本文总结自OpenGL入门教程》大家可以下载。

/*******************************************************************/

 

我们生活在一个三维的世界——如果要观察一个物体,我们可以:
1、从不同的位置去观察它。(视图变换
2、移动或者旋转它,当然了,如果它只是计算机里面的物体,我们还可以放大或缩小它。(模型变换
3、如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(剪裁)。(投影变换
4、我们可能希望把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部。(视口变换
这些,都可以在OpenGL中实现。

 

1、  模型变换和视图变换
从“相对移动”的观点来看,改变观察点的位置与方向和改变物体本身的位置与方向具有等效性。在OpenGL中,实现这两种功能甚至使用的是同样的函数。 由于模型和视图的变换都通过矩阵运算来实现,在进行变换前,应先设置当前操作的矩阵为“模型视图矩阵”。设置的方法是以GL_MODELVIEW为参数调用glMatrixMode函数,像这样:
glMatrixMode(GL_MODELVIEW);
通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。这也只需要一行代码:
glLoadIdentity();

然后,就可以进行模型变换和视图变换了。进行模型和视图变换,主要涉及到三个函数:
glTranslate*,把当前矩阵和一个表示移动物体的矩阵相乘。三个参数分别表示了在三个坐标上的位移值。
glRotate*,把当前矩阵和一个表示旋转物体的矩阵相乘。物体将绕着(0,0,0)到(x,y,z)的直线以逆时针旋转,参数angle表示旋转的角度。
glScale*,把当前矩阵和一个表示缩放物体的矩阵相乘。x,y,z分别表示在该方向上的缩放比例。

以上都是针对改变物体的位置和方向来介绍的。如果要改变观察点的位置,除了配合使用glRotate*和glTranslate*函数以外,还可以使用这个函数:

gluLookAt。它的参数比较多,前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到(x,y,z)的直线,它表示了观察者认为的“上”方向。

 

2、投影变换
投影变换就是定义一个可视空间,可视空间以外的物体不会被绘制到屏幕上。(注意,从现在起,坐标可以不再是-1.0到1.0了!)
OpenGL支持两种类型的投影变换,即透视投影和正投影。投影也是使用矩阵来实现的。如果需要操作投影矩阵,需要以GL_PROJECTION为参数调用glMtrixMode函数。
glMatrixMode(GL_PROJECTION);
通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。
glLoadIdentity();

使用glFrustum函数可以将当前的可视空间设置为透视投影空间。其参数的意义如下图:

也可以使用更常用的gluPerspective函数。其参数的意义如下图:

正投影相当于在无限远处观察得到的结果,它只是一种理想状态。但对于计算机来说,使用正投影有可能获得更好的运行速度。
使用glOrtho函数可以将当前的可视空间设置为正投影空间。其参数的意义如下图:

如果绘制的图形空间本身就是二维的,可以使用gluOrtho2D。他的使用类似于glOrgho。

 

 

3、视口变换
当一切工作已经就绪,只需要把像素绘制到屏幕上了。这时候还剩最后一个问题:应该把像素绘制到窗口的哪个区域呢?通常情况下,默认是完整的填充整个窗口,但我们完全可以只填充一半。

使用glViewport 来定义视口。其中前两个参数定义了视口的左下脚(0,0表示最左下方),后两个参数分别是宽度和高度。

 

4、操作矩阵堆栈

我们在进行矩阵操作时,有可能需要先保存某个矩阵,过一段时间再恢复它。当我们需要保存时,调用glPushMatrix函数,它相当于把矩阵(相当于盘子)放到堆栈上。当需要恢复最近一次的保
存时,调用glPopMatrix函数,它相当于把矩阵从堆栈上取下。OpenGL规定堆栈的容量至少可以容纳32个矩阵,某些OpenGL实现中,堆栈的容量实际上超过了32个。因此不必过于担心矩阵
的容量问题。
通常,用这种先保存后恢复的措施,比先变换再逆变换要更方便,更快速。
注意:模型视图矩阵和投影矩阵都有相应的堆栈。使用glMatrixMode来指定当前操作的究竟是模型视图矩阵还是投影矩阵。

 

#include "stdafx.h"
#include <GL/glut.h> 
#include <time.h>
static int day = 0; // day的变化:从0到359

double CalFrequency() //计算帧率FPS
{ 
	static int count; 
	static double save; 
	static clock_t last, current; 
	double timegap; 

	++count; 
	if( count <= 50 )//计算50帧的总时间 
		return save; 
	count = 0; 
	last = current; 
	current = clock(); 
	timegap = (current-last)/(double)CLK_TCK; 
	save = 50.0/timegap;//计算帧率
	return save; 
} 

void myDisplay(void) 
{ 
	double FPS = CalFrequency(); 
	printf("FPS = %f\n", FPS); 
	// 太阳、地球和月亮 
	// 假设每个月都是30天 
	// 一年12个月,共是360天 
	glEnable(GL_DEPTH_TEST); 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
	//使用glMatrixMode来确定该操作是模型视图矩阵还是投影矩阵

	glMatrixMode(GL_PROJECTION);//投影变换:定义一个可视空间,可是空间以外的物体不会被绘制在屏幕上; 
	glLoadIdentity(); //变换前将当前矩阵设置为单位矩阵;
	gluPerspective(75, 1, 1, 400000000); //正投影;
	
	glMatrixMode(GL_MODELVIEW);//模型和视图变换;
	glLoadIdentity();//在进行变换前把当前矩阵设置为单位矩阵; 
	gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);//改变观察点的位置; 

	// 绘制红色的“太阳” 
	glColor3f(1.0f, 0.0f, 0.0f); 
	glutSolidSphere(69600000, 20, 20); 
	// 绘制蓝色的“地球” 
	glColor3f(0.0f, 0.0f, 1.0f); 
	glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f); 
	glTranslatef(150000000, 0.0f, 0.0f); 
	glutSolidSphere(15945000, 20, 20); 
	// 绘制黄色的“月亮” 
	glColor3f(1.0f, 1.0f, 0.0f); 
	glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f); 
	glTranslatef(38000000, 0.0f, 0.0f); 
	glutSolidSphere(4345000, 20, 20); 

	glFlush(); 
	glutSwapBuffers(); //
} 
void myIdle(void) 
{ 
	/* 新的函数,在空闲时调用,作用是把日期往后移动一天并重新绘制,达到动画效果 */ 
	++day; 
	if( day >= 360 ) 
		day = 0; 
	myDisplay(); 
} 
int main(int argc, char *argv[]) 
{ 
	glutInit(&argc, argv); 
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); 
	glutInitWindowPosition(100, 100); 
	glutInitWindowSize(400, 400); 
	glutCreateWindow("太阳,地球,月亮"); 
	glutDisplayFunc(&myDisplay);
	glutIdleFunc(&myIdle);               // 新加入了这句:表示在CPU空闲的时间调用某一函数
	glutMainLoop(); 
	return 0; 
}


 

可以参考文章:

OpenGL 基础图形绘制与投影变换

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值