复习下基本流程

  1. 初始化glut模块
  2. 加载配置模块
  3. 设置窗口大小
  4. 给窗口取个名字
  5. 注册一个方法监测窗口改变
  6. 注册一个方法监测键盘输入
  7. 注册一个方法监测屏幕刷新
  8. 初始化glew模块并检测是否初始化成功
  9. 设置默认值(环境)
  10. 加载并启动MainLoop

实际上并不需要完全按照上面的步骤来做,像注册监测的那三个方法可以自由变换顺序。

具体代码:

  1. //着色器管理类
  2. GLShaderManager shaderManager;
  3. //甜甜圈容器
  4. GLTriangleBatch donutBatch;
  5. //窗口改变
  6. void ChangeSize(int w, int h) { }
  7. //监测键盘输入信号
  8. void SpecialKeys(int key, int x, int y) { }
  9. //屏幕刷新
  10. void RenderScene() { }
  11. //初始化相关工作
  12. void SetupRC() { }
  13. int main( int argc, char *argv[]) {
  14. gltSetWorkingDirectory(argv[0]);
  15. //初始化glut模块
  16. glutInit(&argc, argv);
  17. //加载配置模块
  18. glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
  19. //设置窗口大小
  20. glutInitWindowSize(400, 400);
  21. //给窗口取个名字
  22. glutCreateWindow("There is a donut here");
  23. //注册一个方法监测窗口改变
  24. glutReshapeFunc(ChangeSize);
  25. //注册一个方法监测键盘输入
  26. glutSpecialFunc(SpecialKeys);
  27. 注册一个方法监测屏幕刷新
  28. glutDisplayFunc(RenderScene);
  29. //初始化glew模块并检测是否初始化成功
  30. GLenum err = glewInit();
  31. if (GLEW_OK != err) {
  32. fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
  33. return 1;
  34. }
  35. //设置默认值(环境)
  36. SetupRC();
  37. //加载并启动MainLoop
  38. glutMainLoop();
  39. return 0;
  40. }

绘制甜甜圈的准备工作

1.因为这次是要画一个可以展览的甜甜圈(不然就没有灵魂了),所以我们需要有一个观察者,也就是摄影师(观察者坐标系),且观察者自身也有一个属性(物体坐标系)

  1. GLMatrixStack cameraMatix;
  2. GLFrame cameraFrame;

2.而甜甜圈自身也是有大小的,所以我们需要描绘甜甜圈的属性(物体坐标系)

  1. GLMatrixStack donutMatrix;

3.在将甜甜圈转化为世界坐标系的时候,需要设置甜甜圈的投影规则(称之为管线)

  1. GLGeometryTransform donutTransform;

4.这次需要绘制的是立体的甜甜圈,所以为了有立体感,需要设置投影方式为透视投影,为此,我们需要设置一个全局变量用于记录当前透视状态

  1. GLFrustum viewFrustum;

初始化工作

SetupRC中需要设置背景颜色,初始化全局变量

  1. glClearColor(240/255.0, 248/255.0, 255/255.0, 1.0f);
  2. shaderManager.InitializeStockShaders();
  3. //将相机向后移动7个单元:肉眼到物体之间的距离
  4. cameraFrame.MoveForward(7.0);
  5. //创建一个甜甜圈
  6. //void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
  7. //参数1:GLTriangleBatch 容器帮助类
  8. //参数2:外边缘半径
  9. //参数3:内边缘半径
  10. //参数4、5:主半径和从半径的细分单元数量
  11. gltMakeTorus(donutBatch, 1.0f, 0.3f, 52, 26);

窗口改变时

  1. //1.防止h变为0
  2. if(h == 0)
  3. h = 1;
  4. //2.设置视口窗口尺寸
  5. glViewport(0, 0, w, h);
  6. //3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
  7. // 设置透视模式,初始化其透视矩阵
  8. viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
  9. //4.把透视矩阵加载到透视矩阵对阵中
  10. donutMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
  11. //5.初始化渲染管线
  12. donutTransform.SetMatrixStacks(cameraMatix, donutMatrix);

