本周做了很多的实验,其中关于OpenGL的实验还是比较有意思的,最近也是很忙,没时间细细品味了,实验报告贴这里供大家参考(实验要求和目的以及内容都是我原创的哦~~哈哈!)

实验一:实现折线和矩形的橡皮筋

一、实验目的与要求

使用橡皮筋技术实现绘制折线和矩形。橡皮筋技术的关键在于控制图形随着用户的操作(鼠标移动)而不断发生变化,此时要擦除原有的图形同时生成新的图形。橡皮筋技术有两种实现方法:一种是利用颜色的异或操作,对原有图形并不是擦除,而是再绘制一条同样的直线段并与原图形进行异或操作,此时原图形会从屏幕上消失;另一种是是利用双缓存技术,绘制图形时分别绘制到两个缓存,交替显示。

二、实验内容

对于同时实现绘制折线和矩形两个功能,我设计了右键鼠标显示一个简单的菜单,用于选择两种功能。使用双缓存技术交替显示两个缓存的内容。同时设计了鼠标相应函数。

折线的处理是鼠标点击左键时画下第一个点,然后根据鼠标的移动绘制橡皮筋线,直到鼠标点击下左键,就绘制第二个点,然后继续跟踪鼠标的轨迹。当点击鼠标右键时停止对这条折线的绘制。

矩形的处理是鼠标点击左键时确定矩形的左上角,然后跟踪鼠标的轨迹,绘制出橡皮筋矩形,直到鼠标点击下左键,结束对这个矩形的绘制,等待鼠标绘制下一个矩形。

三、实验结果

绘制折线效果图:

 

绘制矩形效果图:

 

 

 

四、体会

通过这次OpenGL橡皮筋技术的实现过程,我学习很多OpenGL编程相关的知识,比如如何在windows平台下配置OpenGL库进行编程,以及如何设置编译器路径来引用OpenGL的库。同时我了解了OpenGL在世界上的相关技术支持网站和版本发行工作,以及这个开放图形库的最新动态。又看了一遍它产生的历史以及专家对它未来发展的展望。不过最重要的是我练习了用OpenGL编写一个简单的图形学程序,以前的编程大部分是针对文本输出进行的,虽然也写过基于TurboC的图形输出界面,也做过调用win32的SDK设计windows下的程序,不过都不能算上真正的图形学编程,最多只能算作界面的编程,可以说这是第一次图形编程,在重复了很多次后终于对于图形的处理了解更加清晰。对OpenGL的编程框架也有了更深的了解。而且这次实验中我设计的同时处理两个流程完成不同的功能代码也非常好的通过设计鼠标右击操作和菜单完成了。非常感谢老师给我这次锻炼的机会,对于以后的程序我更加有信心做好了。

