实验九3D编程基础
一、实验目的
1.熟悉3D基本编程。
2.熟悉视点观察函数的设置和使用。
3.熟悉投影变换函数的设置和使用。
4.熟悉基本3D图元的绘制。
二、实验内容
1.读懂以下3D物体程序,并结合本书内容理解一些新的绘制函数和投影变换函数的含 义:3D Cube.cpp (见后面参考程序)为正交投影下的旋转3D立方体,按下鼠标可实现不同 方向的旋转,效果图参见实验图9-1,分析3D编程代码与程序结构。
对于以下操作需要记录不同效果图和修改的相应参数:
1)让静止的立方体绕Z轴不停旋转。
gluLookAt(0, 0, 3, 0, 0, 0, 0, 1, 0);//视点设置函数
2)修改不同视点,目标点不变,观看显示效果。
gluLookAt(3, 0, 0, 0, 0, 0, 0, 1, 0);//视点设置函数
3)修改目标点,视点不动,观看显示效果。
gluLookAt(3, 3, 3, 0, 3, 0, 0, 1, 0);//视点设置函数
4)视点与目标点同时修改,观看显示效果。
gluLookAt(5, 0, 0, 3, 0, 0, 0, 1, 0); //视点设置函数
5)视点与目标点不变,修改观察体大小,观看显示效果。
gluPerspective(50, w / h, 10, 60); //定义透视投影投影观察体大小
6)将正交投影观察体改为透视投影观察体,并设置其大小,观察显示效果。
正交投影比透视投影观察体的亮度暗些,细节部分缺失。
7)将立方体替换为茶壶,观看显示效果。
8)将立方体替换为圆环,观看显示效果。
2.构思绘制茶壶和圆环造型程序Teapot Torus.cpp。在紧挨着茶壶下方添加一个平行的圆 环,茶壶和圆环不停绕中心轴旋转,观看显示效果。
3.编写或改写程序,构造自己的3D物体场景造型。
void teapot_torus() {
glPushMatrix();
glTranslatef(0, 1, 0);
glColor3f(1, 0.5, 0);
//glutWireTeapot(1.2);
glutSolidTeapot(1.2);
glPopMatrix();
//绘制茶壶
glColor3f(1, 0.5, 0);
//绘制圆环
glColor3f(1, 0, 0);
glPushMatrix();
glTranslatef(0, 0, 0);
glRotatef(90, 1, 0, 0);
glutWireTorus(0.3, 1.2, 30, 30);
glPopMatrix();
glColor3f(0,0,1);
glPushMatrix();
glTranslatef(0, 0, 0);
glRotatef(90, 1,0, 0);
glutWireTorus(1, 1.01, 30, 30);
glPopMatrix();
}
三、函数参考
1 .视点设置函数:
void gluLookAt(GLdouble eyex, GLdouble eyey,GLdouble eyezz GLdouble atx,GLdouble aty,GLdouble atz,GLdouble upx,GLdouble upy,GLdouble upz)
2.正交投影变换设置函数:
void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far)
3.透视投影变换设置函数:
void gluPerspective(GLdouble fov, GLdouble aspect, GLdouble near,GLdouble far)
4.三维基本图形绘制函数:
1 )立方体绘制函数:
void glutWireCube(GLdouble size) //线框模式
void glutSolidCube(GLdouble size)//实体模式
功能:绘制一个边长为size的线框或实心立方体,立方体的中心位于原点。
2)小球绘制函数:
void glutWireSphere(GLdouble Radius, Glint slices,Glint stacks) ;//线框模式
void glutSolidSphere(GLdouble Radius, Glint slices,Glint stacks);//实体模式
功能:绘制一个半径为Radius的线框或实心小球,小球的中心点位于原点,slices为小 球的经线数目,stacks为小球的纬线数目。
gluSphere(GLUquadricObj *obj,GLdouble radius,GLint slices,GLint stacks);
用法如下:
GLUquadricObj *sphere;//定义二次曲面对象
sphere = gluNewQuadric();//生成二次曲面对象
gluSphere(Sphere,8,50,50);//半径为8,球心在原点,经线和纬线数目为50的小球
3)茶壶绘制函数:
void glutWireTeapot(GLdouble size);//线框模式
void glutSolidTeapot(GLdouble size);//实体模式
功能:绘制一个半径为size的线框或实心茶壶,茶壶的中心位于原点。
参数说明:参数size为茶壶的近似半径,以size为半径的球体可完全包容这个茶壶。
4)圆环绘制函数:
void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius,Glint slices,Glint stacks) ; //线框模式
void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius,Glint slices,Glint stacks) ; //实体模式
功能:绘制一个半径为size的线框或实心圆环体,圆环体的中心位于原点,圆环的内径 和外径由参数innerRadius、outerRadius指定。
参数说明:innerRadius为圆环体的内径;outerRadius为圆环体的外径;slices为圆环体 的经线数目;stacks为圆环体的纬线数目。
5)正八面体绘制函数:
void glutWireOctahedron (void) ; // 线框模式
void glutSolidOctahedron (void); //实体模式
功能:绘制一个线框的或实心的正八面体,其中心位于原点,半径为1。
6)正十二面体绘制函数:
void glutWireDodehedron (void) ; // 线框模式
void glutSolidDodehedron (void); //实体模式
功能:绘制一个线框的或实心的正十二面体,其中心位于原点,-半径为3的平方根。
7)正二十面体绘制函数:
void glutWirelcosahedron (void); //线框模式
void glutSolidlcosahedron (void); //实体模式
功能:绘制一个线框的或实心的正二十面体,其中心位于原点,半径为1。
8)正四面体绘制函数:
void glutWireTetrahedron (void); //线框模式
void glutSolidTetrahedron (void); //实体模式
功能:绘制一个线框的或实心的正四面体,其中心位于原点,半径为3的平方根。
9)裁剪平面函数:
void glClipPlane(GLenum plane, const GLdouble *equation)
定义一个附加的裁减平面。plane指岀要定义的附加裁减平面名称,取值为GL_CLIP_ PLANEi,i=0 ~ 5 ; equation指向由平面方程Ax+By+Cz+D=0的4个系数刀、B、C、O构成的 数组,以定义一个裁减平面。调用此函数,先启用glEnable(GL_CLIP_PLANEi),需要时用 glDisable(GL_CLIP_PLANEi)关闭某裁减平面。
5.图形变换函数:
1) glTranslatef(xz y,z)
2) gLRotatef(Q,x, y, z)
3) glScalef(x,y,z)
6.void glPushMatrix (void)函数
7.void glPopMatrix (void)函数
8.void glutIdleFunc( (*f) (void)) 〃注册闲置响应函数
9.void myidle () //闲置响应回调函数
{
//当时间空闲时系统要做的事情
}
四、思考题
1.修改目标点与视点,显示结果有何不同?
修改目标点会使物体在程序框的相对位置改变,修改视点观察角度不同。
2.视点与目标点不变,修改观察体大小,显示结果有什么规律?
视场角变大时,物体反而变小。
3.正交投影与透视投影有何不同?
在正交投影中,图形沿平行线变换到投影面上;对透视投影,图形沿收敛于某一点的直线变换到投影面上,此点称为投影中心,相当于观察点,也称为视点。
正交投影和透视投影区别在于透视投影的投影中心到投影面之间的距离是有限的,而正交投影的投影中心到投影面之间的距离是无限的。
当投影中心在无限远时,投影线互相平行,所以定义正交投影时,给出投影线的方向就可以了,而定义透视投影时,需要指定投影中心的具体位置
正交投影保持物体的有关比例不变,这是三维绘图中产生比例图画的方法。物体的各个面的精确视图可以由平行投影得到。
另一方面,透视投影不保持相关比例,但能够生成真实感视图。对同样大小的物体,离投影面较远的物体比离投影面较近物体的投影图象要小,产生近大远小的效果
五、课后加分题
如何编写程序从不同角度观看物体模型?如按“A”键,视点越来越远;按“S”键视点 越来越近?如何切换俯视、侧视或正视?
将视点设为变量,按相应的键进行改变。切换俯视、侧视或正视,设置键位使视点切换至相应的方向。
六、程序代码
#define GLUT_DISABLE_ATEXIT_HACK
//3D Cube.cpp,旋转立方体参考程序
#include <windows.h>
#include <gl/glut.h>
#include <math.h>
int flag = 0;
//GLfloat vertices[][3] = {{-1.0,-1.0,1.0},{-1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,-1.0,1.0}, {-1.0,-1.0,-1.0},
//{-1.0,1.0,-1.0},{1.0,1.0,-1.0},{1.0,-1.0,-1.0}};
GLfloat vertices[][3] = { { -1.0,-1.0,-1.0},{1.0,-1.0,-1.0},{1.0,1.0,-1.0 }, { -1.0,1.0, -1.0 }, {-1.0, -1.0, 1.0},
{1.0, -1.0, 1.0},{ 1.0,1.0,1.0 }, { -1.0,1.0,1.0 }};
GLfloat colors[][3] = { {1.0,0.0,0.0}, {0.0,1.0,1.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0, 0.0,1.0},
{1.0,0.0,1.0 }, {0.0, 1.0, 1.0}, {1.0, 1.0, 1.0}};
static GLfloat theta[] = { 0.0,0.0,0.0 };
static GLint axis = 2;
void reshape(int w,int h);
void init();
void display();
void mouse(int btn, int state,int x, int y);
void polygon(int a,int b,int c,int d);
void colorcube(void);
void spinCube();
int main(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
char* argv[] = { (char*)"hello ",(char*)" " };
int argc = 2; //argv中的字符串数
glutInit(&argc, argv); //初始化GLUT库
int sheight = glutGet(GLUT_SCREEN_WIDTH);
int swidth = glutGet(GLUT_SCREEN_HEIGHT);
glutInitWindowSize(800, 800); //设置显示窗口大小
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //设置显示模式(注意双缓存 )
glutInitWindowPosition(sheight / 2 - 400, swidth / 2 - 400); //窗口左上角在屏幕的位置
glutCreateWindow("colorcube"); //创建显示窗口
init(); //初始化设置
glutReshapeFunc(reshape); //注册窗口改变回调函数
glutDisplayFunc(display); //注册显示回调函数
glutIdleFunc(spinCube);
glutMouseFunc(mouse);
glutMainLoop(); //进入事件处理循环
return 0;
}
void display() {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0, 0, 4, 0, 0, 0, 0, 1, 0);
glRotatef(theta[0], 1.0, 0.0, 0.0);
glRotatef(theta[1], 0.0, 1.0, 0.0);
glRotatef(theta[2], 0.0, 0.0, 1.0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
flag = 0;
colorcube(); //绘制彩色立方体
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
flag = 1;
colorcube(); //绘制彩色立方体
glutSwapBuffers();
}
void init() {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_DEPTH_TEST);
glLineWidth(3);
}
void polygon(int a,int b,int c,int d){
/* draw a polygon via list of vertices */
if (flag == 0){
glBegin(GL_POLYGON);
glColor3fv(colors[a]);
glVertex3fv(vertices[a]);
glColor3fv(colors[b]);
glVertex3fv(vertices[b]);
glColor3fv(colors[c]);
glVertex3fv(vertices[c]);
glColor3fv(colors[d]);
glVertex3fv(vertices[d]);
glEnd();
}
else{
glColor3f(0, 0, 0);
glBegin(GL_POLYGON);
glVertex3fv(vertices[a]);
glVertex3fv(vertices[b]);
glVertex3fv(vertices[c]);
glVertex3fv(vertices[d]);
glEnd();
}
}
void colorcube(void) {
/* map vertices to faces */
polygon(0, 3, 2, 1);
polygon(2, 3, 7, 6);
polygon(0, 4, 7, 3);
polygon(1, 2, 6, 5);
polygon(4, 5, 6, 7);
polygon(0, 1, 5, 4);
}
void mouse(int btn, int state,int x, int y){
if (btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN) axis=0;
if (btn==GLUT_MIDDLE_BUTTON && state==GLUT_DOWN) axis=1;
if (btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN) axis=2;
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//定义正交投影观察体
if (w <= h)
glOrtho(-2.0, 2.0, -2.0 * (GLfloat)h / (GLfloat)w, 2.0 * (GLfloat)h / (GLfloat)w, 1.0, 20.0);
else
glOrtho(-2.0 * (GLfloat)w / (GLfloat)h, 2.0 * (GLfloat)w / (GLfloat)h, -2.0, 2.0, 1.0, 20.0); //glu Perspective (120,w/hzl,60); / / 定义透视投影观察体
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void spinCube(){
theta[axis] += 1.0;
if (theta[axis] > 360.0) theta[axis] -= 360.0; glutPostRedisplay();
}