屏幕刷新时

  1. //1.清除窗口和深度缓冲区
  2. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  3. //2.把摄像机矩阵压入模型矩阵中
  4. cameraMatix.PushMatrix(cameraFrame);
  5. //3.设置绘图颜色
  6. GLfloat colorDonut[] = { 255/255.0, 215/255.0, 0/255.0, 1.0f };
  7. //4.
  8. //使用平面着色器
  9. //参数1:平面着色器
  10. //参数2:模型视图投影矩阵
  11. //参数3:颜色
  12. // shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
  13. //使用默认光源着色器
  14. //通过光源、阴影效果跟提现立体效果
  15. //参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
  16. //参数2:模型视图矩阵
  17. //参数3:投影矩阵
  18. //参数4:基本颜色值
  19. shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, donutTransform.GetModelViewMatrix(), donutTransform.GetProjectionMatrix(), colorDonut);
  20. //5.绘制
  21. donutBatch.Draw();
  22. //6.出栈 绘制完成恢复
  23. cameraMatix.PopMatrix();
  24. //7.交换缓存区
  25. glutSwapBuffers();

[demo5].不画甜甜圈的吃货不是好的程序猿 - 图3

Run一下,发现甜甜圈出现了,开心~

但是,这是一个不会动的甜甜圈!!!
为了让他动起来,我们必须进行操作
SpecialKey

  1. //监测键盘输入信号
  2. void SpecialKeys(int key, int x, int y) {
  3. //w 设置为后空翻吧,就是围绕x轴向后旋转
  4. if(key == 119)
  5. cameraFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
  6. //s 设置为前空翻吧,就是围绕x轴向前旋转
  7. if(key == 115)
  8. cameraFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
  9. //a 设置为直立顺时针转圈,就是围绕y轴顺时针旋转
  10. if(key == 97)
  11. cameraFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
  12. //d 设置为直立顺时针转圈,就是围绕y轴逆时针旋转
  13. if(key == 100)
  14. cameraFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
  15. //q 设置为正向中心顺时针转圈,就是围绕z轴逆时针旋转
  16. if(key == 113)
  17. cameraFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 0.0f, 1.0f);
  18. //e 设置为正向中心逆时针转圈,就是围绕z轴逆时针旋转
  19. if(key == 101)
  20. cameraFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 0.0f, 1.0f);
  21. //3.重新刷新
  22. glutPostRedisplay();
  23. }

再Run一下,棒~想要的效果出来了!👏👏👏👏👏

又又又又又但是!在旋转的时候,出现了很多黑色的东西,色香味色先挂了,哪里还有食欲?想要解决它,首先得了解它是什么。
[demo5].不画甜甜圈的吃货不是好的程序猿 - 图4
在现实世界中,一个甜甜圈还没开始吃的时候,应该只能看到它脆脆的皮,而不可能看到它松松的肉,但是在我们绘制的甜甜圈中,我们看到了它松松的肉,这是不正常的。
在OpenGL的世界中,所描绘的每一个物体都是有它的正面和背面的,正面不会被遮挡,而背面会被遮挡住,用这样的方式来模拟现实世界中不透明的物体的成像。
因此现在的这种情况,我们只需要打开OpenGL自身的背面剔除功能就好了,所以在RenderScene绘制的时候我们需要设置背面剔除.

  1. glEnable(GL_CULL_FACE);

再run一次,发现不再出现黑色了,👏👏👏👏👏

但是又来了,这次,黑色没有了,可旋转到某一个方向的时候,出现了透视的现象,这是为什么呢?

[demo5].不画甜甜圈的吃货不是好的程序猿 - 图5

在这次的甜甜圈的世界里,因为采用的是油画的画法,即先画底下涂层的,再画上面涂层,因此,图形的正面与背面是已经设定好了的,所以在旋转的时候,由于正面位于背面的涂层下面,但是正面是不会被背面所遮挡的,所以便出现了透视的效果

该怎么做呢?先看一下下一篇OpenGL的深度测试把,深度测试可以帮我们定位很多问题!