如何定义视见区和裁剪区

书读三遍,其义自见,OpenGL封装的不是代码,而是各种3D概念。

上一篇介绍到视见区裁剪区物理窗口的概念以及与3D图形之间的关系。这里就讲一下我对视见区和裁剪区的设置的理解,这里主要讲两个函数:

glOrtho(left, right, bottom, top, near, far)//
glViewport (GLint x, GLint y, GLsizei width, GLsizei height)//
下面就来介绍这两个函数的含义和使用(注意,这里glOrgho只是用来设置垂直投影,3D主要有两种投影:垂直投影和透视投影glFrutrum。垂直投影一般用户工程制图中,就是显示那些不用随着离眼睛远近而发生大小变化的图形,一般3D场景用透视投影,因为真实感更强)。

先上代码:

#include "../../shared/gltools.h"	// OpenGL toolkit


// Define a constant for the value of PI
#define GL_PI 3.1415f

// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;

static GLfloat xdt = 0.0f;
static GLfloat ydt = 0.0f;

///
// Called to draw scene
void RenderScene(void)
	{
        GLfloat x,y,z,angle; // Storeage for coordinates and angles

	// Clear the window with current clearing color
	//glClear(GL_COLOR_BUFFER_BIT);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Save matrix state and do the rotation
	glPushMatrix();
	//glRotatef(xRot, 1.0f, 0.0f, 0.0f);
	//glRotatef(yRot, 0.0f, 1.0f, 0.0f);
	//glTranslatef(xdt, ydt, -1.0f);
	
	//glTranslatef(-2.00f, 0.00f, 0.00f);
	glutSolidSphere(20.0, 20, 16);

	// Restore transformations
	glPopMatrix();

	// Flush drawing commands
	//这里不再调用glFlush,因为当我们执行缓冲区交换时,隐含了执行一次刷新操作。
	glutSwapBuffers();
	}

///
// This function does any needed initialization on the 
// rendering context. 
void SetupRC()
	{
	// Black background
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f );

	// Set drawing color to white
	glColor3f(1.0f, 1.0f, 1.0f);

	GLfloat mat_specular[] = {1.0, 1.0, 1.0};
	GLfloat mat_shininess[] = {50.0};
	GLfloat light_position[] = {1.0, 1.0f, 1.0, 0.0};
	GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat Imodel_ambient[] = {0.1, 0.1, 0.1, 1.0};
	glShadeModel(GL_SMOOTH);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
	glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Imodel_ambient);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
}

///
// Window has changed size, recalculate projection
void ChangeSize(int w, int h)
	{
	GLfloat nRange = 20;

	// Prevent a divide by zero
	if(h == 0)
		h = 1;

	// Set Viewport to window dimensions
	// 定义视见区

    <span style="white-space:pre">	</span>glViewport(0, 0, 1.0*w, 1.0*h);

	// Reset coordinate system
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Establish clipping volume (left, right, bottom, top, near, far)
   <span style="white-space:pre">	</span>if (w <= h) 
		glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
	else 
		glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
		
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

///
// Main Program Entry Point
int main(int argc, char* argv[])
	{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800,600);
	glutCreateWindow("Lines Example");
	glutReshapeFunc(ChangeSize);
	glutSpecialFunc(SpecialKeys);
	glutDisplayFunc(RenderScene);
	SetupRC();
	glutMainLoop();

	return 0;
}
上面代码中RanderScene函数中只有做一件事,绘制一个球体:

glutSolidSphere(20.0, 20, 16);注意,第一个参数代表球半径,也就是20.0个单位。

SetupRC()函数可暂时忽略,主要设置照相机位置和物理的反射属性的,也就是光照设置。

这里主要看一下 ChangeSize(int w, int h),这里的w、h分别代表窗口,物理窗口的宽和高。

第一行代码:GLfloat nRange = 20;注意这个值,跟球半径相等。这时候我们看到的是什么呢:


当我们将这个值分别设置为50、100的时候,这个球形的渲染结果又是什么样的呢:



可以看到,随着数值的增大,球体看起来离屏幕越来越远了。这是怎么实现的呢,先来看这个函数:

if (w <= h) 
	glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
else 
	glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);

6个参数别分表示左右(-x,+x),上下(-y,+y),远景(-z,+z);

这个函数简单理解起来,就是一个物体摆在那里,你怎么去截取他。这里,我们先抛开glViewport函数不看。先单独理解glOrtho的功能。 假设有一个球体,半径为20,圆心在(0, 0, 0),那么,我们设定glOrtho(-20, 20, -20, 20, -20, 20);就表示用一个宽高都是40的框框把这个球体整个都装了进来。  如果设定glOrtho(0.0, 20, -20, 20, -20, 20);就表示用一个宽是20, 高是40的框框把整个球体的右面装进来;如果设定glOrtho(0.0, 20, 0.0, 20, -20, 20);就表示用一个宽和高都是20的框框把球体的右上角装了进来。

注意这里将代码修改为:

glutInitWindowSize(500, 500);



glOrgho设置正交平行的视见体,通过它可以将裁剪区映射到物理窗口中,视见区设置的越大,裁剪区相对于视见区就越小,物体显示的就越小,反之则越大。

那么中间还有一行代码:

glViewport(0, 0, w, h);
这个函数主要是将视见区裁剪的图像按怎样的比例显示到屏幕上,这里,我们是将裁剪区的图像,映射到坐标为(0, 0),宽w高h的矩形区域内,也就是整个物理窗口,不明白的地方可以到上一篇文章中关于视见体和裁剪区的描述。

由于我们的裁剪区为正方形,如果窗口不是正方形的话,势必会产生形变,将代码修改为:

glutInitWindowSize(800,600);

如图所示


为了消除形变,下面的代码保证在窗口的大小发生变化时,及时修改视见区的大小(视见区不再是正方形,而是随着窗口的宽高比按一定的规则变化,当窗口被拉的细长时,即高很高,视见区的高也以相应的比例拉大;但窗口被拉的粗短时,即宽很宽,视见区的宽也以相应的比例拉大):

if (w <= h) 
	glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
else 
	glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);




夜色已晚,留待明天续写。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值