一、走样与反走样

走样(Aliasing)就是锯齿化,反走样(Anti-aliasing)就是抗锯齿

只要玩过游戏,那么都应该对抗锯齿不陌生,不少游戏也都有关于抗锯齿的设置

如上图,放大的部分能很明显的看到“锯齿”边,如果了解光栅化的过程,那么也很容易理解锯齿是怎样产生的,这不是什么底层的BUG,正是完全正确的流程会出现这中“锯齿”现象,本质原因是场景的定义在三维空间中是连续的,而最终显示的像素则却是一个离散的二维数组,所以在判断一个点到底没有被某个像素覆盖的时候不应该单纯是一个“有”或者“没有"问题,也因此,抗锯齿一定是采用优化手段而无法根治

二、采样

(参考于learnopengl.com)

光栅化将属于一个基本图形的所有顶点转化为一系列片段,顶点坐标理论上可以含有任何坐标,但片段却不是这样,因为它与你的窗口的解析度有关,几乎永远都不会有顶点坐标和片段的一对一映射,所以光栅化必须以某种方式决定每个特定顶点最终结束于哪个片段/屏幕坐标上

每个像素中心会包含一个采样点(sample point),它被用来决定一个像素是否被三角形所覆盖,红色的采样点如果在三角形内部,那么就会为这个被覆盖像素生成一个片段,否则就算三角形覆盖了部分屏幕像素,只要采样点没被覆盖这个像素就不会被处理

如果你的顶点组成的线段刚好与屏幕平行,那么这个时候就会好很多,但事实上只要出现斜边,就必然出现上图的情况

为了解决,一个很容易想到的方法是:每个像素不再只有中心一个采样点,而是设置多个采样点,假设最终有x%个采样点被覆盖,那么这个像素的颜色就按照对应比例x%进行平均化

三、MSAA

上面所说的方法正是多重采样抗锯齿(MultiSampling Anti-Aliasing),也是最常用的抗锯齿算法,使用n个采样点意味着需要n倍的颜色缓冲区空间,但是对于这n个采样点,仍然只需要执行一次着色器,着色器使用的顶点数据会通过插值锁定在像素的中间,然后在计算最终颜色的时候乘上覆盖率,若执行多次着色器,会很显著的降低性能

上图就是MSAA优化后的效果,其实采样点数量是可以任意指定的,不过如果你的分辨率过低又或者采样点过少,就会产生一个新的问题:边缘模糊

好在是,这些东西GPU已经帮我们做了,如果是在openGL中进行最简单的应用,只需要添加2行代码就ok:

  • glfwWindowHint(GLFW_SAMPLES, n):提示GLFW,希望使用一个带有n个样本的多样本缓冲(Multisample Buffer)以代替普通的颜色缓冲,需要在创建窗口前完成
  • glEnable(GL_MULTISAMPLE):开启多采样,一般是默认开启

再看看效果,应该就是没问题了

四、多采样缓冲

前置:OpenGL基础33:帧缓冲之离屏渲染

很可惜,如果用了自己的帧缓冲,那么仅用上面的2行代码就不可以了,GLFW并不会对你自己创建的缓冲负责,在这种情况下就需要自己生成多采样缓冲以实现MSAA

可以参考前置章节,用同样的方法(纹理附件和渲染缓冲附件)创建多采样缓冲,并使其成为帧缓冲的附件,主要逻辑是在中间插入一个新的FBO,专门用于用于处理抗锯齿,之后再转入之前用于显示的FBO中进行我们想要的后处理:

  • glTexImage2DMultisample:创建一个支持储存多采样点的纹理,第2个参数现在设置了纹理拥有的样本数,如果最后一个参数等于GL_TRUE,图像上的每一个纹理像素(texel)都会使用相同的样本位置,以及同样的子样本数量
  • glRenderbufferStorageMultisample:和之前的glRenderbufferStorage方法一样,缓冲目标后面多了一个参数,为样本数量
  • GL_TEXTURE_2D_MULTISAMPLE:多采样目标纹理,之前都是GL_TEXTURE_2D