五、源程序

 
  
  1. #include <gl/glut.h>  
  2.  
  3. #define NUM 100 //折线的最大折线段  
  4.  
  5. int Flag = 0; //标记是否已经开始绘制折线  
  6. int RFlag = 0; //标记是否已经完成一个矩形  
  7. int Function = 1; //标记选择的功能是画折线还是矩形  
  8. int winWidth = 800, winHeight = 600; //窗口的宽度和高度  
  9. int Mousex, Mousey; //用于记录当前鼠标的位置  
  10. int n = 0; //用于记录折线有几段  
  11. int m = 0; //用于记录矩形个数  
  12. //线性结构体  
  13. struct LineNode {  
  14.     int x1;  
  15.     int y1;  
  16.     int x2;  
  17.     int y2;  
  18. }Line[NUM];  
  19. //矩形结构体  
  20. struct Rectangle {  
  21.     int x1;  
  22.     int y1;  
  23.     int x2;  
  24.     int y2;  
  25. }Rect[NUM];  
  26.  
  27.  
  28. void Initial(void)  
  29. {  
  30.     glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //设置窗口背景颜色  
  31. }  
  32.  
  33. void ChangeSize(int w, int h)  
  34. {  
  35.     //保存当前窗口的大小  
  36.     winWidth = w;  
  37.     winHeight = h;  
  38.     glViewport(0, 0, w, h); //指定窗口显示区域  
  39.     glMatrixMode(GL_PROJECTION); //指定设置投影参数  
  40.     glLoadIdentity(); //调用单位矩阵,去掉以前的投影参数设置  
  41.     gluOrtho2D(0.0, winWidth, 0.0, winHeight); //设置投影参数  
  42. }  
  43.  
  44. void ProcessMenu(int value)  
  45. {  
  46.     Function = value; //接收选择  
  47.     n = 0;  
  48.     Flag = 0;  
  49.     m = 0;  
  50.     RFlag = 0;  
  51.     glutPostRedisplay();  
  52. }  
  53.  
  54. void Display()  
  55. {  
  56.     int i, j;  
  57.     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //线性模式画图  
  58.     glClear(GL_COLOR_BUFFER_BIT); //用当前背景色填充窗口  
  59.     glColor3f(1.0f, 0.0f, 0.0f); //指定当前的绘图颜色  
  60.     if (Function == 1) {  
  61.         for (i = 0; i < n; i++) {  
  62.             glBegin(GL_LINES); //绘制直线段  
  63.                 glVertex2i(Line[i].x1, Line[i].y1);  
  64.                 glVertex2i(Line[i].x2, Line[i].y2);  
  65.             glEnd();  
  66.         }  
  67.         //动态绘制鼠标动作  
  68.         if (Flag == 1) {  
  69.             glBegin(GL_LINES);  
  70.                 glVertex2i(Line[i].x1, Line[i].y1);  
  71.                 glVertex2i(Mousex, Mousey);  
  72.             glEnd();  
  73.         }  
  74.     }  
  75.     else {  
  76.         //绘制矩形  
  77.         for (j = 0; j < m; j++) {  
  78.             glRecti(Rect[j].x1, Rect[j].y1, Rect[j].x2, Rect[j].y2);  
  79.         }  
  80.         //动态绘制鼠标动作  
  81.         if (RFlag == 1) {  
  82.             glRecti(Rect[j].x1, Rect[j].y1, Mousex, Mousey);  
  83.         }  
  84.     }  
  85.     glutSwapBuffers(); //交换缓冲区  
  86. }  
  87.  
  88. void MousePlot(GLint button, GLint action, GLint xMouse, GLint yMouse)  
  89. {  
  90.     if (Function == 1) {  
  91.         if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN) {  
  92.             if (Flag == 0) {  
  93.                 Flag = 1;  
  94.                 Line[n].x1 = xMouse;  
  95.                 Line[n].y1 = winHeight - yMouse;  
  96.             }  
  97.             else {  
  98.                 Line[n].x2 = xMouse;  
  99.                 Line[n].y2 = winHeight - yMouse;  
  100.                 n++;  
  101.                 //折线的第二点作为下一段线的第一个的点  
  102.                 Line[n].x1 = Line[n-1].x2;  
  103.                 Line[n].y1 = Line[n-1].y2;  
  104.             }  
  105.         }  
  106.     }  
  107.     else {  
  108.         //矩形处理  
  109.         if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN) {  
  110.             if (RFlag == 0) {  
  111.                 RFlag = 1;  
  112.                 Rect[m].x1 = xMouse;  
  113.                 Rect[m].y1 = winHeight - yMouse;  
  114.             }  
  115.             else {  
  116.                 RFlag = 0;  
  117.                 Rect[m].x2 = xMouse;  
  118.                 Rect[m].y2 = winHeight - yMouse;  
  119.                 m++;  
  120.                 glutPostRedisplay();  
  121.             }  
  122.         }  
  123.     }  
  124. }  
  125.  
  126. void PassiveMouseMove(GLint xMouse, GLint yMouse)  
  127. {  
  128.     Mousex = xMouse;  
  129.     Mousey = winHeight - yMouse;  
  130.     glutPostRedisplay();  
  131. }  
  132.  
  133. int main(int argc, char *argv[])  
  134. {  
  135.     glutInit(&argc, argv);  
  136.     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //使用双缓存及RGB模型  
  137.     glutInitWindowSize(400, 300); //指定窗口的尺寸  
  138.     glutInitWindowPosition(100, 100); //指定窗口在屏幕上的位置  
  139.     glutCreateWindow("橡皮筋技术");  
  140.     glutCreateMenu(ProcessMenu);  
  141.     glutAddMenuEntry("画折线", 1);  
  142.     glutAddMenuEntry("画矩形", 2);  
  143.     glutAttachMenu(GLUT_RIGHT_BUTTON);  
  144.     glutDisplayFunc(Display);  
  145.     glutReshapeFunc(ChangeSize); //指定窗口再×××回调函数  
  146.     glutMouseFunc(MousePlot); //指定鼠标响应函数  
  147.     glutPassiveMotionFunc(PassiveMouseMove); //指定鼠标移动响应函数  
  148.     Initial();  
  149.     glutMainLoop(); //启动主GLUT事件处理循环  
  150.     return 0;  

实验二:实现KOCK曲线

一、实验目的与要求

Kock曲线的初始生成元是一条线段,生成规则是将直线段均分为三等分,首尾两段保持不变。中间用两段等长且互成60度角的直线段代替。这样,直线段被分成四段,每段长度都只有原来的1/3,分形维数D = ln4 / ln3 = 1.26186。

要求实现初始图元为直线和等边三角形的Kock曲线。

二、实验内容

因为Kock曲线需要迭代,所以我采取的做法是使用键盘输入控制迭代次数,我使用的是空格键来设置迭代次数,而且我发现计算机处理达到10次迭代时已经非常慢了,所以我设置最大只能迭代10次,再往后迭代就已经出现显示极慢的情况。

对于初始图元为直线的Kock曲线,我的处理是先绘制直线的两个端点,然后根据键盘的空格输入,来计算迭代后的点集,用直线连接这些点。

对于初始图元为等边三角形的Kock曲线,我的处理是先绘制三角形,然后对每条边进行直线的Kock曲线的处理,相比直线的Kock曲线只是计算量更大一些罢了。

三、实验结果

直线的Kock曲线效果图:

 

等边三角形Kock曲线效果图:

 

四、体会

这是第二次的图形学实验,通过上一次的实验我了解了OpenGL编程的基本框架,这次实验我就把重点放在了程序的功能性实现上,Kock曲线的公式书本上已经给出了,我只需要照着书本写就可以了,只是对于实现当中的浮点数处理需要小心一点,因为当出现四舍五入会影响直线端点的计算准确性,同过这次实验,我已经能更加熟练的使用OpenGL库函数进行图形学的程序设计,分形的图形初开起来会觉得实现应该相对复杂,但真正做起来才发现没有想象中那么麻烦,因为计算机正适合做这样不停迭代的计算操作,我只需要利用计算机的这一特性,就可以绘制出很好的图形来。通过这次实验我加深了对图形学绘制流程的了解,并深入了解了Kock曲线绘制的细节,明白了很多以前没有注意的地方。

五、源程序

直线Kock曲线:

 
  
  1. #include <GL/glut.h>  
  2. #include <iostream>  
  3. #include <vector>  
  4. #include <math.h>  
  5.  
  6. #define PI 3.1415926  
  7.  
  8. using namespace std;  
  9.  
  10. struct Point  
  11. {  
  12.     float x;  
  13.     float y;  
  14. };  
  15.  
  16. static vector<Point> count;  
  17. static int m = 0; //迭代次数  
  18. float line ;  
  19.  
  20. void first_state(vector<Point> &count)  //初始情况是只有两个点  
  21. {  
  22.     Point first = {-1.0f,0.0f},end = {1.0f,0.0f};  
  23.     line = sqrt( pow(first.x - end.x,2) + pow(first.y - end.y,2) );  
  24.     count.push_back(first);  
  25.     count.push_back(end);  
  26. }  
  27.  
  28. void Draw_pic(vector<Point> &count)  
  29. {  
  30.     for (vector<Point>::size_type i = 0; i != (count.size()-1) ; i++)  
  31.     {  
  32.         glBegin(GL_LINES);    
  33.             glVertex2f(count[i].x, count[i].y);  
  34.             glVertex2f(count[i+1].x, count[i+1].y);  
  35.         glEnd();  
  36.     }  
  37. }  
  38.  
  39. void Calculate_point(vector<Point> &count)  
  40. {  
  41.     vector<Point> new_count;  
  42.     float pline = line /(float) pow((float)3,(int)m) ;  
  43.     vector<Point>::size_type i;  
  44.     for ( i= 0; i != (count.size() - 1); i++ )  
  45.     {  
  46.         Point p1 , p2 , pmid;  
  47.         p1.x = count[i].x + (count[i+1].x - count[i].x) / 3;  
  48.         p1.y = count[i].y + (count[i+1].y - count[i].y) / 3;  
  49.         p2.x = count[i+1].x - (count[i+1].x - count[i].x) / 3;  
  50.         p2.y = count[i+1].y - (count[i+1].y - count[i].y) / 3;  
  51.         double alpha = 0.0;  
  52.         if (count[i+1].y >= count[i].y)  
  53.         {  
  54.             alpha = atan((double) (count[i+1].y - count[i].y) / (count[i+1].x - count[i].x) );  
  55.             if (count[i+1].x > count[i].x)  
  56.             {     
  57.                 pmid.x = p1.x + pline * cos(alpha + PI / 3);  
  58.                 pmid.y = p1.y + pline * sin(alpha + PI / 3);  
  59.             }  
  60.             else 
  61.             {  
  62.                 pmid.x = p1.x - pline * cos(alpha + PI / 3);  
  63.                 pmid.y = p1.y - pline * sin(alpha + PI / 3);  
  64.             }  
  65.         }  
  66.         else 
  67.         {  
  68.             alpha = atan((double) (count[i].y - count[i+1].y) / (count[i+1].x - count[i].x) );  
  69.             if (count[i+1].x > count[i].x)  
  70.             {  
  71.                 pmid.x = p2.x - pline * cos(alpha + PI / 3);  
  72.                 pmid.y = p2.y + pline * sin(alpha + PI / 3);  
  73.             }  
  74.             else 
  75.             {  
  76.                 pmid.x = p2.x + pline * cos(alpha + PI / 3);  
  77.                 pmid.y = p2.y - pline * sin(alpha + PI / 3);  
  78.             }  
  79.         }  
  80.         new_count.push_back(count[i]);  
  81.         new_count.push_back(p1);  
  82.         new_count.push_back(pmid);  
  83.         new_count.push_back(p2);  
  84.     }  
  85.     new_count.push_back(count[i]);  
  86.     count.clear();  
  87.     count = new_count; //之前要清空  
  88. }  
  89.  
  90. void myDisplay()  
  91. {  
  92.     glClear(GL_COLOR_BUFFER_BIT);  
  93.     switch (m)  
  94.     {  
  95.         case 0:  
  96.         case 1:  
  97.         case 2:  
  98.         case 3:  
  99.         case 4:  
  100.         case 5:  
  101.         case 6:  
  102.         case 7:  
  103.         case 8:  
  104.         case 9:  
  105.         case 10:  
  106.             Draw_pic(count);  
  107.             break;  
  108.         default:  
  109.             break;  
  110.     }  
  111.     glFlush();  
  112. }  
  113.  
  114. void Keyboard(unsigned char key,int x,int y)     
  115. {  
  116.     if (key == ' ')  
  117.     {  
  118.         ++m;  
  119.         Calculate_point(count);  
  120.     }  
  121.     glutPostRedisplay();  
  122. }  
  123.  
  124. int main(int argc, char *argv[])  
  125. {  
  126.     first_state(count);  
  127.     glutInit(&argc, argv);  
  128.     glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  
  129.     glutInitWindowPosition(100, 100);  
  130.     glutInitWindowSize(400, 400);  
  131.     glutCreateWindow("直线Kock曲线");  
  132.     glutDisplayFunc(&myDisplay);  
  133.     glutKeyboardFunc(Keyboard); //键盘响应回调函数  
  134.     glutMainLoop();  
  135.     return 0;  
  136. }  
  137.  

等边三角形Kock曲线:

 
  
  1. #include <GL/glut.h>  
  2. #include <iostream>  
  3. #include <vector>  
  4. #include <math.h>  
  5.  
  6. #define PI 3.1415926  
  7.  
  8. using namespace std;  
  9.  
  10. struct Point  
  11. {  
  12.     float x;  
  13.     float y;  
  14. };  
  15. static vector<Point> count1,count2,count3;  
  16. const Point first = {-0.5f,0.1f},end = {0.5f,0.1f},second = {0.0f,-0.766f};  
  17. static int m = 0; //迭代次数  
  18. float line ;  
  19.  
  20. void first_state(vector<Point> &count1,vector<Point> &count2,vector<Point> &count3) //初始情况是只有两个点  
  21. {  
  22.     line = sqrt( pow(first.x - end.x,2) + pow(first.y - end.y,2) );  
  23.     count1.push_back(first);  
  24.     count1.push_back(end);  
  25.     count2.push_back(end);  
  26.     count2.push_back(second);  
  27.     count3.push_back(second);  
  28.     count3.push_back(first);  
  29. }  
  30.  
  31. void Draw_pic(vector<Point> &count1)  
  32. {  
  33.  
  34.     for (vector<Point>::size_type i = 0; i != (count1.size()-1) ; i++)  
  35.     {  
  36.         glBegin(GL_LINES);    
  37.         glVertex2f(count1[i].x, count1[i].y);  
  38.         glVertex2f(count1[i+1].x, count1[i+1].y);  
  39.         glEnd();  
  40.     }  
  41.     for (vector<Point>::size_type j = 0; j != (count2.size()-1) ; j++)  
  42.     {  
  43.         glBegin(GL_LINES);    
  44.         glVertex2f(count2[j].x, count2[j].y);  
  45.         glVertex2f(count2[j+1].x, count2[j+1].y);  
  46.         glEnd();  
  47.     }  
  48.     for (vector<Point>::size_type k = 0; k != (count3.size()-1) ; k++)  
  49.     {  
  50.         glBegin(GL_LINES);    
  51.         glVertex2f(count3[k].x, count3[k].y);  
  52.         glVertex2f(count3[k+1].x, count3[k+1].y);  
  53.         glEnd();  
  54.     }  
  55. }  
  56.  
  57. void Calculate_point(vector<Point> &count1)  
  58. {  
  59.     vector<Point> new_count;  
  60.     float pline = line /(float) pow((float)3,(int)m) ;  
  61.     vector<Point>::size_type i;  
  62.     for ( i= 0; i != (count1.size() - 1); i++ )  
  63.     {  
  64.         Point p1 , p2 , pmid;  
  65.         p1.x = count1[i].x + (count1[i+1].x - count1[i].x) / 3;  
  66.         p1.y = count1[i].y + (count1[i+1].y - count1[i].y) / 3;  
  67.         p2.x = count1[i+1].x - (count1[i+1].x - count1[i].x) / 3;  
  68.         p2.y = count1[i+1].y - (count1[i+1].y - count1[i].y) / 3;  
  69.         //mark  count[i+1].x < count[i].x情况下不成立  
  70.         double alpha = 0.0;  
  71.         if (count1[i+1].y >= count1[i].y)  
  72.         {  
  73.             alpha = atan((double) (count1[i+1].y - count1[i].y) / (count1[i+1].x - count1[i].x) );  
  74.             if (count1[i+1].x > count1[i].x)  
  75.             {     
  76.                 pmid.x = p1.x + pline * cos(alpha + PI / 3);  
  77.                 pmid.y = p1.y + pline * sin(alpha + PI / 3);  
  78.             }  
  79.             else 
  80.             {  
  81.                 pmid.x = p1.x - pline * cos(alpha + PI / 3);  
  82.                 pmid.y = p1.y - pline * sin(alpha + PI / 3);  
  83.             }  
  84.         }  
  85.         else 
  86.         {  
  87.             alpha = atan((double) (count1[i].y - count1[i+1].y) / (count1[i+1].x - count1[i].x) );  
  88.             if (count1[i+1].x > count1[i].x)  
  89.             {  
  90.                 pmid.x = p2.x - pline * cos(alpha + PI / 3);  
  91.                 pmid.y = p2.y + pline * sin(alpha + PI / 3);  
  92.             }  
  93.             else 
  94.             {  
  95.                 pmid.x = p2.x + pline * cos(alpha + PI / 3);  
  96.                 pmid.y = p2.y - pline * sin(alpha + PI / 3);  
  97.             }  
  98.         }  
  99.         new_count.push_back(count1[i]);  
  100.         new_count.push_back(p1);  
  101.         new_count.push_back(pmid);  
  102.         new_count.push_back(p2);  
  103.     }  
  104.     new_count.push_back(count1[i]);  
  105.     count1.clear();  
  106.     count1 = new_count; //之前要清空  
  107. }  
  108.  
  109. void myDisplay()  
  110. {  
  111.     glClear(GL_COLOR_BUFFER_BIT);  
  112.     switch (m)  
  113.     {  
  114.         case 0:  
  115.         case 1:  
  116.         case 2:  
  117.         case 3:  
  118.         case 4:  
  119.         case 5:  
  120.         case 6:  
  121.         case 7:  
  122.         case 8:  
  123.         case 9:  
  124.         case 10:  
  125.             Draw_pic(count1);  
  126.             Draw_pic(count2);  
  127.             Draw_pic(count3);  
  128.             break;  
  129.         default:  
  130.             break;  
  131.     }  
  132.     glFlush();  
  133. }  
  134.  
  135. void Keyboard(unsigned char key,int x,int y)     
  136. {  
  137.     if (key == ' ')  
  138.     {  
  139.         ++m;  
  140.         Calculate_point(count1);  
  141.         Calculate_point(count2);  
  142.         Calculate_point(count3);  
  143.     }  
  144.     glutPostRedisplay();  
  145. }  
  146.  
  147. int main(int argc, char *argv[])  
  148. {  
  149.     first_state(count1,count2,count3);  
  150.     glutInit(&argc, argv);  
  151.     glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  
  152.     glutInitWindowPosition(100, 100);  
  153.     glutInitWindowSize(400, 400);  
  154.     glutCreateWindow("三角形Kock曲线");  
  155.     glutDisplayFunc(&myDisplay);  
  156.     glutKeyboardFunc(Keyboard); //键盘响应回调函数  
  157.     glutMainLoop();  
  158.     return 0;  
  159. }  
  160.  

实验三:实现太阳、地球、月球模型

一、实验目的与要求

使用投影和光照模型技术实现太阳、地球、月球的运行模型。要求使用动画和深度测试。对于光照的使用,要求使用环境光、漫反射、镜面反射等技术,对于绘制球体,要求具有表面材质性能。尽量匹配真实的天体运动状态。

二、实验内容

动画的实现是通过不断修改旋转变换的角度参数来实现的。也就是说,需要创建一个循环,在每次调用显示回调函数之前改变角度参数的值,使天体看起来像绕着太阳旋转。为了不断地调用显示回调函数,需要利用GLUT库中的函数:

void TimerFunc(unsigned int msecs, (*func) (int value), int value);

指定一个定时器回调函数,即经过msecs毫秒后由GLUT调用指定的函数,并将value值传递给它。被定时器调用的函数原型为:

void TimerFunction(int value);

但是,这个函数与其他的回调函数不一样,该函数只能调用一次。为了实现连续的动画,必须在定时器函数中再次设置定时器回调函数。

深度测试即当地球或月球运动到太阳的背面时,由于地球和月球是后绘制的,所以无法被太阳挡住,解决办法是启用深度测试。深度测试是一种移除被挡住表面的有效技术,它的过程是:绘制一个像素时,会给它分配一个值(称为z值),这个值表示它与观察者的距离。然后,如果需要在同一位置上绘制另一个像素,将比较新像素和已经保存在该位置的像素的z值。如果新像素的z值小,即它离观察者更近因而在原来那个像素的前面,原来的像素就会被新像素挡住。这一操作在内部由深度缓冲区完成。为了启用深度测试,只要调用函数:

glEnable(GL_DEPTH_TEST);

打开深度测试功能。要使用深度测试,必须在创建窗口时指定其具有深度缓冲区,代码为:

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

并且,为了使深度缓冲区正常完成深度测试功能,在每次渲染场景时,必须使用函数:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

消除深度缓冲区。

实验中我设置了两个点光源,一个是太阳的发光源,还有一个指向太阳,将太阳照亮,在绘制太阳结束后关闭,因为如果不是用第二个光源的话,太阳本身就会很灰暗。对于太阳、地球和月球的绘制,我定义了每个球体的表面材质,光反射率等等信息。绕行角度也精心设计过,尽量符合真实天体运动,比如黄赤交角、黄白交角等等,都是上网查过相关资料才设计的,所以比较好的符合了现实的天体运行。

三、实验结果

天体运行效果图:

 

 

四、体会

 

这次实验是图形学的最后一个实验,也是难度最大的,耗时最久的。这次实验使用了深度测试,动画,光照模型等等技术。将后面9、10章的内容也包括进来了。通过做实验,我把书本上关于深度测试的一些细节体会的更加清晰了,对于光照模型也理解的比较深刻了,尤其对于电光源的使用和理解更加明确,当发现太阳比较昏暗时展开了思考,为什么太阳会昏暗呢?因为没有光照射到它的表面,于是我设置了第二盏点光源用于照射太阳,可如果这盏点光源一直存在势必对后面的地球和月球的光照产生干扰,于是在绘制完太阳后我将这盏电光源关闭了,这样就体现出比较好的光照感。在天体的各种反射系数上也花了很多时间进行调试,直到完成的比较好为止。当然实验中也遇到过很多问题,比如变换矩阵堆栈就是一个很头疼的问题,因为变换矩阵多了,对于什么时候Push,什么时候Pop就要精心考虑,既不能浪费了已经存在的矩阵,也尽量使变换能够重复使用,在这个上面我花了很多时间思考和测试;还有比如观察空间的设置,之前一直是绘制二维图形所以没有特别在意观察空间的定义,这次三维图形就出现了观察空间的定义不合理的问题,我将观察空间的默认方向理解错了,导致之前画的图都只能看到部分。这次实验学到了很多,对于以后做图形学方面的研究很有帮助。

五、源程序

 
  
  1. #include <windows.h>  
  2. #include <gl/gl.h>  
  3. #include <gl/glu.h>  
  4. #include <gl/glut.h>  
  5.  
  6. float fElect1 = 2.0f; //地球绕太阳的旋转角度  
  7. float fElect2 = 24.0f; //月球绕地球的旋转角度  
  8.  
  9. //定义一个灯照射太阳  
  10. GLfloat light1_ambient[] = { 1.0f, 1.0f, 0.0f, 1.0f };  
  11. GLfloat light1_diffuse[] = { 1.0f, 1.0f, 0.0f, 1.0f };  
  12. GLfloat light1_specular[] = { 1.0f, 1.0f, 0.0f, 1.0f };  
  13. GLfloat light1_position[] = { 100.0f, 100.0f, 100.0f, 1.0f };  
  14.  
  15. //定义太阳的光系数  
  16. GLfloat light_ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f };  
  17. GLfloat light_diffuse[] = { 0.8f, 0.8f, 0.4f, 1.0f };  
  18. GLfloat light_specular[] = { 1.0f, 1.0f, 0.0f, 1.0f };  
  19. GLfloat light_position[] = { 0.0f, 0.0f, 0.0f, 1.0f };  
  20.  
  21. //定义太阳的材质系数  
  22. GLfloat sun_ambient[] = { 0.2f, 0.2f, 0.0f, 1.0f };  
  23. GLfloat sun_diffuse[] = { 0.8f, 0.8f, 0.0f, 1.0f };  
  24. GLfloat sun_specular[] = { 1.0f, 1.0f, 0.0f, 1.0f };  
  25. GLfloat sun_emission[] = { 0.1f, 0.0f, 0.0f, 1.0f};  
  26.  
  27. //定义地球的材质系数  
  28. GLfloat earth_ambient[] = { 0.0f, 0.2f, 0.0f, 1.0f };  
  29. GLfloat earth_diffuse[] = { 0.0f, 0.8f, 0.0f, 1.0f };  
  30. GLfloat earth_specular[] = { 0.0f, 1.0f, 0.0f, 1.0f };  
  31. GLfloat earth_shininess[] = { 80.0f };  
  32. GLfloat earth_emission[] = { 0.0f, 0.1f, 0.0f, 1.0f };  
  33.  
  34. //定义月球的材质系数  
  35. GLfloat moon_ambient[] = { 0.0f, 0.0f, 0.1f, 1.0f };  
  36. GLfloat moon_diffuse[] = { 0.0f, 0.0f, 0.8f, 1.0f };  
  37. GLfloat moon_specular[] = { 0.0f, 0.0f, 0.9f, 1.0f };  
  38. GLfloat moon_shininess[] = { 50.0f };  
  39. GLfloat moon_emission[] = { 0.0f, 0.1f, 0.0f, 1.0f };  
  40.  
  41. void Initial()  
  42. {  
  43.     glEnable(GL_LIGHTING); //启用光照  
  44.     glEnable(GL_DEPTH_TEST); //启用深度测试  
  45.     glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //背景为黑色  
  46. }  
  47.  
  48. void ChangeSize(int w, int h)  
  49. {  
  50.     GLfloat fAspect;  
  51.     if (0 == h)  
  52.         h = 1;  
  53.     glViewport(0, 0, w, h); //设置视区尺寸  
  54.     glMatrixMode(GL_PROJECTION); //指定当前操作投影矩阵堆栈  
  55.     glLoadIdentity(); //重置投影矩阵  
  56.  
  57.     fAspect = (float)w / (float)h; //计算视区的宽高比  
  58.     gluPerspective(45.0f, fAspect, 1.0f, 1000.0f); //指定透视投影的观察空间  
  59.     glMatrixMode(GL_MODELVIEW);  
  60.     glLoadIdentity();  
  61. }  
  62.  
  63. void Display(void)  
  64. {  
  65.     glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);  
  66.     glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);  
  67.     glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);  
  68.     glLightfv(GL_LIGHT0, GL_POSITION, light_position);  
  69.     glEnable(GL_LIGHT0); //打开光源0  
  70.  
  71.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色和深度缓冲区  
  72.     glMatrixMode(GL_MODELVIEW); //指定当前操作模型视图矩阵堆栈  
  73.     glLoadIdentity(); //重置模型视图矩阵  
  74.  
  75.     glTranslatef(0.0f, 0.0f, -500.0f); //将图形沿z轴负向移动  
  76.  
  77.     //画太阳  
  78.     glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);  
  79.     glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);  
  80.     glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);  
  81.     glLightfv(GL_LIGHT1, GL_POSITION, light1_position);  
  82.     glEnable(GL_LIGHT1); //打开光源1  
  83.     glMaterialfv(GL_FRONT, GL_AMBIENT, sun_ambient);  
  84.     glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_diffuse);  
  85.     glMaterialfv(GL_FRONT, GL_SPECULAR, sun_specular);  
  86.     glMaterialfv(GL_FRONT, GL_EMISSION, sun_emission);  
  87.  
  88.     glColor3f(0.8f, 0.0f, 0.0f);  
  89.     glutSolidSphere(70.0f, 20, 20);  
  90.     glDisable(GL_LIGHT1); //关闭光源1  
  91.  
  92.     //画地球  
  93.     glMaterialfv(GL_FRONT, GL_AMBIENT, earth_ambient);  
  94.     glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_diffuse);  
  95.     glMaterialfv(GL_FRONT, GL_SPECULAR, earth_specular);  
  96.     glMaterialfv(GL_FRONT, GL_SHININESS, earth_shininess);  
  97.     glMaterialfv(GL_FRONT, GL_EMISSION, earth_emission);  
  98.  
  99.     glColor3f(0.0f, 0.8f, 0.0f);  
  100.     glRotatef(23.27,0.0,0.0,1.0); //地球与太阳的黄赤交角  
  101.     glRotatef(fElect1, 0.0f, 1.0f, 0.0f);  
  102.     glTranslatef(300.0f, 0.0f, 0.0f);  
  103.     glutSolidSphere(20.0f, 20, 20);  
  104.  
  105.     //画月球  
  106.     glMaterialfv(GL_FRONT, GL_AMBIENT, moon_ambient);  
  107.     glMaterialfv(GL_FRONT, GL_DIFFUSE, moon_diffuse);  
  108.     glMaterialfv(GL_FRONT, GL_SPECULAR, moon_specular);  
  109.     glMaterialfv(GL_FRONT, GL_SHININESS, moon_shininess);  
  110.     glMaterialfv(GL_FRONT, GL_EMISSION, moon_emission);  
  111.  
  112.     glPopMatrix();  
  113.     glPopMatrix();  
  114.     glRotatef(6.0f, 1.0f, 1.0f, 1.0f);  
  115.     glRotatef(fElect2, 0.0f, 1.0f, 0.0f);  
  116.     glColor3f(0.0f, 0.0f, 0.8f);  
  117.     glTranslatef(30.0f, 0.0f, 0.0f);  
  118.     glutSolidSphere(5.0f, 20, 20);  
  119.  
  120.     glLoadIdentity();  
  121.     fElect1 += 2.0f; //增加旋转步长,产生动画效果  
  122.     if (360.0f < fElect1)  
  123.         fElect1 = 2.0f;  
  124.     fElect2 += 24.0f;  
  125.     if (360.0f < fElect2)  
  126.         fElect2 = 24.0f;  
  127.     glFlush();  
  128.     glutSwapBuffers();  
  129. }  
  130.  
  131. void TimerFunc(int value)  
  132. {  
  133.     glutPostRedisplay();  
  134.     glutTimerFunc(100, TimerFunc, 1); //100毫秒后调用定时器回调函数  
  135. }  
  136.  
  137. int main(int argc, char *argv[])  
  138. {  
  139.     glutInit(&argc, argv);  
  140.     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //窗口使用RGB颜色,双缓存和深度缓存  
  141.     glutInitWindowSize(400, 400);  
  142.     glutCreateWindow("地球运动动画");  
  143.     glutReshapeFunc(ChangeSize);  
  144.     glutDisplayFunc(Display);  
  145.     glutTimerFunc(500, TimerFunc, 1); //指定定时器回调函数  
  146.     Initial();  
  147.     glutMainLoop();  
  148.     return 0;