美文网首页
OpenGL-渲染技巧

OpenGL-渲染技巧

作者: tp夕阳武士 | 来源:发表于2020-02-25 18:13 被阅读0次
    1.渲染一个甜甜圈:

    源码共享地址

    image.png
    油画算法

    我们在渲染图形的时候,因为有光照的原因,有些面会被隐藏,有的面不被隐藏,但是OpenGL本身不知道(<<计算机图形学>>中有讲述).

    使用油画算法可以解决这个问题:
    油画算法先绘制观察者距离较远的物体,再绘制距离较近的物体;

    image.png

    油画算法存在的问题:
    1.如果不被显示的图形内容也被渲染到屏幕上的话,会增加GPU的渲染工作,降低工作效率
    2.有些时候不知如何判断哪个物体在上面那个物体在下面.


    image.png
    正背面剔除

    正背面剔除的思路:
    1.任何一个物体在观察者的角度看来,都有一些面是无法看到的,这种面我们称之为背面(例如一个正方体,我们无论从任何角度去观察,最多只会看到这个正方体三个面)
    2.在绘制图形是,直接不绘制那些不被看到的面,OpenGL可以做到检查所有正面朝向观察者角度的面,并渲染他们,然后丢弃背面朝向观察者的面,这样的渲染方式可以节约片元着色器的性能.

    什么是正面什么是背面?

    逆时针绘制的三角形是正面,顺时针绘制的三角形为背面;(这个正背面的判断条件是可以修改的)

    正方面三角形判断.png
    如何开启正背面剔除
    //1.关闭与开启正背面剔除的方法
    glEnable(GL_CULL_FACE);//开启正背面剔除
    glDisable(GL_CULL_FACE);//关闭正背面剔除
    //2.开发者选择剔除那个面(正面或者背面)
    void glCullFace(GLenum mode);
    //mode -> GL_FRONT,GL_BACK,GL_FRONTANDBACK
    
    //3.用户指定绕序那个正面
    void glFrontFace(GLenum mode);
    mode参数为GL_CW(逆时针为正面), GL_CCW(顺时针为正面)
    默认值为:GL_CCW;
    
    
    //4.剔除正面的实现代码:
    glCullFace(GL_FRONT); //背面显示
    glFrontFace(GL_CW);//设定顺时针为正面
    
    也可以:
    glCullFace(GL_BACK);
    
    增加右键菜单功能:
    
    //声明菜单方法
    void ProcessMenu(int value){
        switch (value) {
               case 1:
                printf("功能1\n");
                break;
              default:
                break;
         }
    }
    // main 函数内部
    {
        glutCreatMenu(ProcessMenu); //创建菜单栏的函数
        glutAddMenuEntry("功能1",1);
        glutAddMenuEntry("功能2",2);//这是一个类似于枚举的方式添加的
        glutAttachMenu(GLUT_RIGHT_BUTTON); //只能鼠标的那个键触发
    }
    
    关于深度缓冲区与深度测试

    深度指的是像素点在3D世界中距离摄像机的距离,即是Z值;
    (个人理解: 假设我们人眼去观察一前一后的两个正方体,前面的正方体会把后面的正方体遮住,两个正方体距离观察者的眼睛的距离就表示深度);

    如果不使用深度缓冲,在渲染的时候,先绘制了距离观察者较近的的图形,再绘制较远的图形,则远距离的图形反而会把近距离的图形遮盖了,这个不符合现实情况.有了深度缓冲的技术后,绘制图形的先后顺序问题就不那么重要了,只有存在深度缓冲区,OpenGL都会把像素的深度值写入到缓冲区中,除非调用了: glDepthMask(GL_FLASE).来禁止写入;

    深度缓冲区和颜色缓冲区是对应的,颜色缓冲区存储颜色的缓冲信息,深度缓冲区存储的像素点的深度信息,在决定绘制一个物体的表面的时候,首先要将表面对应的像素点的深度值与当前的深度缓冲区中的值进行比较,如果大于深度缓冲区中的值,则丢弃,否则利用新的深度值和颜色值替换缓冲区的值,这个过程称为: 深度测试.

    什么时候开启深度测试?

    当我们在绘制立体图形的时候,就应该把深度测试的功能打开
    开启了深度测试的时候,在清除缓冲区的时候,一定要清除深度缓冲区,glclear(GL_DEPTH_BUFFER_BIT)

    glEnable(GL_DEPTH_TEST); // 开启深度测试的方法
    
    glDisable(GL_DEPTH_TEST);//关闭深度测试的方法;
    
    深度测试的mode
    glDepthFunc(GLEnum mode);
    mode的枚举值如下:  (这个值指的是深度-像素点距离观察者的距离)
    GL_ALWAYS : 总是通过测试
    GL_NEVER : 总是不通过
    GL_LESS : 当前深度 < 缓冲区深度是通过
    GL_EQUAL : 当前深度=缓冲区深度是通过
    GL_LEQUAL : 当前深度<=缓冲区深度是通过
    GL_GREATER : 当前深度>缓冲区深度是通过
    GL_NOTEQUAL : 当前深度不等于缓冲区深度的时候通过
    GL_GEQUAL: 当前深度>=缓冲区深度是通过
    
    闪烁问题-ZFighting
    闪烁问题示例图.png

    闪烁问题是如何出现的?
    由于深度测试缓冲区精度受限于深度差距非常小的情况下OpenGL可能无法作出正确的判断两者的深度会导致深度测试的结果无法预测,显示出来的现象时出现交错闪烁的问题,两个图形交替显示.(这个就好按照身高排队,但是有两个人裸高相等,就无法判定哪一个排前面);
    解决方案:让两个点的深度人为添加一个间隙.

    //开启多边形片偏移

    glEnable(GL_POLYGON_OFFSET_FILL);
    参数列表:
    GL_POLYGON_OFFSET_POINT;
    GL_POLYGON_OFFSET_LINE;
    GL_POLYGON_OFFSET_FILL;
    

    //指定偏移量

    void glPolygonOffset(Glfloat factor, Glfloat units);
    每一个片段(Fragment)的深度值都会增加如下偏移量:
    Offset = (m*factor) + (r*unitis)
    m:多边形深度的斜率最大值,一个多边形越是与近剪裁面平行,m就越接近0;
    r:能产生窗口坐标系数的深度中可分辨的差异最小值,它是由具体的OpenGL平台指定的一个产量
    
    一个大于0的Offset会把模型推到离观察者根源的位置,
    一般而言,只需要-1.0到1.0之间这样的值赋值给glPolygonOffset基本可以满足需求;
    

    //关闭偏移量

    glDisable(GL_POLYGON_OFFSET_FILL);
    参数列表相同
    
    闪烁问题的预防
    • 不要讲两个物体考得太近,避免渲染时三角形跌在一起,这种方式要求对场景中物体插入一个少量的偏移量,那么就可以避免ZFighting现象
    • 尽可能将近剪裁面设置得离观察者远一点,上图在近剪裁面附近,深度的精确度是很高的,因此尽量让剪裁面远一些,但是这种方式会使得离观察者较近的物体被剪裁掉,因此需要调制好剪裁参数
    • 使用更搞位数的深度位数,(有16位/24位/32位,大多数情况下是24位);如果使用32位的缓冲区,可以使精度更搞;

    (学习到目前,暂时没有合适的demo来颜色Zfighting现象);

    剪裁

    在OpenGL中提高渲染的一种方式,值刷新屏幕中发生变化的部分,OpenGL允许将要进行渲染的窗口只去指定一个剪裁框.
    基本原理:用于渲染时,限制绘制区,通过这个技术,在屏幕上指定一块矩形区域,启用剪裁测试之后,不在此区域内的片元被丢弃,只有在这个区域内的片元才会进入帧缓冲,因此实际打到的效果就是在屏幕上开辟了一个小的串口可以在其中进行指定内容的绘制.

    //开启
    glEnable(GL_SCISSOR_TEST); 
    //关闭
    glDisable(GL_SCISSOR_TEST);
    //指定剪裁窗口的
    void glScissor(GLint x,GLint y, GLSize width,GLSize height);
    
    //召唤场景
    void RenderScene(void)
    {
        //设置清屏颜色为蓝色
        glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        //1.现在剪成小红色分区
        //(1)设置裁剪区颜色为红色
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        //(2)设置裁剪尺寸
        glScissor(100, 100, 600, 400);
        //(3)开启裁剪测试
        glEnable(GL_SCISSOR_TEST);
        //(4)开启清屏,执行裁剪
        glClear(GL_COLOR_BUFFER_BIT);
        
        // 2.裁剪一个绿色的小矩形
        //(1).设置清屏颜色为绿色
        glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
        //(2).设置裁剪尺寸
        glScissor(200, 200, 400, 200);
        //(3).开始清屏执行裁剪
        glClear(GL_COLOR_BUFFER_BIT);
        
        //关闭裁剪测试
        glDisable(GL_SCISSOR_TEST);
        
        //强制执行缓存区
        glutSwapBuffers();
    }
    
    
    剪裁效果.png
    窗口 / 视口 / 剪裁区域
    • 窗口 显示界面
    • 视口 就是窗口中用来显示图形的一块矩形区域,可以比窗口大或小都可以,只有被绘制在视口范围内的图形才能被显示,如果图形有一部分出了视口区域,那么这部分就没办法看到了.通过glViewport(GLint x, GLint y, GLsizei width, GLsizei height)设置
    • 剪裁区域(平行投影) 就是视口矩形区域的最小最大x坐标(left,right)和最小最大y坐标 (bottom,top),⽽不是窗口的最小最大x坐标和y坐标(可以理解为在视口范围内设置剪裁区域,而不是在窗口范围内设置剪裁区域)。通过glOrtho()函数设置,这个函数还需指定最近最远z坐标,形成⼀个立体的裁剪区域。

    混合

    混合是指在有透明度的图形重叠时需要用到的一种颜色重叠的计算,OpenGL在绘制图形是,会把颜色值存储在一个缓冲区内,每个片段的深度值也存放在深度缓冲区内,当"深度测试"功能被关闭的时候,新的颜色会简单地覆盖原来的颜色缓冲区存在的值,当深度缓冲功能打开时,新的颜色片段是当它们比原来的值更接近邻近的裁剪平面才会替换原来的颜色⽚段。

    //打开混合功能

    glEnabel(GL_BLEND);
    

    //设置混合因子

    glBlendFunc(GLenum S, GLenum D)
    S:源混合因子
    D:目标混合因子
    
    image.png

    总结: 源颜色的透明度值越高,源颜色保留的成分就越多,目标颜色保留的成分就越少(目标颜色指的是颜色缓冲区内保存的颜色值);

    混合效果.png

    混合案例

    相关文章

      网友评论

          本文标题:OpenGL-渲染技巧

          本文链接:https://www.haomeiwen.com/subject/klorzctx.html