OpenGL 隐藏面消除及混合

在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见,或者哪些部分是对观察者不可见的。对于不可见的部分,就不应该渲染。这种情况叫做隐藏面消除(Hidden surface elimination)。

  • 正背面剔除(Face Culling)

    • 默认情况下,按照逆时针顶点连接顺序的三角形面为正面

    • 默认情况下,按照顺时针顶点连接顺序的三角形面为背面

    • 开启正背面剔除(默认背面剔除)

      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
      
  • 深度缓冲区

    • 专门存储每个像素点的深度值,深度值越大,则离摄像机越远。
    • 在不使用深度测试的时候,如果我们先绘制一个距离比较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制而把距离近的物体覆盖掉。有了深度缓冲区后,绘制物体的顺序就不那么重要的。只要存在深度缓冲区,OpenGL都会把像素的深度值写入到缓冲区中。除非调用glDepthMask(GL_FALSE)来禁止写入。
  • 深度测试

    • 深度缓冲区(DepthBuffer)和颜色缓冲区(ColorBuffer)是对应的。

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

    • 深度值一般由16位、24位、32位值表示。通常是24位,位数越高,深度精确度更好。

    • 开启深度测试

      glEnable(GL_DEPTH_TEST)
      
    • 清除深度缓冲区(默认值为1.0,表示最大的深度值,深度值的范围为[0, 1]之间,值越小表示越靠近观察者,值越大表示越远离观察者)

      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
      
    • 深度测试判断模式

      glDepthFunc(GLEnum mode)
      
      枚举值 描述
      GL_NEVER 总是不能通过测试
      GL_ALWAYS 总是通过测试
      GL_LESS 当前深度值 < 存储的深度值时通过
      GL_LEQUAL 当前深度值 <= 存储的深度值时通过
      GL_EQUAL 当前深度值 = 存储的深度值时通过
      GL_GEQUAL 当前深度值 >= 存储的深度值时通过
      GL_GREATER 当前深度值 > 存储的深度值时通过
      GL_NOTEQUAL 当前深度值 != 存储的深度值时通过
    • 打开/阻断 深度缓冲区写入

      glDepthMask(GLboolean flag)
      flag: GL_TRUE 开启深度缓冲区写入,GL_FALSE 关闭深度缓冲区写入
      
  • ZFighting闪烁问题

    • 由于深度缓冲区精度的限制,对于深度值相差非常小的情况,OpenGL就可能出现不能正确判断两者的深度值,就会导致深度测试的结果不可预测,显示出来的现象就是交错闪烁两个画面。

    • 启用 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(GLFloat factor, GLFloat units)
      
      应用到片段上总偏移计算方程式:
      Depth Offset = (Dz * factor) + (r * units)
      Dz: 深度值
      r: 使得深度缓冲区产生变化的最小值
      
      可以设置factor和units为-1,-1。
      
    • 关闭 Polygon Offset

      glDisable(GL_POLYGON_OFFSET_FILL)
      
  • 裁剪

    • 用于渲染时限制绘制区域。

    • 开启裁剪

      glEnable(GL_SCISSOR_TEST)
      
    • 关闭裁剪

      glDisable(GL_SCISSOR_TEST)
      
    • 指定裁剪窗口

      void glScissor(GLint x, GLint y, GLsize width, GLsize height)
      
    • 窗口、视口、裁剪区域

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

      • 开启混合

        glEnable(GL_BLEND)
        
      • 组合颜色
        目标颜色:已经存储在颜色缓存区的颜色值
        源颜色:作为当前渲染命令结果进入颜色缓存区的颜色值
        在默认情况下,混合方程式如下:

        Cf = (Cs * S) + (Cd * D)
        Cf: 结果颜色
        Cs: 源颜色
        Cd: 目标颜色
        S: 源混合因子
        D: 目标混合因子
        
      • 设置混合因子

        glBlendFunc(GLenum S, GLenum D)
        S: 源混合因子
        D: 目标混合因子
        
        枚举值 RGB混合因子 AlPha混合因子
        GL_ZERO (0,0,0) 0
        GL_ONE (1,1,1) 1
        GL_SRC_COLOR (Rs,Gs,Bs) As
        GL_ONE_MINUS_SRC_COLOR (1,1,1)-(Rs,Gs,Bs) 1-As
        GL_DST_COLOR (Rd,Gd,Bd) Ad
        GL_ONE_MINUS_DST_COLOR (1,1,1)-(Rd,Gd,Bd) 1-Ad
        GL_SRC_ALPHA (As,As,As) As
        GL_ONE_MINUS_SRC_ALPHA (1,1,1)-(As,As,As) 1-As
        GL_DST_ALPHA (Ad,Ad,Ad) Ad
        GL_ONE_MINUS_DST_ALPHA (1,1,1)-(Ad,Ad,Ad) 1-Ad
        GL_CONSTANT_COLOR (Rc,Gc,Bc) Ac
        GL_ONE_MINUS_CONSTANT_COLOR (1,1,1)-(Rc,Gc,Bc) 1-Ac
        GL_CONSTANT_ALPHA (Ac,Ac,Ac) Ac
        GL_ONE_MINUS_CONSTANT_ALPHA (1,1,1)-(Ac,Ac,Ac) 1-Ac
        GL_SRC_ALPHA_SATURATE (f,f,f)*f=min(As,1-Ad) 1

        表中R、G、B、A分别表示红、绿、蓝、alpha
        下标s、d,分别表示源、目标
        表中c代表常量颜色(默认黑色)

      • 改变混合方程式

        glBlendEquation(GLenum mode)
        
        枚举值 RGB混合因子
        GL_FUNC_ADD Cf=(Cs * S) + (Cd * D)
        GL_FUNC_SUBTRACT Cf=(Cs * S) - (Cd * D)
        GL_FUNC_REVERSE_SUBTRACT Cf=(Cd * D) - (Cs * S)
        GL_MIN Cf=min(Cs,Cd)
        GL_MAX Cf=max(Cs,Cd)
      • 设置混合因子(RGB和Alpha分开设置)

        void glBlendFuncSeparate(GLenum strRGB, GLenum dstRGB, GLenum strAlpha, GLenum dstAlpha)
        
      • 设置常量混合颜色

        void glBlendColor(GLclampf red, GLclampf green, GLclamf blue, GLclampf alpha)
        
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,406评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,976评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,302评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,366评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,372评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,457评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,872评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,521评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,717评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,523评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,590评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,299评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,859评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,883评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,127评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,760评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,290评论 2 342

推荐阅读更多精彩内容