OpenGL 渲染技巧:正背面剔除

  • 甜甜圈案例

    • 案列分析

      这个项目和上一个博客的绘制金字塔、六边形的案例的绘制流程基本上是差不多的,无非就是上一个博客绘制了好几个图案可以监听空格键来切换每个图案的显示,,此案例少了空格键的监听,还有就是变化方式,上一个博客是通过变换模型矩阵来变换图形的,而此案列通过变换观察者矩阵来变换图形,绘制流程有疑问可以看博客juejin.im/post/5f070a…

    • 运行结果

  • 问题面出现原因以及解决方案

    冲上面的运行结果会发现很明显在转动的过程中会出现黑色区域,出现的原因其实很简单,首先明确有光源照到的地方也就是阳面肯定是显示的是正常的颜色,没有光源照到的地方也就是阴面肯定是黑色,而在图形转动的过程中OpenGl不知道该显示阳面还是阴面,所以就导致出现红黑色交替的现象。解决方案:

    1. 油画家算法

      由远及近绘制盖住重叠的部分,但是这样也有问题就是凸显图形交叉的现象列如:

    2. 开启正背面剔除不绘制看不见的面(也就是阴面)

      首先正背面剔除性能方面就能节省50%,举例,一个正方体,最多只能看到单个面,使用正背面剔除就只绘制三个面另外三个看不见的面都不用绘制还一个就是OpenGL中默认规定了逆时针方向绘制的三角形是正面,当然你可以改为顺时针为正面,但是一般不建议这么操作,主要是由于这个设置不仅仅是作用于你的项目,而是作用于OpenGL全局的。
      正背面剔除相关方法

      • glEnable(GL_CULL_FACE);开启正背面剔除 (默认背面剔除)
      • glDisable(GL_CULL_FACE);关闭正背面
      • glFrontFace(GLenum mode);修改正面的函数GL_CW(顺时针),GL_CCW(逆时针),默认GL_CCW
      • glCullFace(GLenum mode);设置需要剔除的面GL_FRONT正面、GL_BACK反面、GL_FRONT_AND_BACK剔除正背面

  • 完整代码

    //演示了OpenGL背面剔除,深度测试,和多边形模式

    #include "GLTools.h"

    #include "GLMatrixStack.h"

    #include "GLFrame.h"

    #include "GLFrustum.h"

    #include "GLGeometryTransform.h"

    #include <math.h>

    #ifdef __APPLE__

    #include <glut/glut.h>

    #else

    #define FREEGLUT_STATIC

    #include <GL/glut.h>

    #endif

    ////设置角色帧,作为相机

    GLFrame viewFrame;

    //使用GLFrustum类来设置透视投影

    GLFrustum viewFrustum;

    GLTriangleBatch torusBatch;

    GLMatrixStack modelViewMatix;

    GLMatrixStack projectionMatrix;

    GLGeometryTransform transformPipeline;

    GLShaderManager shaderManager;

    //标记:背面剔除、深度测试

    int iCull = 0;

    int iDepth = 0;

    //渲染场景

    void RenderScene()

    {

    //1.清除窗口和深度缓冲区

    //可以给学员演示一下不清空颜色/深度缓冲区时.渲染会造成什么问题. 残留数据

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //开启/关闭正背面剔除功能

    if (iCull) {

    glEnable(GL_CULL_FACE);

    glFrontFace(GL_CCW);

    glCullFace(GL_BACK);

    }else

    {

    glDisable(GL_CULL_FACE);

    }

    //2.把摄像机矩阵压入模型矩阵中

    modelViewMatix.PushMatrix(viewFrame);

    //3.设置绘图颜色

    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };

    //4.

    //使用平面着色器

    //参数1:平面着色器

    //参数2:模型视图投影矩阵

    //参数3:颜色

    // shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);

    //使用默认光源着色器

    //通过光源、阴影效果跟提现立体效果

    //参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器

    //参数2:模型视图矩阵

    //参数3:投影矩阵

    //参数4:基本颜色值

    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);

    //5.绘制

    torusBatch.Draw();

    //6.出栈 绘制完成恢复

    modelViewMatix.PopMatrix();

    //7.交换缓存区

    glutSwapBuffers();

    }

    void SetupRC()

    {

    //1.设置背景颜色

    glClearColor(0.3f, 0.3f, 0.3f, 1.0f );

    //2.初始化着色器管理器

    shaderManager.InitializeStockShaders();

    //3.将相机向后移动7个单元:肉眼到物体之间的距离

    viewFrame.MoveForward(7.0);

    //4.创建一个甜甜圈

    //void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);

    //参数1:GLTriangleBatch 容器帮助类

    //参数2:外边缘半径

    //参数3:内边缘半径

    //参数4、5:主半径和从半径的细分单元数量

    gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);

    //5.点的大小(方便点填充时,肉眼观察)

    glPointSize(4.0f);

    }

    //键位设置,通过不同的键位对其进行设置

    //控制Camera的移动,从而改变视口

    void SpecialKeys(int key, int x, int y)

    {

    //1.判断方向

    if(key == GLUT_KEY_UP)

    //2.根据方向调整观察者位置

    viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);

    if(key == GLUT_KEY_DOWN)

    viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);

    if(key == GLUT_KEY_LEFT)

    viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);

    if(key == GLUT_KEY_RIGHT)

    viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);

    //3.重新刷新

    glutPostRedisplay();

    }

    //窗口改变

    void ChangeSize(int w, int h)

    {

    //1.防止h变为0

    if(h == 0)

    h = 1;

    //2.设置视口窗口尺寸

    glViewport(0, 0, w, h);

    //3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)

    // 设置透视模式,初始化其透视矩阵

    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);

    //4.把透视矩阵加载到透视矩阵对阵中

    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

    //5.初始化渲染管线

    transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);

    }

    void ProcessMenu(int value)

    {

    switch(value)

    {

    case 1:

    iDepth = !iDepth;

    break;

    case 2:

    iCull = !iCull;

    break;

    case 3:

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    break;

    case 4:

    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    break;

    case 5:

    glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);

    break;

    }

    glutPostRedisplay();

    }

    int main(int argc, char* argv[])

    {

    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

    glutInitWindowSize(800, 600);

    glutCreateWindow("Geometry Test Program");

    glutReshapeFunc(ChangeSize);

    glutSpecialFunc(SpecialKeys);

    glutDisplayFunc(RenderScene);

    //添加右击菜单栏

    // Create the Menu

    glutCreateMenu(ProcessMenu);

    glutAddMenuEntry("Toggle depth test",1);

    glutAddMenuEntry("Toggle cull backface",2);

    glutAddMenuEntry("Set Fill Mode", 3);

    glutAddMenuEntry("Set Line Mode", 4);

    glutAddMenuEntry("Set Point Mode", 5);

    glutAttachMenu(GLUT_RIGHT_BUTTON);

    GLenum err = glewInit();

    if (GLEW_OK != err) {

    fprintf(stderr, "GLEW Error: %sn", glewGetErrorString(err));

    return 1;

    }

    SetupRC();

    glutMainLoop();

    return 0;

    }

以上是 OpenGL 渲染技巧:正背面剔除 的全部内容, 来源链接: utcz.com/a/31861.html

回到顶部