GLuint FBO, RBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);GLuint textureColorBufferMultiSampled = getMultiSampleTexture(4);           //MSAA 4x抗锯齿
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);glGenRenderbuffers(1, &RBO);
glBindRenderbuffer(GL_RENDERBUFFER, RBO);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);GLuint screenFBO;
glGenFramebuffers(1, &screenFBO);
glBindFramebuffer(GL_FRAMEBUFFER, screenFBO);
GLuint textureColorBuffer = getAttachmentTexture();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorBuffer, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)cout << "ERROR::FRAMEBUFFER:: Intermediate framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);GLuint getMultiSampleTexture(GLuint samples)
{GLuint texture;glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, WIDTH, HEIGHT, GL_TRUE);glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);return texture;
}

渲染到多采样帧缓冲对象是自动的,只要我们在帧缓冲绑定时绘制任何东西,光栅器就会负责所有的多重采样运算。我们最终会得到一个多重采样颜色缓冲以及/或深度和模板缓冲,不过多采样缓冲有点特别,我们不能为其他操作直接使用它们的缓冲图像,比如在着色器中进行采样

当然我们其实根本不需要采样图象,也不需要拿多采样缓冲来做什么,只要底层帮我们解决,那么剩下的只需要还原图像就好,也就是我们要将多重采样缓冲位块传送到一个没有使用多重采样纹理附件的FBO中,然后用这个普通的颜色附件来做后期处理,从而达到我们实际的目的:

  • glBlitFramebuffer:把一个4屏幕坐标源区域传送(Blitting)到一个也是4空间坐标的目标区域,其中源缓冲为GL_READ_FRAMEBUFFER绑定的目标,目标缓冲为GL_DRAW_FRAMEBUFFER绑定的缓冲,前4个参数为指定读帧缓冲区的矩形范围,第5-8个参数为指定写帧缓冲区的矩形范围,第9个参数为指定要读取的缓冲区,第10个参数为指定伸缩变形时的插值方法
glBindFramebuffer(GL_READ_FRAMEBUFFER, FBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, screenFBO);
glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
shaderScreen.Use();
glBindVertexArray(quadVAO);
glBindTexture(GL_TEXTURE_2D, textureColorBuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);

这样就可以顺利的使用之前所说的kernel过滤器了

五、扩展

其它抗锯齿技术:

上面详细介绍了MSAA,也就是多采样抗锯齿,事实上,抗锯齿方法还有很多,效率和效果也都不太一样:

  • SSAA:超级采样抗锯齿,使用比正常分辨率更高的分辨率来渲染场景,当图像输出在帧缓冲中更新时,分辨率再被下采样(Downsample)至正常的分辨率,性能巨耗,但是效果最好(理论最完美解决方法)
  • FXAA:快速近似抗锯齿,相对于MSAA速度更快、显存占用更低,但是效果也更差,可以配合锐化使用
  • CSAA:覆盖采样抗锯齿,原理是将边缘多边形里需要采样的子像素坐标覆盖掉,将原像素坐标强制安置在硬件和驱动程序预告算好的坐标中,这就好比采样标准统一的MSAA,能够最高效率地执行边缘采样,当然现在很多时候已经不再支持CSAA了,也逐渐冷门
  • FSAA:全屏抗锯齿,SSAA的特殊考虑

自定义抗锯齿算法:

因为屏幕纹理重新变回了只有一个采样点的普通纹理,有些后处理过滤器,比如边检测(edge-detection)将会再次导致锯齿边问题,为了修正此问题,往往要对纹理进行模糊处理,又或者创建自己的抗锯齿算法。将一个多重采样的纹理图像不进行还原直接传入着色器也是可行的,GLSL提供了这样的选项,以让我们能够对纹理图像的每个子样本进行采样

要想获取每个子样本的颜色值,需要将纹理uniform采样器设置为sampler2DMS,而不是平常使用的sampler2D:

  • texelFetch:获取每个子样本的颜色值,注意样本标签从0开始
uniform sampler2DMS screenTextureMS;
void main()
{vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3);
}

