您好,欢迎访问姐妹文库,我们提供范文大全,个人简历,教案下载,课件中心,优秀作文,考试辅导,试题库,诗词鉴赏等

收藏

IT计算机 频道

互联网|电脑基础知识|软件及应用|硬件及网络|

计算机图形学实验报告2

字体大小:[日期:2017-11-23]阅读:

导读:计算机图形学实验报告
实验二、 三维网格模型光顺

一、实验目的与基本要求: (1)掌握 Obj 文件的读入; (2)利用给定的数据结构类,建立读入网格模型数据结构; (3)利用 OpenGL 类库,对

计算机图形学实验报告 实验二、 三维网格模型光顺 一、实验目的与基本要求: (1)掌握 Obj 文件的读入; (2)利用给定的数据结构类,建立读入网格模型数据结构; (3)利用 OpenGL 类库,对三维模型进行绘制; (4)利用 OpenGL 类库,增加采用鼠标交互方式对三维模型进行旋转、放 缩、平移等操作; (5)实现 Laplacian 方法的三维模型光顺操作,并观察三维模型光顺过程; 二、实验设备(环境)及要求 1. 操作系统:Windows XP 或 Windows 7 2. 编程环境:Microsoft Visual Studio 2010,OpenGL 库函数 3. 界面框架:Win32,MFC,QT选择其中一种 三、实验内容与步骤 实验分为以下几个步骤: (1)掌握 Obj 文件的读入顶点和面的个数; (2)建立数组存储点的坐标及面上的点数; (3)存储顶点的邻接面数,并记录每个顶点周围的邻接点 (4)计算每个面的法向 利用 OpenGL 类库,增加采用鼠标交互方式对三维模型进行旋转、放缩、平移等操作; (5)利用面法向及顶点坐标进行绘制几何体 (6)实现鼠标对物体旋转、平移、缩放的算法 (7)实现 Laplacian 方法的三维模型光顺操作,并观察三维模型光顺过程; 四、实现过程说明及成果展示: (1)掌握 Obj 文件的读入顶点和面的个数; 由于 obj 文件的存储形式是 v x1 x2 x3; … f v1 v2 v3; … 这种形式,所以在记录点和面的数量时,只需按行读取,然后再判断首字母是 v/f 即可 实现代码如下: (2)建立数组存储点的坐标及面上的点数; 数组的大小由点数和面数决定, 点数组和面数组均由 0 开始记录, 故后面再用面对应点的时 候,由于面上点是从 1 开始记录,故需要减 1 然后使用,代码如下: (3)存储顶点的邻接面数,并记录每个顶点周围的邻接点 记录点邻接面的是新建一个数组,在读面的时候,将该面的序号存入对应点的数组中,然后 再在每个面上取一点, 记录到点的邻接点数组中, 在每个面上取得的点为向外右手方向的下 一个点,实现代码如下: (4)计算每个面的法向 计算面的法向方式为面上右手方向上的两向量的叉乘得到,即 所用代码为: (8)利用面法向及顶点坐标进行绘制几何体 用法向绘制的方式是 先用 glNormal3fv(v)指出面的法向;再用 glVertex3f 传入面上点的坐标; 由于我将 glNormal3fv(v) 中写在算法向所以我直接对此直接调用即可,代码如下: (9)实现鼠标对物体旋转、平移、缩放的算法 平移:利用 Transform 函数和键盘事件来改变参数,w,s,a,d 分别控制绘制的 kitty 猫的上下左 右的移动:实现代码如下: 旋转:利用 gllookat();函数设定了观察角度,并用鼠标事件改变参数,用实现观察视角的变 化实现物体的旋转,代码如下: 缩放:运用 glScalef 方法和键盘事件改变参数,实现物体的放大和缩小,代码如下: (10)实现 Laplacian 方法的三维模型光顺操作,并观察三维模型光顺过程; Laplacian 方法的原理是利用目标点与其所有邻接点平均后的点的差向量, 对目标点的坐标进 行变换的过程,具体方法是: ①建立每个点的邻接顶点数组,存储每个点的邻接点 ②对每个顶点的邻接点进行求平均, 即将邻接点的坐标求和后除以邻接点个数, 从而得到邻 接平均点 ③得到优化向量 优化向量 = 邻接平均点 - 目标点 ④设定优化度参数λ ,得到优化后的新坐标 新坐标 = 目标点 + λ *优化向量 在程序中,对于第 num 个顶点,我设定的变量为 目标点 vArr 邻接平均点 v0 优化向量 l 新坐标数组 vNewArr 具体代码如下: 五、结果展示及说明 计算面法向后直接绘制(未光顺) : 光顺进行一次后 光顺多次后 利用点绘制结果为 旋转后 缩放后 平移后 六、心得体会 (1)计算面法向时法向量的方向没有运用右手方向,导致有的面法向向里,从而 出现雪花 (2)在运用 Laplacian 算法进行求邻接平均点时未初始化邻接平均点数组, 导致平均点的累加 从而出现越光顺越粗糙的现象 (3)在 obj 文件中面对应顶点数和顶点数组的标号相差 1.在运用的时候需减 1,面 从 1 开始记顶点,顶点数组从 0 开始记顶点 七、实验代码 #defineGLUT_DISABLE_ATEXIT_HACK #include<stdlib.h> #include<GL/glut.h> #include<stdio.h> #include<iostream> #include<string> #include<fstream> #include<sstream> #include<cmath> #include<vector> usingnamespace std; int v_num = 0; //记录点的数量 int f_num = 0; //记录面的数量 int vn_num = 0;//记录法向量的数量 int vt_num = 0; GLfloat **vArr; //存放点的二维数组 GLfloat **vNewArr;//存放点的二维数组 int **fvArr; //存放面顶点的二维数组 GLfloat **fnArr;//存放面法向量的二维数组 int **ftArr; int **vfArr;//存放顶点临接的面数的数组 int **nextVArr;//存放下一个临界顶点的数组 GLfloat **vnArr;//存放点法向量的二维数组 string s1, s2, s3, s4; GLfloat f2, f3, f4; int num1, num2, num3; typedefGLfloatpoint3[3]; point3 x, y,l;//平面上的两个向量 x,y 和拉普拉斯向量 l staticGLfloat theta[] = { 0.0,0.0,0.0 }; staticGLint axis = 2; staticGLdouble viewer[] = { 0.0, 0.0, 5.0 }; /* initial viewer location */ staticGLdouble Tran[] = { 0.0,0.0,0.0 }; staticGLdouble sca = 1.0; void getLineNum(stringaddrstr) //获取点和面的数量 { ifstream infile(addrstr.c_str()); //打开指定文件 if (!infile) { cout <<"open error!"<< endl; exit(1); } string sline;//每一行 int i = 0, j = 0; while (getline(infile, sline)) //从指定文件逐行读取 { if (sline[0] == 'v') { v_num++; } if (sline[0] == 'f') { f_num++; } } } int readfile(stringaddrstr) //将文件内容读到数组中去 { //getLineNum(addrstr); //new 二维数组 vArr = newGLfloat*[v_num]; for (int i = 0; i < v_num; i++) { vArr[i] = newGLfloat[3]; } vNewArr = newGLfloat*[v_num]; for (int i = 0; i < v_num; i++) { vNewArr[i] = newGLfloat[3]; } vnArr = newGLfloat*[vn_num]; for (int i = 0; i < vn_num; i++) { vnArr[i] = newGLfloat[3]; } vfArr = newint*[v_num]; for (int i = 0; i < v_num; i++) { vfArr[i] = newint[10]; for (int j = 0; j < 10; j++) { vfArr[i][j] = -1; } } nextVArr = newint*[v_num]; for (int i = 0; i < v_num; i++) { nextVArr[i] = newint[10]; for (int j = 0; j < 10; j++) { nextVArr[i][j] = -1; } } fvArr = newint*[f_num]; fnArr = newGLfloat*[f_num]; ftArr = newint*[f_num]; for (int i = 0; i < f_num; i++) { fvArr[i] = newint[3]; fnArr[i] = newGLfloat[3]; ftArr[i] = newint[10]; } ifstream infile(addrstr.c_str()); if (!infile) { cout <<"open error!"<< endl; exit(1); } string sline;//每一行 int ii = 0, jj = 0, kk = 0, mm = 0; while (getline(infile, sline)) { if (sline[0] == 'v')//存储点 { istringstream sin(sline); sin >> s1 >> f2 >> f3 >> f4; vArr[ii][0] = f2; vArr[ii][1] = f3; vArr[ii][2] = f4; ii++; } if (sline[0] == 'f') //存储面 { istringstream in(sline); GLfloat a; in >> s1>>num1>>num2>>num3;//去掉 f fvArr[kk][0] = num1; fvArr[kk][1] = num2; fvArr[kk][2] = num3; for (int i = 0; i < 10; i++) { if (vfArr[num1-1][i] == -1) { vfArr[num1-1][i] = kk; nextVArr[num1-1][i] = num2; break; } } for (int j = 0; j < 10; j++) { if (vfArr[num2-1][j] == -1) { vfArr[num2-1][j] = kk; nextVArr[num2-1][j] = num3; break; } } for (int i = 0; i < 10; i++) { if (vfArr[num3-1][i] == -1) { vfArr[num3-1][i] = kk; nextVArr[num3-1][i] = num1; break; } } kk++; } } return 0; } //计算面法向 void nomal(point3p) { /*矢量的归一化*/ //double sqrt(); float d = 0.0; int i; for (i = 0; i < 3; i++) d += p[i] * p[i]; d = sqrt(d); if (d > 0.0) for (i = 0; i < 3; i++) p[i] /= d; } void getFaceNormal(point3A, point3B, point3C) { x[0] = C[0] - A[0]; x[1] = C[1] - A[1]; x[2] = C[2] - A[2]; y[0] = A[0] - B[0]; y[1] = A[1] - B[1]; y[2] = A[2] - B[2]; point3 v; v[0] = x[1] * y[2] - x[2] * y[1]; v[1] = x[2] * y[0] - x[0] * y[2]; v[2] = x[0] * y[1] - x[1] * y[0]; nomal(v); glNormal3fv(v); } void Laplacian() { for (int num = 0; num < v_num; num++) { int m = 0; //求得点的邻接面数 for (int i = 0; i < 10; i++) { if (nextVArr[num][i] != -1) { m++; } else { break; } } point3 v0; for (int i = 0; i < 3; i++) { v0[i] = 0; } for (int i = 0; i < m; i++) { v0[0] += vArr[nextVArr[num][i] - 1][0]; v0[1] += vArr[nextVArr[num][i] - 1][1]; v0[2] += vArr[nextVArr[num][i] - 1][2]; } if (m != 0) { for (int i = 0; i < 3; i++) v0[i] /= m; } l[0] = v0[0] - vArr[num][0]; l[1] = v0[1] - vArr[num][1]; l[2] = v0[2] - vArr[num][2]; vNewArr[num][0] = vArr[num][0] + l[0] * 0.3; vNewArr[num][1] = vArr[num][1] + l[1] * 0.3; vNewArr[num][2] = vArr[num][2] + l[2] * 0.3; } for (int i = 0; i < v_num; i++) { vArr[i][0] = vNewArr[i][0]; vArr[i][1] = vNewArr[i][1]; vArr[i][2] = vNewArr[i][2]; } } void init(void) { getLineNum("H:\\kitten_noisy.obj"); readfile("H:\\kitten_noisy.obj"); double viewer[] = { 0.0, 0.0, 8.0 }; /* initial viewer location */ GLfloat mat_specular[] = { 1.0, 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 lmodel_ambient[] = { 0.1, 0.1, 0.1, 1.0 }; glClearColor(0.0, 0.0, 0.0, 0.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, lmodel_ambient);//环境光源 // glEnable(glMaterialf); glEnable(GL_LIGHTING);//启动光照 glEnable(GL_LIGHT0);//启用 0 度光源 glEnable(GL_DEPTH_TEST);//启动深度测试 /* glShadeModel(GL_SMOOTH); Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); 黑色背景 glClearDepth(1.0f); // 深度缓冲区设置 glEnable(GL_DEPTH_TEST); 允许深度测试 glDepthFunc(GL_LEQUAL); // 定义深度测试类型 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); Really Nice Perspective Calculation */ } // // // // void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(viewer[0], viewer[1], viewer[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.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); glTranslatef(Tran[0], Tran[1], Tran[2]); glScalef(sca, sca, sca); //glPolygonMode(GL_FRONT_AND_BACK, GLU_FILL); glBegin(GL_TRIANGLES); //getFaceNormals(); for (int i = 0; i<f_num; i++) { getFaceNormal(vArr[fvArr[i][0] - 1], vArr[fvArr[i][1] - 1], vArr[fvArr[i][2] - 1]); glVertex3f(vArr[fvArr[i][0] - 1][0], vArr[fvArr[i][0] - 1][1], vArr[fvArr[i][0] - 1][2]); glVertex3f(vArr[fvArr[i][1] - 1][0], vArr[fvArr[i][1] - 1][1], vArr[fvArr[i][1] - 1][2]); glVertex3f(vArr[fvArr[i][2] - 1][0], vArr[fvArr[i][2] - 1][1], vArr[fvArr[i][2] - 1][2]); } glEnd(); /* glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glBegin(GL_POINTS); glColor3f(1.0, 1.0, 1.0); for (int i = 0; i < v_num; i++) { glVertex3fv(vArr[i]); glNormal3fv(fnArr[i]); } glEnd(); */ glFlush();//强制绘图 glutSwapBuffers(); } void reshape(intw, inth) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); //视口设置 glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w<= h)//描绘了两种不同情况下的平行修剪空间 glOrtho(-0.6, 0.6, -0.6 * (GLfloat)h / (GLfloat)w, 0.6 * (GLfloat)h / (GLfloat)w, -10.0, 10.0); else glOrtho(-0.6*(GLfloat)w / (GLfloat)h, 0.6*(GLfloat)w / (GLfloat)h, -0.6, 0.6, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); } #defineGLUT_WHEEL_UP 3 #defineGLUT_WHEEL_DOWN 4 void mouse(intbtn, intstate, intx, inty) { if (btn == GLUT_LEFT_BUTTON&&state == GLUT_DOWN) { Laplacian(); } if (btn == GLUT_MIDDLE_BUTTON&&state == GLUT_DOWN) axis = 0; if (btn == GLUT_RIGHT_BUTTON&&state == GLUT_DOWN) axis = 1; theta[axis] += 2.0; if (theta[axis] > 360.0) theta[axis] -= 360.0; display(); } void keys(unsignedcharkey, intx, inty) { if (key == 'a') Tran[0] -= 0.2; if (key == 'd') Tran[0] += 0.2; //定义滚轮操作 if (key == 's') Tran[1] -= 0.2; if (key == 'w') Tran[1] += 0.2; if (key == 'q') sca -= 0.1; if (key == 'e') sca += 0.1; display(); } int main(intargc, char **argv) { glutInit(&argc, argv);//对 GLUT 进行初始化 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);//设置显示方式,单缓冲、RGB 颜色 glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); //glutCreateWindow(argv[0]); glutCreateWindow("Test"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutKeyboardFunc(keys); glEnable(GL_DEPTH_TEST); glutMainLoop(); return 0; }