五、OpenGL绘制甜甜圈引发的思考

前言

如何绘制一个甜甜圈

1.设置窗口以及矩阵

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);

}

2.键位设置,控制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();

}

3.做一些准备工作,比如设置背景色,创建甜甜圈容器等

void SetupRC()

{

    //1.设置背景颜色

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


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

    shaderManager.InitializeStockShaders();


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

    viewFrame.MoveForward(7.0);


    //4.创建一个甜甜圈

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


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

    glPointSize(4.0f);

}

4.渲染场景

void RenderScene()

{

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

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


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

    modelViewMatix.PushMatrix(viewFrame);


    //3.设置绘图颜色

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


    //4.使用平面着色器


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


    //使用默认光源着色器

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


    //5.绘制

    torusBatch.Draw();

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

    modelViewMatix.PopMatrix();


    //7.交换缓存区

    glutSwapBuffers();

}

通过上面的方式,我们会发现当我们进行键位控制时,我们会发现我们的甜甜圈变成黑色的,如下图所示:

出现这种情况就是因为我们没有进行正背面剔除,当我们采用正背面剔除时,只对可见的部分进行渲染,此时OpenGL的渲染性能即可提高超过50%

什么是正背面剔除

任何平面都有2个面,正面/背面,意味着观察者一个时刻只能看到其中一个面,而OpenGL可以通过分析顶点数据的顺序检测到所有此时面向观察者的面并渲染他们,从而丢弃背面的渲染,这样可以节约片元着色器的性能。

怎样分析顶点数据的顺序

正面:按照逆时针顶点连接顺序的三角形面

背面:按照顺时针顶点连接顺序的三角形面

分析立方体中的正背面

当观察者在右侧时,则右侧的三角形方向为逆时针方向则为正面,左侧的三角形为顺时针为背面

当观察者在左侧时,则左侧的三角形方向为逆时针方向为正面,右侧的三角形为顺时针为背面

正面和背面是由三角形的顶点定义顺序和观察者的方向共同决定的,随着观察者的角度方向改变,正面背面也会跟着改变

正背面剔除代码实现

//开启表面剔除(默认是背面剔除)

    void glEnable(GL_CULL_FACE)

    //关闭表面剔除

    void glDisable(GL_CULL_FACE)

    //用户选择剔除哪个面(正面/背面)

    void glCullFace(GL_BACK)

    //用户指定逆时针/顺时针为正面,默认为逆时针

    void glFrontFace(GL_CCW)

结果我们发现又会出现深度问题,如下图所示:

什么是深度和深度缓冲区

深度就是该像素点在世界坐标系中距离观察者的距离,Z值

深度缓冲区就是一块内存区域,专门存储着每个像素点(绘制在平面上的)深度值,深度值越大,则离摄像机就越远

深度缓冲区和颜色缓存区是对应的,颜色缓存区存储着像素的颜色信息,深度缓冲区存储像素的深度信息。在决定是否绘制一个物体表面时,首先会将该物体对应的像素深度值与当前深度缓冲区的值进行比较,如果大于缓冲区的值,则丢弃这部分,否则利用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜色缓存区,这个过程称为深度测试

深度测试代码实现

    //开启深度测试

    void glEnable(GL_DEPTH_TEST)

    //关闭深度测试

    void glDisable(GL_DEPTH_TEST)

    //绘制场景前,要清空颜色缓存区和深度缓冲区

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

ZFighting闪烁问题

因为开启深度测试后,OpenGL就不会再去绘制模型被遮挡的部分,这样实现的显示更加真实,但是由于深度缓冲区精度的限制对于深度相差非常小的情况下,OpenGL就可能出现不能正确判断两者的深度值,导致结果不可预测,显示出来的现象是交错闪烁的,2个画面交错出现。

启用Polygon Offset(多边形偏移)解决

1.启用启用Polygon Offset,让深度值之间产生间隔,如果2个图形之间有间隔,意味着就不会产生干涉,可以理解为在执行深度测试前将立方体的深度值做一些细微的增加

2.指定偏移量

3.关闭Polygon Offset

代码实现如下

  //开启多边形偏移

       glEnable(<#GLenum cap#>)

       //参数类标

       GL_POLYGON_OFFSET_FILL,GL_POLYGON_OFFSET_LINE,GL_POLYGON_OFFSET_POINT

       //指定s偏移量

       glPolygonOffset(<#GLfloat factor#>, <#GLfloat units#>)

       //关闭多边形偏移

       glDisable(<#GLenum cap#>)

你可能感兴趣的:(五、OpenGL绘制甜甜圈引发的思考)