OpenGL中一种高效的线段反走样技术

令人讨厌的“走样”

        我在日常工作中通过传统的OpenGL绘制函数绘制线段时,发现绘制出的线段边缘充满了“锯齿”,而这种“锯齿”在线段运动和旋转时往往会更加明显(图 1)。这种我们不希望看到的“锯齿”被成为“走样”,而消除这种“锯齿”的过程就是我们所说的“反走样”。虽然OpenGL提供了诸如设置 GL_LINE_SMOOTH 属性、多重采样等线段反走样的方法,但效果和质量受到很多方面的限制,而且不同的硬件厂商使用不同的反走样算法,所以使得反走样的结果在不同的GPU上有 着不同的效果。因此我们需要一种更为高效和通用的线段反走样技术。

图1  采用传统OpenGL绘制方法绘制的线段

为什么会“走样”?

      在介绍如何对线段反走样之前,我们必须了解为什么我们绘制的线段会产生“走样”。

      我们都知道,在数学的定义中一条线段是由两个端点确定的,而线段是没有宽度和面积的。但在计算机图形领域中,为了让人的肉眼能够看到,必须给线段一定的宽 度,所以我们的线段通常是由两个端点和一个宽度参数确定的,而我们计算机中图形的宽度通常都是以像素为单位的,因此我们的线段宽度有可能是1像素也有可能 是n像素。

        如果需要在白色的背景下绘制一条宽度为1像素的黑色线段,从信号处理的观点上来看,我们可以把这条线段看做一个值为1的信号,而线段外部的区域信号值为0,如果不加任何处理,线段的边界就是这样一个不连续的阶梯函数(图2)。 因为帧缓存和显示器所能容纳的像素点是有限的,所以我们需要对这个信号进行采样。

图2 线段信号采样示意图

       我们可以看到:离散采样(图2 中用蓝色虚线表示)的间隔无论多么的小都无法精确的表达它的不连续性,因此我们无论怎么提高分辨率,都无法彻底消除走样。而根据耐奎斯特的信号采样定理:要重构一个不走样的信号,采样率至少是信号最高频率的两倍。

                                                 即:C = B * log2 N ( bps )

       因此,从理论上来说要绘制一条没有走样的线段,我们必须拥有足够大的信号频率,也就是我们需要无限放大我们的屏幕分辨率才能彻底消除走样。

图3 通过提高分辨率减轻走样现象

       从图3我们可以看出,虽然我们提高了分辨率,但是走样依然存在。因此,一味地提高分辨率是无法彻底解决掉线段走样问题的,而且在时间、空间以及金钱有限的情况下是不允许我们这么做的。

解决之道

      计算机图形学领域中广泛采用的一种方法是:限制信号的带宽。也就是说既然无法提高分辨率,我们可以将信号中无法还原的高频部分去掉以达到“反走样”的目的。这样线段就不再有明显尖锐的边界了,相反,线段的边界处将变得模糊,这种将边界模糊的过程我们称之为“过滤”。我们可以让信号通过一个低通过滤器,来过滤掉信号中的高频部分,以达到过滤的效果,这样的过滤器有很多,可以是简单的线性过滤器也可以是稍微复杂一点的盒状过滤器或高斯过滤器。本文将以高斯过滤器为例,为大家介绍整个过滤的过程。       

图4 低通高斯过滤器对2D信号的过滤效果

       图4演示了高斯过滤器对一个2D信号进行过滤操作的整个过程,首先图4(a)表示未处理的线段信号,其中x和y轴表示线段所处平面坐标系,z轴表示图像信号的强度,可以认为是RGBA颜色中的alpha值。其中左半部分z=1表示位于线段内部,右半部份z=0表示位于线段外部,这里z=0和z=1边界处是不连续的。图4(b)所表示的是一个高斯地同过滤器,将它与图4(a)中的某一段信号做卷积后就得到了图4(c)中的效果。卷积在这里等效于求出过滤器与信号相交部分的体积,图4(d)就是将所有信号与过滤器卷积后得到的最终过滤效果。

图5 经过半径为2的高斯过滤后的线段信号示意图

       从图5可以看到:经过过滤后的线段边界将不再是一段不连续的阶梯函数,而是一段连续的平滑曲线。

