OpenGL 渲染常见问题

常见问题

隐藏面消除

在绘制3D场景的时候,我们需要决定哪些部分是对观察者可⻅的,或者哪些部分是对观察者不可⻅的.对于不可⻅的部分,应该及早丢弃.例如在⼀个不透明的墙壁后,就不应该渲染.这种情况叫做”隐藏⾯消除”(Hidden surface elimination).


image.png
解决方案
  • 油画法
    先绘制场景中的离观察者较远的物体,再绘制较近的物体
    如下图,先绘制红⾊部分,再绘制⻩⾊部分,最后再绘制灰⾊部分,即可解决隐藏⾯消除的问题


    image.png

    油画法弊端:如果三个三⻆形是叠加的情况,油画算法将⽆法处理


    image.png
  • 正背面剔除(Face Culling)
    任何平⾯都有2个⾯,正⾯/背⾯.意味着你⼀个时刻只能看到⼀⾯
    OpenGL 可以做到检查所有正⾯朝向观察者的⾯,并渲染它们.从⽽丢弃背⾯朝向的⾯. 这样可以节约⽚元着⾊器的性能.
    正⾯/背⾯区分:
    • 正⾯: 按照逆时针顶点连接顺序的三⻆形⾯
    • 背⾯: 按照顺时针顶点连接顺序的三⻆形⾯


    image.png
// 开启表⾯剔除(默认背⾯剔除) 
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);
  • 深度测试
    • 什么是深度?
    深度其实就是该像素点在3D世界中距离摄像机的距离,Z值
    • 什么是深度缓冲区?
    深度缓存区,就是⼀块内存区域,专⻔存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z值)越⼤,则离摄像机就越远.
    • 为什么需要深度缓冲区?
    在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物理,再绘制距离较远的物理,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么重要的. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中,除⾮调⽤glDepthMask(GL_FALSE).来禁⽌写⼊.
    • 深度测试:
    深度缓冲区(DepthBuffer)和颜⾊缓存区(ColorBuffer)是对应的.颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息. 在决定是否绘制⼀个物体表⾯时, ⾸先要将表⾯对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利⽤这个像素对应的深度值和颜⾊值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试”
  • 使用深度测试
    深度缓冲区,⼀般由窗⼝管理系统,GLFW创建.深度值⼀般由16位,24位,32位值表示. 通常是24位.位数越⾼,深度精确度更好.
// 开启深度测试
glEnable(GL_DEPTH_TEST);
// 在绘制场景前,清除颜⾊缓存区,深度缓冲
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 打开或关闭深度缓冲区
// GL_TURE 开启深度缓冲区写⼊; GL_FALSE 关闭深度缓冲区写⼊
void glDepthMask(GLBool value);
// 指定深度测试判断模式
void glDepthFunc(GLEnum mode);

判断模式glDepthFunc可以传如下表所示参数,默认为GL_LESS


image.png
  • ZFighting闪烁问题
    因为开启深度测试后,OpenGL 就不会再去绘制模型被遮挡的部分. 这样实现的显示更加真实.但是由于深度缓冲区精度的限制对于深度相差⾮常⼩的情况下.(例如在同⼀平⾯上进⾏2次制),OpenGL 就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测.显示出来的现象时交错闪烁.的前⾯2个画⾯,交错出现.


    image.png

解决:
让深度值之间产⽣间隔.可以理解为在执⾏深度测试前将⽴⽅体的深度值做⼀些细微的增加.于是就能将重叠的2个图形深度值之前有所区分.

//第⼀步: 启⽤ Polygon Offset ⽅式解决
//参数列表: 
//GL_POLYGON_OFFSET_POINT 对应光栅化模式: GL_POINT 
//GL_POLYGON_OFFSET_LINE 对应光栅化模式: GL_LINE 
//GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL
glEnable(GL_POLYGON_OFFSET_FILL)
//第⼆步: 指定偏移量
// ⼀般⽽⾔,只需要将-1.0 和 0.0 这样简单赋值给glPolygonOffset 基本可以满⾜需求.
// Depth Offset = (DZ(深度) * factor) + (r(变化的最⼩值) * units);
void glPolygonOffset(Glfloat factor,Glfloat units);
//第三步: 关闭Polygon Offset
glDisable(GL_POLYGON_OFFSET_FILL)

ZFighting闪烁问题预防
• 不要将两个物体靠的太近,避免渲染时三⻆形叠在⼀起。这种⽅式要求对场景中物体插⼊⼀个少量的偏移,那么就可能避免ZFighting现象。例如上⾯的⽴⽅体和平⾯问题中,将平⾯下移0.001f就可以解决这个问题。当然⼿动去插⼊这个⼩的偏移是要付出代价的。
• 尽可能将近裁剪⾯设置得离观察者远⼀些。上⾯我们看到,在近裁剪平⾯附近,深度的精确度是很⾼的,因此尽可能让近裁剪⾯远⼀些的话,会使整个裁剪范围内的精确度变⾼⼀些。但是这种⽅式会使离观察者较近的物体被裁减掉,因此需要调试好裁剪⾯参数。
• 使⽤更⾼位数的深度缓冲区,通常使⽤的深度缓冲区是24位的,现在有⼀些硬件使⽤使⽤32位的缓冲区,使精确度得到提⾼