OpenGL基础43:抗锯齿相关推荐

  1. OpenGL快速近似抗锯齿FXAA

    OpenGL快速近似抗锯齿FXAA 先上图,再解答. 正常显示 按下空格键 完整主要的源代码 源代码剖析 先上图,再解答. 正常显示 按下空格键 完整主要的源代码 #

  2. OpenGL核心技术之抗锯齿

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

  3. OpenGL学习笔记一之高级OpenGL篇十一 抗锯齿

    转载自 https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/11%20Anti%20Aliasing/ 在学习渲染的旅途中,你可能会时不时遇 ...

  4. 【我的OpenGL学习进阶之旅】收集到的关于如何在OpenGL ES上使用MSAA(Multisample Anti-aliasing)实现抗锯齿效果的资料和源码

    文章目录 一.需求 1.2 OpenGL的MSAA功能不能在OpenGL ES上使用,你崩溃了吗? 1.3 OpenGL ES的抗锯齿在离屏渲染和在屏渲染上的不同需求 二.OpenGL ES上实现的M ...

  5. Learn OpenGL 笔记5.11 Anti Aliasing(抗锯齿)

    这种清晰地看到边缘组成的像素结构的效果称为锯齿. 有很多称为抗锯齿技术的技术可以通过产生更平滑的边缘来对抗这种锯齿行为.(小时候打开一个新游戏,第一件事情就是把抗锯齿给关了,开抗锯齿太卡了) 起初,我 ...

  6. OpenGL MSAA多重采样抗锯齿的实例

    OpenGL MSAA多重采样抗锯齿 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <glad/glad.h> #include & ...

  7. OpenGL抗锯齿实例

    OpenGL抗锯齿实例 先上图,再解答. 完整主要的源代码 先上图,再解答. 完整主要的源代码 #include <glad/gl.h> #define GLFW_INCLUDE_NONE ...

  8. 【ShaderToy】基础篇之再谈抗锯齿(antialiasing,AA)

    写在前面 在之前的基础篇中,我们讲到了在绘制点线时如何处理边缘的锯齿,也就是使用smoothstep函数.而模糊参数是一些定值,或者是跟屏幕分辨率相关的数值,例如分辨率宽度的5%等等.但这种方法其实是 ...

  9. OpenGL 半透明效果的实现(混合)、雾效果 和 圆滑(抗锯齿)

    在前面的glColor总是忽略最后一个参数alpha,今天就来学习下这个参数的使用 在早上的光照球体的例子基础上进行演示,早上的例子是在白色的地面上有几个彩色的球体和一个类似于玉镯子的模型,利用半透明 ...

最新文章

  1. 设计模式学习笔记——代理(Proxy)模式
  2. 何小鹏发文力挺李斌:2019年最惨的人
  3. 平板波导 matlab,非对称平板波导色散曲线求解(附matlab程序).doc
  4. Windows10升级失败怎么办?赶快用微软官方的升级工具:Windows10易升
  5. HTML5是不是解决跨平台问题的终极密钥
  6. Visual Studio下载安装编写C语言
  7. PHP之支付宝APP支付
  8. MFC——CWnd类
  9. 存在的hive插入数据_往hive表中插入数据以及导出数据
  10. Android 仿淘宝详情视频图片混合轮播
  11. 一键将kafka,zookeeper安装为windows服务
  12. 金庸的小说人生(1)
  13. redis展示 删除 详情
  14. 极域工具包 1.1正式发布!窗口化极域,解键盘锁,适配学生机房管理助手7.4-7.5!
  15. 边缘计算网关是什么?有什么功能和特点?
  16. 计算机序列类型和字典试题,计算机考试试题和资料
  17. Win10 + VS2013 配置OpenBLAS
  18. 小D课堂-jekins-02
  19. Android FM 模块学习之四 源码解析(1)
  20. Routerboard/DR6018CV01-wifi6-Qual-IPQ6010-IPQ6018-FAMILY-2T2R-2.5G-ETH-port-supporting-5G-cellua

热门文章

  1. 线上python课程一般多少钱-python培训班一般多少钱?一篇文章告诉你
  2. 基于python的在线考试系统-Python在线考试系统防作弊功能的思路和实现
  3. 自学python需要安装什么-Python学习需要安装的工具
  4. python自动化办公实例-python自动化测试实例解析
  5. 在vue项目中导入element-ui
  6. SpringBoot 集成log4j2
  7. 关于axios中'$router' of undefined问题
  8. 【深度优先搜索】计蒜客:Betsy的旅行
  9. 【python 笔记】集合类型详解
  10. java 包依赖_java命令执行带jar包依赖的文件执行不了