预处理

      如图6所示,在预处理过程中,我们将半径为R的过滤器和宽度为W的线段进行卷积,所得到的强度值根据过滤器的位置变化而变化。当过滤器刚好位于直线上(图6a)时,我们得到的强度值最大,因为此时过滤器与直线重叠部分最多,(在图4所示坐标系中)重叠部分的体积也就越大。相反的,当过滤器位于距离直线w/2+R的位置(图6b)时,卷积所得强度值最小,因为此时过滤器与直线没有重叠。而在过滤器从距离为0移动到w/2+R处的过程中,强度值在慢慢变小。

图6 过滤器位置影响卷积值

       然而,我们并不希望计算量会随线段宽度变化,我们希望我们的渲染过程的效率是稳定的,因此,我们需要一张固定宽度的查找表。通过实践发现,一张32个强度值的查找表已经足够应付任意宽度的直线了(图7),如果觉得这样不够精细,你还可以使用64个强度值的查找表,因为对于GPU来说,处理一个32或64元素的1D纹理实在是小菜一碟。

图7 32个强度值的查找表

      如图8中的代码片段所示,生成这样一个纹理只需按照设定的强度值数量利用过滤器计算出相应数量的强度值就可以了。唯一需要注意的是这个纹理是关于直线中心对称的,以及纹理参数中缩放过滤参数要设置为GL_NEAREST。

图8 生成一张64个强度值查找表的过程

运行时

      预处理只需要在CPU中运行一次,而当我们将过滤后的纹理完成后,我们的预处理工作就算告一段落,接下来就可以进行渲染了。渲染时,我们需要进行两种计算,一种是在CPU中的线段相关参数的计算,另一种计算GPU的着色器中进行的,主要是利用CPU提供的参数在顶点着色器和片段着色器中计算出真正的位置和颜色。

图9 将线段端点沿两侧法向量平移w/2距离后得到矩形4个顶点

      计算矩形顶点的坐标看起来也不是一件很困难的事情,只需要将线段的两个顶点向两侧分别平移w/2距离就可以得到(图9),而线段的平移方向正好是xy平面上垂直于该线段的法向量方向,因此我们只需要计算这个法向量即可。

图10 顶点着色器

      有了法向量后,顶点着色器中只需将顶点和法向量相乘,再乘上w/2就可以得到平移后的顶点位置,最后再与线段的模型视图投影矩阵相乘,计算出最终的顶点位置(图10)。


图11 片段着色器

      片段着色器只需对纹理进行一次采样得到强度值再与线段颜色进行一次叠加就行了,这样就能得到一条任意颜色的线段。

最终效果

图12 过滤后线段边缘变得模糊

      并且对它进行拉伸或者旋转都不会产生新的走样(图13)。

图13(a) 经过反走样处理的线段拉伸效果图
图13(b) 反走样后线段旋转效果图

      通过图13的对比我们可以清楚地看出,经过预过滤反走样处理的线段相比普通线段和硬件反走样处理的线段锯齿感明显要弱了许多。这种处理方式所需的存储空间代价仅仅是额外的两个顶点和一个宽度64的一维纹理,而运行时处理上也只是增加了一次法向量的计算,可以称得上是简单高效。

图13(c) 各种反走样线段效果:
从左至右依次为 普通线段 默认硬件反走样 盒状过滤器 高斯过滤器

      最后,希望这个方法能够对大家处理2D线段抗锯齿问题能够有所帮助,如果对有对这方面研究感兴趣的朋友,欢迎加入我们进行讨论(QQ群:280689979)。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 2016年6月7日,离我参加高考的日子,已过去了整整十年。 十年之前,是一个普通的中学生; 十年之后,是一个普通的...
    励新阅读 402评论 0 0
  • 当我在朋友圈转发了一篇关于寝室夜谈的文章并留下“大四寝室凑不齐咯”后,班长留言说,你们寝室大三就聚不齐了。心里咯噔...
    陈啊飘阅读 269评论 0 0
  • 我住在写字楼里,隔壁的聚会里一帮小年轻吵吵闹闹的,我无法入睡。起身穿衣拖鞋带手机离开办公室,走到隔壁聚会屋...
    简一亾阅读 408评论 2 0