LearnOpenGL - 渲染技巧

渲染技巧


©shuan9999

1. 隐藏⾯消除((Hidden surface elimination)

  • 先看如下图的问题:
    在这里插入图片描述
  • 出现这种问题的原因在于我们将其背面也绘制出来了,正常来说我们是看不到也不需要看到背面的,所以先看第一种解决办法:
  • 油画算法
    • 先绘制场景中的离观察者较远的物体,再绘制较近的物体
    • 例如下⾯的图例: 先绘制红⾊部分,再绘制⻩⾊部分,最后再绘制灰⾊部分,即可解决隐藏⾯消除的问题
    • 在这里插入图片描述
    • 弊端:
      • 使⽤油画算法,只要将场景按照物理距离观察者的距离远近排序,由远及近的绘制即可。那么会出现什么问题? 如果三个三⻆形是叠加的情况,油画算法将⽆法处理。
      • 在这里插入图片描述
看到了油画算法的弊端后,我们再来看看第二种解决办法:
  • 正背⾯剔除(Face Culling)
    • 3D图形里,对于一个立方体我们最多能看到的只有3面,而那另外的看不到的3面我们是不用去绘制的,而且当我们以某种方式去放弃绘制这些看不见的数据时,OpenGL的渲染性能可以提高50%以上。
    • 但是要如何知道哪里是背面哪里是正面呢?这就是上一篇说道的三角形的环绕方向。OpenGL 可以做到检查所有正⾯朝向观察者的⾯,并渲染它们。从⽽丢弃背⾯朝向的⾯,这样可以节约⽚元着⾊器的性能
    • 总之,正⾯和背⾯是有三⻆形的顶点定义顺序和观察者⽅向共同决定的。随着观察者的⻆度⽅向的改变,正⾯背⾯也会跟着改变

      开启表⾯剔除(默认背⾯剔除) :
      void glEnable(GL_CULL_FACE);
      关闭表⾯剔除(默认背⾯剔除) :
      void glDisable(GL_CULL_FACE);
      选择剔除那个⾯(正⾯/背⾯) :
      void glCullFace(GLenum mode);
      mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默认GL_BACK
      指定绕序那个为正⾯:
      void glFrontFace(GLenum mode);
      mode参数为: GL_CW,GL_CCW,默认值:GL_CCW
      例如,剔除正⾯实现(1) :
      glCullFace(GL_BACK);
      glFrontFace(GL_CW);
      例如,剔除正⾯实现(2) :
      glCullFace(GL_FRONT)

2. 深度

深度就是该像素点在3D世界中距离摄像机的距离,Z值
  • 深度缓冲区
    • 深度缓存区,就是⼀块内存区域,专⻔存储着每个像素点(绘制在屏幕上的)深度值。深度值(Z值)越⼤,则离摄像机就越远
  • 为什么需要深度缓冲区?
    • 在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物理,再绘制距离较远的物理,则距离远的位图因为后绘制,会把距离近的物体覆盖掉.。有了深度缓冲区后,绘制物体的顺序就不那么重要了。实际上,只要存在深度缓冲区,OpenGL都会把像素的深度值写⼊到缓冲区中。除⾮调⽤
      glDepthMask(GL_FALSE)来禁⽌写⼊
  • 深度测试
    • 深度缓冲区(DepthBuffer)和颜⾊缓存区(ColorBuffer)是对应的。颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息。在决定是否绘制⼀个物体表⾯时,⾸先要将表⾯对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较。如果⼤于深度缓冲区中的值,则丢弃这部分。否则利⽤这个像素对应的深度值和颜⾊值。分别更新深度缓冲区和颜⾊缓存区。这个过程称为”深度测试”

开启深度测试:
glEnable(GL_DEPTH_TEST);
在绘制场景前,清除颜⾊缓存区、深度缓冲区:
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清除深度缓冲区默认值为1.0,表示最⼤的深度值,深度值的范围为(0,1)之间。值越⼩表示越靠近观察者,值越⼤表示越远离观察者
指定深度测试判断模式:
void glDepthFunc(GLEnum mode);
在这里插入图片描述
打开/阻断 深度缓存区写⼊:
void glDepthMask(GLBool value);
value : GL_TURE 开启深度缓冲区写⼊; GL_FALSE 关闭深度缓冲区写⼊

  • ZFighting闪烁问题
    • 为什么会出现 ZFighting 闪烁问题?
    • 因为开启深度测试后,OpenGL就不会再去绘制模型被遮挡的部分。这样实现的显示更加真实。但是由于深度缓冲区精度的限制对于深度相差⾮常⼩的情况下。(例如在同⼀平⾯上进⾏2次 制),OpenGL 就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测。显示出来的
      现象时交错闪烁。的前⾯2个画⾯,交错出现。
      在这里插入图片描述
  • 解决⽅法
    • 第⼀步: 启⽤ Polygon Offset
    • 让深度值之间产⽣间隔。如果2个图形之间有间隔,是不是意味着就不会产⽣⼲涉。可以理
      解为在执⾏深度测试前将⽴⽅体的深度值做⼀些细微的增加。于是就能将重叠的2个图形深度值之
      前有所区分

      //启⽤Polygon Offset ⽅式
      glEnable(GL_POLYGON_OFFSET_FILL)
      参数列表:
      GL_POLYGON_OFFSET_POINT 对应光栅化模式: GL_POINT
      GL_POLYGON_OFFSET_LINE 对应光栅化模式: GL_LINE
      GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL

    • 第⼆步: 指定偏移量
    • 通过glPolygonOffset 来指定glPolygonOffset 需要2个参数: factor ,units
    • 每个Fragment 的深度值都会增加如下所示的偏移量:

      Offset = ( m * factor ) + ( r * units);
      m : 多边形的深度的斜率的最⼤值,理解⼀个多边形越是与近裁剪⾯平⾏,m 就越接近于0
      r : 能产⽣于窗⼝坐标系的深度值中可分辨的差异最⼩值,r 是由具体OpenGL 平台指定的⼀个常量

    • ⼀个⼤于0的Offset 会把模型推到离你(摄像机)更远的位置,相应的⼀个⼩于0的Offset会把模型拉近
    • ⼀般⽽⾔,只需要将-1.0和-1这样简单赋值给glPolygonOffset基本可以满⾜需求

      void glPolygonOffset(Glfloat factor,Glfloat units);
      应⽤到⽚段上总偏移计算⽅程式:
      Depth Offset = (DZ * factor) + (r * units);
      DZ:深度值(Z值)
      r:使得深度缓冲区产⽣变化的最⼩值
      负值,将使得z值距离我们更近,⽽正值,将使得z值距离我们更远,

    • 第三步: 关闭Polygon Offset

      glDisable(GL_POLYGON_OFFSET_FILL)

3. 裁剪

  • 在OpenGL中提⾼渲染性能的⼀种⽅式,只刷新屏幕上发⽣变化的部分,OpenGL 允许将要进⾏渲染的窗⼝只去指定⼀个裁剪框。
  • ⽤于渲染时限制绘制区域,通过此技术可以在屏幕(帧缓冲)指定⼀个矩形区域。启⽤剪裁
    测试之后,不在此矩形区域内的⽚元被丢弃,只有在此矩形区域内的⽚元才有可能进⼊帧缓冲。因此实
    际达到的效果就是在屏幕上开辟了⼀个⼩窗⼝,可以再其中进⾏指定内容的绘制

    //1 开启裁剪测试
    glEnable(GL_SCISSOR_TEST);
    //2.关闭裁剪测试
    glDisable(GL_SCISSOR_TEST);
    //3.指定裁剪窗⼝
    void glScissor(Glint x,Glint y,GLSize width,GLSize height);
    x,y:指定裁剪框左下⻆位置;
    width , height:指定裁剪尺⼨

  • 理解窗⼝,视⼝,裁剪区域
    • 窗口: 就是显示界面
    • 视口: 就是窗口中用来显示图形的一块矩形区域,它可以和窗口等大,也可以比窗口大或者小。只有绘制在视口区域中的图形才能被显示,如果图形有一部分超出了视口区域,那么那一部分是看不到的。通过glViewport()函数设置。
    • 裁剪区域:就是视口矩形区域的最小最大x坐标(left,right)和最小最大y坐标(bottom,top),而不是窗口的最小最大x坐标和y坐标。通过glOrtho()函数设置,这个函数还需指定最近最远z坐标,形成一个立体的裁剪区域。

4. 混合

  • 我们把OpenGL 渲染时会把颜色值存在颜色缓存区中,每个片段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜色将简单的覆盖原来颜色缓存区存在的颜色值,当深度缓冲区再次打开时,新的颜色片段只是当它们比原来的值更接近邻近的裁剪平面才会替换原来的颜色片段。

    glEnable(GL_BlEND);

  • 目标颜色:已经存储在颜色缓存区的颜色值

  • 源颜色:作为当前渲染命令结果进入颜色缓存区的颜色值

  • 当混合功能被启动时,源颜色和目标颜色的组合方式是混合方程式控制的。在默认情况下,混合方程式如下所示:

    Cf = (Cs * S) + (Cd * D)
    Cf :最终计算参数的颜色
    Cs : 源颜色
    Cd :目标颜色
    S:源混合因子
    D:目标混合因子
    设置混合因子,需要用到glBlendFun函数
    glBlendFunc(GLenum S,GLenum D);
    在这里插入图片描述
    表中R、G、B、A 分别代表 红、绿、蓝、alpha。
    表中下标S、D,分别代表源、目标
    表中C 代表常量颜色(默认黑色)

  • 下面通过一个常⻅的混合函数组合来说明问题:

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    如果颜色缓存区已经有一种颜色红色(1.0f,0.0f,0.0f,0.0f),这个是目标颜色Cd,如果在这上面用一种alpha为0.6的蓝色(0.0f,0.0f,1.0f,0.6f)
    Cd (目标颜色) = (1.0f,0.0f,0.0f,0.0f);
    Cs (源颜色) = (0.0f,0.0f,1.0f,0.6f);
    S = 源alpha值 = 0.6f
    D = 1 - 源alpha值= 1- 0.6f = 0.4f
    方程式Cf = (Cs * S) + (Cd * D)
    等价于 = (Blue * 0.6f) + (Red * 0.4f)

  • 最终颜色是以原先的红色(目标颜色)与 后来的蓝色(源颜色)进行组合。源颜色的alpha值越高,添加的蓝色颜色成分越高,目标颜色所保留的成分就会越少。
    混合函数经常用于实现在其他一些不透明的物体前面绘制一个透明物体的效果。

  • 实际上远不止上面介绍的一种混合方程式,我们可以从5个不同的方程式中进行选择

    选择混合方程式的函数:
    glbBlendEquation(GLenum mode);
    在这里插入图片描述

  • 除了能使用glBlendFunc 来设置混合因子,还可以有更灵活的选择。

    void glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB ,GLenum srcAlpha,GLenum dstAlpha);
    srcRGB: 源颜色的混合因子
    dstRGB: 目标颜色的混合因子
    srcAlpha: 源颜色的Alpha因子
    dstAlpha: 目标颜色的Alpha因子

    • glBlendFunc指定源和目标 RGBA值的混合函数,但是glBlendFuncSeparate函数则允许为RGB和Alpha 成分单独指定混合函数。
    • 在混合因子表中,GL_CONSTANT_COLOR,GL_ONE_MINUS_CONSTANT_COLOR,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT值允许混合方程式中引入一个常量混合颜色。
    • 常量混合颜色,默认初始化为黑色(0.0f,0.0f,0.0f,1.0f),但是还是可以修改这个常量混合颜色。

      void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值