OpenGL

004 --- 正背面剔除、深度测试、多边形模式

2020-07-13  本文已影响0人  清风烈酒2157

前言

默认情况下,我们渲染的每一个点,线或者三角形都会在屏幕上进行光栅化,并且按照组合图元批次指定的顺序进行排列.
这个在某些情况下会产生问题,其中可能出现的一个问题就是:

对于上面问题,一个可能解决的办法:油画法.首先渲染那些较远的三角形,再在他们的的上方渲染那些较近的三角形.但是这个办是很低效的.

  1. 我们必须对任何发生几何图形重叠的地方每个像素进行两次操作,而且储存其中进行读写操作会使速度变慢.
  2. 对独立的三角形进行排序的开销会过高.

绘制甜甜圈

GLFrame             viewFrame; //参考帧
//使用GLFrustum类来设置透视投影
GLFrustum           viewFrustum; //透视投影
GLTriangleBatch     torusBatch; //批次容器
GLMatrixStack       modelViewMatix; // 模型栈
GLMatrixStack       projectionMatrix; //投影栈
GLGeometryTransform transformPipeline; //管线
GLShaderManager     shaderManager; //着色管理器
  1. 清屏
  2. 将viewFrame压栈
  3. 设置图形颜色
  4. 默认光源着色器
  5. 绘制
  6. 出栈
  7. 交换缓存区

void RenderScene()
{
 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    modelViewMatix.PushMatrix(viewFrame);
    GLfloat vRed[] = { 1.0f, 0.0f, 1.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
  
    torusBatch.Draw();
    modelViewMatix.PopMatrix();
    glutSwapBuffers();
}

  1. 设置屏幕颜色
  2. 初始化着色管理器
  3. 参考帧往后移动
  4. 绘制甜甜圈 gltMakeTorus系统`给出绘制API

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

  1. 设置线宽

void SetupRC()
{
    glClearColor(0.0f, 0.3f, 0.3f, 1.0f );
    
    shaderManager.InitializeStockShaders();

    viewFrame.MoveForward(7.0);
    
    gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
    
    glPointSize(4.0f);
}

objectFrame,观察动,物体不动


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



image.png

正面和背面剔除

如果,甜甜圈出现互相遮挡的问题.

image.png

如果以我们看见的角度为正面,看不见的为背面,也就是说我们只要渲染看见的那部分就可以了。

对于正面和背面进行区分的原因之一就是为了进行剔除.

OpenGL可以做到检查所有正面朝向观察者的面,并渲染它们,背后朝向的面则不再渲染,从而节约片元着色器的性能.

1 .添加一个右键单击的示例程序


//标记:背面剔除、深度测试
int iCull = 0;
int iDepth = 0;


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

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


if (iCull) {
        glEnable(GL_CULL_FACE);
        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);
    }else
    {
        glDisable(GL_CULL_FACE);
    }

正背面剔除相关代码


void glEnable(GL_CULL_FACE);


void glDisable(GL_CULL_FACE);

mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默 认GL_BACK

  1. GL_FRONT: 剔除正面
  2. GL_BACK: 剔除背面,是默认值
  3. GL_FRONT_AND_BACK: 剔除正背面

void glCullFace(GLenum mode);

mode参数为: GL_CW,GL_CCW,默认值:GL_CCW

  1. GL_CW: 顺时针
  2. GL_CCW: 逆时针,是默认值

glFrontFace(GL_CW);


glCullFace(GL_BACK); 

glFrontFace(GL_CW);


glCullFace(GL_FRONT);

设置背面剔除之后效果

image.png
            |
            |
image.png

深度测试

深度测试是另外一种高效消除隐藏表面的技术.

在绘制一个像素时,讲一个值(z值)分配它,这个值表示它到观察者的距离.然后,另外一个像素需要在屏幕上的同样位置进行绘制时,新像素的z值将与已经存储的像素的z值进行比较.如果新像素的z值比较大,那么它的距离观察者就比较近,这就就在原来的像素上面,所以原来的像素就会被新的像素覆盖.如果新的像素z值更低,那么它就必须位域原来的像素后面,不能遮住原来的像素,在内部,这个任务是通过深度缓冲区实现的,它存储了屏幕上每个像素的深度值.

观察者可以放在坐标系的任意位置,所以,不能简单的说z数值越大或越小,观察者就越靠近或物体.

image.png

启用深度测试

在使用GLUT设置OpenGL窗口时,应该请求一个深度缓冲区,按如下方式申请一个颜色缓冲区和一个深度缓冲区.


glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

要启用深度测试:

glEnable(GL_DEPTH_TEST);

如果没有深度缓冲区,那么启用深度测试的命令将被忽略.

关闭深度测试:


glDisable(GL_DEPTH_TEST);

image.png

深度缓冲区

深度缓冲区

总结

深度测试在绘制多个对象是能进一步解决性能问题,油画法的绘制形式对于图形硬件来说,会导致同一片区域重复进行绘制,而且每次绘制都产生性能开销.如果开销过大则导致光栅化过程变慢,这种方式被称为填充受限.所以可以将油画法颠倒过来使用,会加速填充性能,首先绘制那些较近的对象,然后在绘制那些较远的对象. 深度测试将消除那些对那些应该被已存在的像素覆盖的像素,这将节省可观的存储器带宽.

多边形模式

多边形(含三角形)不一定是实心的.在默认情况下,多边形作为实心图形绘制的,但是我们可以通过将多边形指定显示为显示轮廓或只有点(值显示顶点)来改变这种行为.函数glPolygonMode允许将多边形渲染成实体,轮廓或只有点.


void glPolygonMode(GLenum face,GLenum mode);

  1. face这个参数确定显示模式将适用于物体的哪些部分,控制多边形的正面和背面的绘图模式:
    GL_FRONT:表示显示模式将适用于物体的前向面(也就是物体能看到的面)
    GL_BACK:表示显示模式将适用于物体的后向面(也就是物体上不能看到的面)
    GL_FRONT_AND_BACK:表示显示模式将适用于物体的所有面

  2. mode这个参数确定选中的物体的面以何种方式显示(显示模式):
    GL_POINT:表示只显示顶点,多边形用顶点显示
    GL_LINE:表示显示线段,多边形用轮廓显示
    GL_FILL:表示显示面,多边形采用填充形式


 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

image.png

glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);

image.png
上一篇 下一篇

猜你喜欢

热点阅读