1.投影模式、观察方位、视口显示等的初始化
glutReshapeFunc(reshape);//注册重绘回调函数
该函数在窗口大小改变以及初始窗口时被调用,在这个函数里,我们完成关于坐标系显示的一系列初始化。
调用该函数设定了截取的图形以怎样的比例显示在视窗上,我们默认用原本窗体的比例。
接下来是设置投影模式,在设置之前先要将矩阵模式转换为投影。
·正投影
glOrtho(-3, 3, -3, 3, -100, 100); (左,右,下,上,近,远 )
六个参数划分出了一个立方体空间,这个空间里物体将以正投影的模式表现。也就是说,不同于透视投影的近大远小,在移动的过程中,观察到的物体大小不会发生变化。这也就解释了为什么在正投影下让物体前后移动,是无法被观测出来的。
·透视投影
gluPerspective(45, whRatio, 1,100); (视角,宽高比,近处,远处)
透视投影中,物体的显示更加符合人眼的观察,前后移动物体时,物体会产生大小的变化。
在绘制的开始,我们通过gluLookAt函数设定了观察物体的方式。
gluLookAt(eye[0], eye[1], eye[2],
center[0], center[1],center[2],
0, 1, 0);
该函数的每一行参数分别代表了观察点位置,望向的位置,观察方向(头顶方向)。
在这里,因为我们希望完成通过按键对物体操控的操作,我们需要注册按键回调函数,它不是在程序启动时执行,而是等待用户完成了某些操作后,再通过函数指针调用函数。
2.光源、深度的初始化
在本次实验中,开启了深度测试,加入了环境光。
glEnable(GL_DEPTH_TEST); //开启深度测试
glEnable(GL_LIGHTING);//开启光照模式
在深度测试算法中,我们通过扫描投影在x0y平面上每一点的z坐标的大小,来判断谁在前面,谁在后面,也就是确定遮挡关系,只显示z坐标小的像素,进而完成遮挡效果。
在光照模式下,我们只开启了一个白色的环境光源。事实上,在opengl中,我们可以设置多种光源,包括环境光、漫反射光、镜面反射光,构建光照模型,来模拟现实中的光照。
3.茶壶和桌子的移动
首先,要明确一点的是,在我们未对物体进行平移旋转等操作时,物体始终处于原点,而之后进行的一系列操作是在物体的局部坐标系上进行的,初始和世界坐标系重合,而进行矩阵操作后将发生变化。
在绘制茶壶和桌子时,它们被绘制在了原点处,中间经历了一些旋转变换以方便桌子的绘制。
当我们希望茶壶和桌子向上下左右移动时,除了可以直接让它们自身移动,也就是乘以glTranlatef矩阵,也可以考虑让观察者动起来,那么,看到的效果,也同样是物体的移动。
那么我们很容易分析出,当我们希望物体向左移动时,我们的眼睛和望向的位置,都应该向右移动相同的距离,如下图所示。这样,我们的物体就会出现在视野的左边,并且,我们看到的依旧是茶壶的正方向。
1)视点右移,观察位置不变。
2)观察位置右移,视点不变。
这种情况下,物体确实在视野中“左移”了,但仔细观察后,会发现,观察到的不再是物体的正面了,而是略有一个倾斜的角度。
4.茶壶的移动与旋转
单独物体的移动,应在绘制茶壶时直接对物体进行平移、旋转操作,即调用glTranslate,glRotatef函数,这是前一次实验的主要内容,所以在这里不做过多阐述。我们把位置信息存在数组里,在键盘指令下修改数组内容,并通过参数传入来完成绘制。
我们发现在按键回调函数中,我们只是修改了一些参数信息,却并没有再次调用绘制函数,然而物体依旧被重绘了。这是因为在设计时,我们采用的并不是修改时重绘,而是空闲时重绘,系统会不断地调用绘制函数,当我们把一些参数改变后,绘制回调函数就会使用新的参数,动画效果就产生了。
当移动因子超过了桌面大小时,我们让其等于桌面边缘坐标。
gluPerspective(45, whRatio, 1,100);
这里选择了45度的视角,根据调节参数这个视角观察到的物体更加令人舒适。whRatio的选取一般来说,要和视窗的宽高比等同。最后两个参数是近点和远点,它们不能为负值,否则可能消失在视野中。根据正投影的-100,100,这里选取了一个相近的参数。
glOrtho(-3 ,3, -3, 3,-100,100);
这里x,y的坐标范围较小,因为物体的实际大小也不会很大,而在z轴上,大小的选取不会影响到投影效果,所以我们可以选得更大一些。
//WASDZC 控制相机上下左右前后移动
//P 切换投影方式(正投影与透视投影)
//O 切换渲染方式(填充模式与线框模式)
//空格键 启动与暂停旋转(桌子与茶壶一起绕桌子中心轴旋转)
//IKJL 控制茶壶前后左右移动。
//E 茶壶旋转
//Q 退出
#include
#include "glut.h"
float fRotate = 0.0f; //旋转因子(茶壶和桌子)
float fScale = 1.0f; //缩放因子
float tRotate = 0.0f; //旋转因子(茶壶)
bool bPersp = false; //是否为透视投影 (vs 正投影)
bool bAnim = false; // 茶壶和桌子是否旋转
bool bWire = false; // 绘制模式是否为线形 (vs 填充)
bool isRotate = false; //茶壶是否旋转
int wHeight = 0;
int wWidth = 0;
int min(int x, int y)
{
return x < y ? x : y;
}
//绘制腿部
void Draw_Leg()
{
glScalef(1, 1, 3);
glutSolidCube(1.0);
}
void Draw_Scene(float place[])
{
//画茶壶
glPushMatrix();
glTranslatef(place[0], place[1], place[2]);//控制平移
glRotatef(90, 1, 0, 0);
glRotatef(tRotate, 0, 1, 0);//控制旋转
glutSolidTeapot(1);
glPopMatrix();
//画桌面
glPushMatrix();
glTranslatef(0, 0, 3.5);
glScalef(5, 4, 1);
glutSolidCube(1.0);
glPopMatrix();
//画四条腿
glPushMatrix();
glTranslatef(1.5, 1, 1.5);
Draw_Leg();
glPopMatrix();
glPushMatrix();
glTranslatef(-1.5, 1, 1.5);
Draw_Leg();
glPopMatrix();
glPushMatrix();
glTranslatef(1.5, -1, 1.5);
Draw_Leg();
glPopMatrix();
glPushMatrix();
glTranslatef(-1.5, -1, 1.5);
Draw_Leg();
glPopMatrix();
}
void updateView(int width, int height)
{
glViewport(0,0,width,height);//设置视窗大小
glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影
glLoadIdentity(); //初始化矩阵为单位矩阵
float whRatio = (GLfloat)width/(GLfloat)height;//设置显示比例
if (bPersp){
gluPerspective(45, whRatio, 1, 100); //透视投影
//参数:
//GLdouble fovy 视角
//GLdouble aspect 宽高比
//GLdouble zNear 近处
//GLdouble zFar 远处
}
else
glOrtho(-3 ,3, -3, 3,-100,100); //正投影
//参数
//GLdouble left,
//GLdouble right,
//GLdouble bottom,
//GLdouble top,
//GLdouble near,
//GLdouble far
glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型
}
void reshape(int width, int height)
{
if (height==0) //如果高度为0
{
height=1; //让高度为1(避免出现分母为0的现象)
}
height = width = min(height, width);
wHeight = height;
wWidth = width;
updateView(wHeight, wWidth); //更新视角
}
void idle()
{
glutPostRedisplay();//调用当前绘制函数
}
float eye[] = {0, 0, 8};
float center[] = {0, 0, 0};
float place[] = {0, 0, 5};
//按键回调函数
void key(unsigned char k, int x, int y)
{
switch(k)
{
case 'q': {exit(0); break; } //退出
case 'p': {bPersp = !bPersp; updateView(wHeight, wWidth);break; } //切换正投影、透视投影
case ' ': {bAnim = !bAnim; break;} //旋转模式的切换
case 'o': {bWire = !bWire; break;} //渲染方式的切换
//整体操作
case 'a': { //向左移动
center[0] += 0.1f;
eye[0] += 0.1f;
break;
}
case 'd': { //向右移动
center[0] -= 0.1f;
eye[0] -= 0.1f;
break;
}
case 'w': { //向上移动
center[1] -= 0.1f;
eye[1] -= 0.1f;
break;
}
case 's': { //向下移动
center[1] += 0.1f;
eye[1] += 0.1f;
break;
}
case 'z': { //向前移动
center[2] -= 0.1f;
eye[2] -= 0.1f;
break;
}
case 'c': { //向后移动
center[2] += 0.1f;
eye[2] += 0.1f;
break;
}
//茶壶相关操作
case 'l': { //右移茶壶
place[0] += 0.1f;
if (place[0] > 1.5f)place[0] = 1.5f; //不超出桌面范围
break;
}
case 'j': { //左移茶壶
place[0] -= 0.1f;
if (place[0] < -1.5f)place[0] = -1.5f;
break;
}
case 'i': { //后移茶壶
place[1] += 0.1f;
if (place[1] > 1.5f)place[1] = 1.5f;
break;
}
case 'k': { //前移茶壶
place[1] -= 0.1f;
if (place[1] < -1.5f)place[1] = -1.5f;
break;
}
case 'e': { //旋转茶壶
isRotate = !isRotate;
break;
}
}
}
void redraw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色缓存和深度缓存
glLoadIdentity(); //初始化矩阵为单位矩阵
gluLookAt(eye[0], eye[1], eye[2],
center[0], center[1], center[2],
0, 1, 0);
// 观察位置(eye[0],eye[1],eye[2]
// 物体位置(center[0],center[1],center[2]
// 观察方向(x, y, z)
if (bWire) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //设置多边形绘制模式:正反面,线型
}
else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //设置多边形绘制模式:正反面,填充
}
glEnable(GL_DEPTH_TEST); //开启深度测试
glEnable(GL_LIGHTING); //开启光照模式
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 }; // 定义颜色
GLfloat light_pos[] = {5,5,5,1}; //定义光源位置
glLightfv(GL_LIGHT0, GL_POSITION, light_pos); //设置第0号光源的光照位置
glLightfv(GL_LIGHT0, GL_AMBIENT, white); //设置第0号光源多次反射后的光照颜色(环境光颜色)
glEnable(GL_LIGHT0); //开启第0号光源
glRotatef(fRotate, 0, 1.0f, 0); //旋转
glRotatef(-90, 1, 0, 0); //旋转
glScalef(0.2, 0.2, 0.2); //缩放
Draw_Scene(place); //场景绘制
if (bAnim) fRotate += 0.5f; //茶壶桌子
if (isRotate) tRotate += 0.5f; //茶壶旋转
glutSwapBuffers(); //交换缓冲区
}
int main (int argc, char *argv[])
{
glutInit(&argc, argv);//对glut的初始化
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);//初始化显示模式:RGB颜色模型,深度测试,双缓冲
glutInitWindowSize(480,480);//设置窗口大小
int windowHandle = glutCreateWindow("Ex 3");//设置窗口标题
glutDisplayFunc(redraw);//注册绘制回调函数
glutReshapeFunc(reshape);//注册重绘回调函数
glutKeyboardFunc(key);//注册按键回调函数
glutIdleFunc(idle);//注册全局回调函数:空闲时调用
glutMainLoop();// glut事件处理循环
return 0;
}