裁剪

在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坐标,形成⼀个⽴体的裁剪区域。

混合(透明叠加时,颜色需要混合)

混合函数经常⽤于实现在其他⼀些不透明的物体前⾯绘制⼀个透明物体的效果

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

// 开启混合
glEnable(GL_BlEND);
  • 组合颜⾊
    ⽬标颜⾊:已经存储在颜⾊缓存区的颜⾊值
    源颜⾊:作为当前渲染命令结果进⼊颜⾊缓存区的颜⾊值
    当混合功能被启动时,源颜⾊和⽬标颜⾊的组合⽅式是混合⽅程式控制的。在默认情况下,混合⽅程式如下所示:Cf = (Cs * S) + (Cd * D)
    Cf :最终计算参数的颜⾊
    Cs : 源颜⾊
    Cd :⽬标颜⾊
    S:源混合因⼦
    D:⽬标混合因⼦
// 设置混合因子
glBlendFunc(GLenum S,GLenum D);
// 常见的组合
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
image.png

表中R、G、B、A 分别代表 红、绿、蓝、alpha。
表中下标S、D,分别代表源、⽬标
表中C 代表常量颜⾊(默认⿊⾊)

  • 可以选择混合方程式
// 选择混合方程式
glbBlendEquation(GLenum mode);
image.png
  • 混合因子的灵活设置
    除了能使⽤glBlendFunc 来设置混合因⼦,还可以有更灵活的选择
// strRGB: 源颜⾊的混合因⼦
//dstRGB: ⽬标颜⾊的混合因⼦
//strAlpha: 源颜⾊的Alpha因⼦
//dstAlpha: ⽬标颜⾊的Alpha因⼦
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB,GLenum strAlpha,GLenum dstAlpha);

glBlendFunc 指定 源和⽬标 RGBA值的混合函数;但是glBlendFuncSeparate函数则允许为RGB 和Alpha 成分单独指定混合函数

在混合因⼦表中,
GL_CONSTANT_COLOR,GL_ONE_MINUS_CONSTANT_COLOR,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT值允许混合⽅程式中引⼊⼀个常量混合颜⾊。

//修改常量混合颜色
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue,GLclampf alpha );
  • 混合处理-抗锯齿
    出现锯齿的原因:每个像素都是一个一个的小正方形,当物体的边界出现颜色分明的变化时,正方形的像素就会出现在分割线的两边,形成锯齿。
//开启混合处理,将锯齿进行颜色混合
glEnable(GL_BLEND);
//指定混合因⼦
GLBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//指定混合⽅程式
glBlendEquation(GL_FUNC_ADD); 
//对点进⾏抗锯⻮处理
glEnable(GL_POINT_SMOOTH); 
//对线进⾏抗锯⻮处理
glEnable(GL_LINE_SMOOTH);
//对多边形进⾏抗锯⻮处理
glEnable(GL_POLYGON_SMOOTH);
  • 多重采用- 抗锯齿
// 1.可以调⽤ glutInitDisplayMode 添加采样缓存区
glutInitDisplayMode(GLUT_MULTISAMPLE);
//2. 打开|关闭 多重采样。
glEnable(GLUT_MULTISAMPLE); 
glDisable(GLUT_MULTISAMPLE);
// 混合,多重采样组合
 glDisable(GLUT_MULTISAMPLE); 
 glEnable(GL_POINT_SMOOTH); 
 //Draw some smooth point 
 glDisable(GL_POINT_SMOOTH); 
 glDisable(GL_LINE_SMOOTH); 
 glEnable(GLUT_MULTISAMPLE);
//Draw some smooth polygon

多重采样缓存区在默认情况下使⽤⽚段RGB值,并不包含颜⾊的alpha成分,我们可以通过调⽤glEnable来修改这个⾏为:

  • GL_SAMPLE_ALPHA_TO_COVERAGE 使⽤alpha值
  • GL_SAMPLE_ALPHA_TO_ON 使⽤alpha值并设为1,并使⽤它。
  • GL_SAMPLE_COVERAGE 使⽤glSampleCoverage所设置的值。
    当启⽤ GL_SAMPLE_COVERAGE 时,可以使⽤glSampleCoverage函数允许指定⼀个特定的值,它是与⽚段覆盖值进⾏按位与操作的结果。

(本文为学习笔记,图片来自cc老师)

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

推荐阅读更多精彩内容