实践项目:https://github.com/JackieLong/OpenGL/tree/main/project_culling_test

需求场景

image.png
从立方体的外部任意角度观察,只能看到最多3个面,完全可以避免绘制其他的面,提升渲染效率。

一个闭合的形状,它的每一个面都有两侧,每一侧要么面向摄像机(眼睛、观察者),要么背对摄像机。OpenGL通过面剔除(Face Culling)来实现只绘制面向观察者的面。
OpenGL能够检查所有面向(Front Facing)观察者的面,并渲染它们,而丢弃那些背向(Back Facing)的面,节省我们很多的片段着色器调用(它们的开销很大!)。但我们仍要告诉OpenGL哪些面是正向面(Front Face),哪些面是背向面(Back Face)。OpenGL使用了一个很聪明的技巧,分析顶点数据的环绕顺序(Winding Order)。

环绕顺序

在程序代码中,我们会按一定顺序定义如下图两个三角形:

  1. // 顶点的定义顺序就是顶点的绘制顺序
  2. const GLfloat vertices[] = {
  3. // 三角形(左),顶点绘制顺序:1->2->3,顺时针(clock-wise,CW)
  4. vertices[0], // 顶点1
  5. vertices[1], // 顶点2
  6. vertices[2], // 顶点3
  7. // 三角形(右),顶点绘制顺序:1->3->2,逆时针(counter clock-wise,CCW)
  8. vertices[0], // 顶点1
  9. vertices[2], // 顶点3
  10. vertices[1] // 顶点2
  11. };

OpenGL_面剔除(Culling) - 图2

相关API

  1. glEnable(GL_CULL_FACE); // 开启面剔除,默认不开启
  2. glFrontFace(mode); // 定义“Front Face”
  3. // mode的可能取值如下:
  4. // GL_CW: 顶点绘制顺序是顺指针的面是“Front Face”
  5. // GL_CCW: 顶点绘制顺序是逆指针的面是“Front Face”,默认值
  6. glCullFace(mode); // 剔除朝向摄像机的面。
  7. // GL_BACK: 剔除朝向摄像机的背面,默认值
  8. // GL_FRONT: 剔除朝向摄像的正面
  9. // GL_FRONT_AND_BACK: 都剔除。

具体如何调用,由三角形顶点数据的定义顺序决定:

  1. // 按顺时针定义立方体的六个面的正面(朝外的面)
  2. float cubeVertices[] = {
  3. // Back face
  4. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // Bottom-left
  5. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
  6. 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // bottom-right
  7. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
  8. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left
  9. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
  10. // Front face
  11. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
  12. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
  13. 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right
  14. 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right
  15. -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // top-left
  16. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
  17. // Left face
  18. -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right
  19. -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-left
  20. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left
  21. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left
  22. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right
  23. -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right
  24. // Right face
  25. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left
  26. 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right
  27. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
  28. 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right
  29. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left
  30. 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
  31. // Bottom face
  32. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right
  33. 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, // top-left
  34. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left
  35. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left
  36. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right
  37. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right
  38. // Top face
  39. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
  40. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
  41. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
  42. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
  43. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
  44. -0.5f, 0.5f, 0.5f, 0.0f, 0.0f // bottom-left
  45. };
  46. // 此时,朝外的面都是顺时针绘制,我们可以如下调用:
  47. glEnable(GL_CULLING);
  48. glFrontFace(GL_CW);
  49. glCullFace(GL_BACK);
  50. // 或者
  51. glEnable(GL_CULLING);
  52. glFrontFace(GL_CCW);
  53. glCullFace(GL_FRONT);