书读三遍,其义自见,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);
夜色已晚,留待明天续写。