我们都知道,在渲染流水线中,顶点着色器对输入的顶点数据进行处理(如顶点的坐标变换和光照计算)以后,GPU会进行进行齐次除法并将顶点从三维空间转换到二维的屏幕坐标,接着将这些所需要的着色数据发送到光栅化阶段。
在光栅化的三角形遍历,我们知道,需要检查所有像素是否被一个三角形网格所覆盖,如果被覆盖,就会生成一个片元。

首先,这一步具体是什么意思呢?


图中每一个小矩形就是一个像素pixel,而每个小矩形中心的点就是“采样点”。整个大三角形就是“三角形网格”。检查每个像素是否被三角形网格覆盖的意思即为,每个像素的采样点是否被覆盖到了,如果被覆盖到了,就会生成一个片元。



那么,我们是如何通过三角形三个顶点的数据,得到片元的信息,用于Fragment Shader中进行光照计算的呢?

在《Unity Shader入门精要》一书中,在三角形遍历这里,书中写到:

三角形遍历的过程。根据几何阶段输出的顶点信息,最终得到该三角网格覆盖的像素位置。对应像素会生成一个片元,而片元的状态是对3个顶点的信息进行插值得到的。

到底这个“对3个顶点的信息进行插值”是什么意思?又是为什么通过对三个顶点进行插值就可以得到整个片元的状态(包括一些计算光照所需要的信息如屏幕坐标,深度信息等等)呢?

这边,我们首先要说线性插值的概念。

我们已经见过很多线性插值的例子了,比如在a和b点之间,构成的以t为参数的直线形式为 p = (1-t)a+tb.
这就是插值,因为当 t = 1的时候,p=b,当 t = 0 的时候, p = a。因为系数 t 和 1-t 是线性多项式,所以被称为线性插值。(参考虎书)

三角形是图形学程序中基本图形,像颜色这样的信息会标记在三角形顶点上,并且被插值到三角形当中。实现这种插值的坐标系被称为重心坐标系

什么是重心坐标?(注意区别于重心)重心坐标系是如何实现插值的呢?

重心坐标是由三角形顶点定义的坐标。二维三角形中的一些关于重心坐标的定义如下:关于三角形重心坐标定义看这里

  • 三角形上的任一顶点p(x,y)都可以用三个顶点的线性组合表示:p=αa+βb+γ*c
  • 且系数之和为1:α+β+γ=1 (只有系数之和为1,p点才在这个三角形所在的平面上)
  • 当满足三个系数都为非负数时,该点在三角形内部
  • 可以看出,重心坐标是齐次坐标的一种这三个系数所表示的坐标(α,β,γ),就是该三角形上p点的(归一化)重心坐标。

通过a,b和c三点构建非直角坐标系。那么p点的坐标就可以表示为
p = a + β(b-a)+ γ(c-a);
拆开就会得到:
p = (1-β-γ)a + βb + γc;
我们让 α ≡ 1 - β - γ,那么p = αa + βb + γc;

于是我们可以得到,α + β + γ = 1 另一种证明方式看这里

对于a,b和c构成的三角形,当
0<α<1
0<β<1
0<γ<1

p点在三角形内部 (也就是说不在内部的点p也可以通过重心坐标来表示),而(α,β,γ)就是p点的重心坐标。

那为什么我们可以用重心坐标来进行插值呢?

参考线性插值。我们前面说过,比如在a和b点之间,构成的以t为参数的直线形式为 p = (1-t)a+tb.这就是插值,因为当 t = 1的时候,p=b,当 t = 0 的时候, p = a。因为系数 t 和 1-t 是线性多项式,所以被称为线性插值。随着t的变化,p的位置也会发生变化,但是仍然在a与b之间的线段当中。

在三角形中,在上述p点的表达式当中,当其中一个坐标为0的时候,其他两个坐标位于0和1之间,该点在三角形边上;当其中有两个坐标为0,剩下一个为1的时候,该点位于顶点。
因此我们可以通过三个三角形的顶点,通过插值计算三角形内部某一个点p的颜色。如果三角形a,b,c的顶点上面的颜色分别表示为W1,W2,W3,那么p点的颜色可以表示为:

p(α,β,γ) = αW1 + βW2 + γW3

这就是利用p点的重心坐标,采用一种平滑的方式将三个顶点的颜色进行插值,从而得到的p点的颜色。

三角形重心坐标插值的作用?在图形学中,利用重心坐标在三角形内部进行任何属性(位置、纹理坐标、颜色、法线、深度、材质属性…)的插值。一般我们通过其他步骤都会得到三角形顶点上的属性,但继续计算时需要用到三角形内部的某点的属性值,利用重心坐标可以得到三角形内部该值的平滑过渡。


所以在三角形遍历中,我们就可以通过对三角形三个顶点的数据进行插值,从而得到该片元的信息(就是所谓的“片元的状态”)。

好,讲明白上述的东西以后,要来讲一讲我一直模糊的概念,锯齿和抗锯齿的问题。

首先,锯齿是怎么产生的呢?这就跟上面我们讲到的东西相关了。

还是这张图,我们可以看到,被三角形网格所覆盖的像素(覆盖了采样点)就会生成一个片元(红色的),但是,可以很明显的看到,有些三角形网格覆盖的区域其实覆盖到了像素的区域,**但是并没有覆盖采样点,**因此,并不会产生一个片元,而且这样的情况是很大程度会发生在三角形的边缘部分。三角形边缘变得锐利了,因此就产生了走样,也就是所谓的锯齿。

这种锯齿,是因为采样不足导致的。

那么我们应该如何解决呢?如何抗锯齿?

简单来说,像素中心的采样点没有被覆盖,所以不会生成一个片元,该像素点就不会被着色,所以才会导致边缘很锐利,不平滑。抗锯齿就是为了让该像素有颜色,使得边缘看起来平滑,这样就抗锯齿了。

两种技术,一个是超采样抗锯齿(Super Sample Anti-aliasing, SSAA)的技术,另一个是多重采样抗锯齿(Multisample Anti-aliasing, MSAA)。在这边讲MSAA,因为MSAA实在SSAA上发展来的。SSAA是理论上效果最好的抗锯齿方案,但在目前的硬件条件下这种性能开销是不可接受的,因此在SSAA的基础上发展出了MSAA。

首先,多重采样,顾名思义就是将一个采样点变成多个采样点。采样点在每个像素的中心,也就是多重采样使用了子采样点。
我们不再使用像素中心的单一采样点,取而代之的是以特定图案排列的4个子采样点(Subsample)。我们将用这些子采样点来决定像素的遮盖度。当然,这也意味着颜色缓冲的大小会随着子采样点的增加而增加。(采样点的数量可以是任意的,更多的采样点能带来更精确的遮盖率。)

左图,采样点未被覆盖,该像素的颜色为白色。右图特定位置的4个子采样点中有两个被覆盖了,那么此时应该如何决定像素的颜色呢?

这边提一下SSAA的原理。SSAA使用比正常分辨率更高的分辨率来渲染场景,当图像输出在帧缓冲中更新时,分辨率会被下采样(Downsample)至正常的分辨率。这些额外的分辨率会被用来防止锯齿边缘的产生。

如下图,SSAA原理是首先对每个像素取四个子采样点(几个可以自己定义),对四个子采样点都运行一下Fragment Shader,将三个顶点插值得到的颜色储存于每个采样点中,然后取平均值,得到最后的像素颜色。但是Fragment Shader运行的次数越多,对性能的消耗是很大的。虽然SSAA可以得到非常好的效果,但是这样的性能开销太大,相当于用4x来暴力渲染4倍分辨率图像。

那么能不能只计算每个像素的颜色,只运行一次FS,而对于那些子采样点只计算一个覆盖信息(coverage)和遮挡信息(occlusion),然后根据这两个信息来决定是否把像素的颜色信息写到每个子采样点里面呢?这就衍生了MSAA技术。

注意,每个子采样点都有颜色和深度,对于子采样点来说,他们共享着中间采样点的颜色值,但是每个子采样点都有独立的深度信息。覆盖,就是判断该子采样点是否被三角网格覆盖了。遮挡,就是模板测试和深度测试。

MSAA的区别就在于,它可以同样在一个像素中取四个子采样点,但只对像素中间的采样点运行了Fragment Shader来计算颜色。
然后可以对每个子采样点都进行深度测试和模板测试,然后通过判断覆盖和遮挡信息,将像素的颜色信息写入子采样点里面。没有通过测试的子采样点,就会不会写入颜色。

“MSAA是把像素扩大N倍,得到N个子采样点,再将扩大成N倍的渲染目标,再经过一个过程缩放成原始的渲染目标,这个过程称为resolve”

这句话是什么意思?

1.由于深度测试是在每个子采样点的级别而不是像素级别进行的,深度buffer必须相应的增大以来存储额外的深度值。在实现中,这意味着深度缓冲区是非MSAA情况下的n倍
2.虽然我们只对每个像素进行着色,但是并不意味着我们只需要存储一个颜色值,而是需要为每一个子采样点都存储颜色值,所以我们需要额外的空间来存储每个子采样点的颜色值。所以,颜色缓冲区的大小也为非MSAA下的n倍

还有一个最重要的问题,虽然我们可以通过覆盖和遮挡信息,来决定是否将像素中心采样点的颜色写入子采样点。那么最终这个像素的颜色到底是如何决定的?

当颜色缓冲的子样本被图元的所有颜色填满时,所有的这些颜色将会在每个像素内部平均化

因为上图的4个采样点中只有2个被覆盖住了,这个像素的颜色将会是三角形颜色与其他两个采样点的颜色(在这里是无色)的平均值,最终形成一种淡蓝色。左边两个子采样点是三角形颜色,右边两个点无色,取平均值,淡蓝色。

再举一个例子,

以下分析来自于这里:

1.光栅化阶段,对四个子采样点执行三角形覆盖判断,在一个四倍分辨率大小的coverage mask中记录每个子采样点被覆盖的情况。
2.像素着色阶段,在像素中心圆点处执行像素着色器。该点的位置、深度、法线、纹理坐标等信息由三角形三个顶点重心插值得到。图中计算得到像素颜色为紫色。
3.对四个子采样点执行模板测试与深度测试,并将测试通过的子采样点数据写入四倍分辨率的模板缓冲与深度缓冲。每个子采样点都拥有自己的深度值,依然是重心插值得到。
4.上图中左下两个子采样点通过了深度测试,并且coverage mask为1,因此将紫色复制到这两个子采样点对应的颜色缓冲中(依然是每个子采样点一个颜色,共四倍大小)。其他两个子采样点暂为背景色。
5.重复上述流程绘制第二个黄色三角形,将像素着色获得的黄色复制到右上角的子采样点中。
6.所有绘制结束之后,通过一个对高层透明的PASS,将四个子采样点的颜色插值获得最终输出的像素颜色。

为什么在延迟渲染中不能使用抗锯齿技术?
看这里看这里

如何解决?
看这里看这里

以上锯齿被称为几何走样,还有着色走样。

现代抗锯齿技术:
Post Processing AA技术。这一类东西包括FXAA,TXAA

关于三角形重心坐标插值/锯齿/抗锯齿/延迟渲染中的抗锯齿问题相关推荐

  1. 使用重心坐标插值三角形顶点的任何属性

    文章目录 三角形重心坐标插值的作用? 什么是重心坐标(此处只考虑三角形)? 为什么三个和为1的系数可以表示p点 重心坐标的由来 计算重心坐标的方法 面积法 公式法 三角形颜色插值实现 参考 三角形重心 ...

  2. 延迟渲染G-buffer所占显存带宽计算(解决移动端和抗锯齿的若干疑问)

    延迟渲染需要在前面阶段,将计算的内容保留在N张G-buffer中,但是网上的文章只是提及了G-buffer应该压缩,并且尽量少用,没有说明G-buffer所占带宽应该是多少,我将在下面介绍G-buff ...

  3. 游戏中的抗锯齿技术Anti-Alasing提炼总结

    游戏中的抗锯齿技术Anti-Alasing提炼总结 锯齿(走样,失真)产生的根本原因 图形学的根本过程是一个图形转化成图像的过程,该过程是一个连续信号经过采样转化成离散信号(显示设备的像素是离散的)的 ...

  4. GAMES101 作业3(附三角形重心坐标,Blinn-Phong光照模型及法线贴图推导)

    目录 写在前面 第一题 三角形重心坐标 第二题 Blinn-Phong光照模型 第三题 纹理贴图 第四题 凹凸贴图实现及法线贴图推导 第五题 位移贴图 写在前面 main 函数中  std::func ...

  5. 图片渲染延迟_前向渲染与延迟渲染

    如果您开发过3D游戏,那么您可能会在现代图形引擎的研究中遇到术语"前向渲染"和"延迟渲染". 而且,通常,您必须选择一种在游戏中使用.但是它们是什么,它们有什么 ...

  6. Unity前项渲染和延迟渲染(二)

    前项渲染和延迟渲染一直懵懵懂懂的,所以希望借此文章让自己更好的理解这两个渲染方式,本文主要参考<Unityshader入门精要>.如果有错误,欢迎指出.图片截图皆来自于<Unitys ...

  7. 从0开始的技术美术之路(十六)延迟渲染

    本篇参考 "技术美术百人计划"·霜狼_may : <Shader入门精要>第九章·冯乐乐女神著: Unity官方手册 抗锯齿相关的大致了解 延迟渲染 手机TBDR框架 ...

  8. 【技术美术图形部分】关于前向渲染和延迟渲染

    学习参考 [技术美术百人计划]图形 3.4 延迟渲染管线介绍 <Unity Shader 入门精要> 1 Unity的渲染路径 关于渲染路径,我在图形渲染管线1.0中就提过了,但只是初步的 ...

  9. mouseenter 延迟_前向渲染与延迟渲染

    如果您开发过3D游戏,那么您可能会在现代图形引擎的研究中遇到术语"前向渲染"和"延迟渲染". 而且,通常,您必须选择一种在游戏中使用.但是它们是什么,它们有什么 ...

最新文章

  1. Pytorch的安装教程
  2. [error] - Build path is incomplete. Cannot find class file for org/aspectj/weaver/refl
  3. 边沿检测与信号同步?
  4. Vue之@click、事件修饰符@click.stop与@click.prevent、按键修饰符@keyup.enter
  5. C++编程语言之Lambda函数与表达式
  6. 架构师成长系列 | 云原生时代的 DevOps 之道
  7. 风车网上线,图片分享网站大潮将至
  8. 突发奇想:flash+.Net+数据库的一种构思
  9. 【转】浏览器缓存机制
  10. oracle中distinct的正确应用
  11. 解决idea中xml注释出现空格和顶格问题
  12. SAP-MM MRP类型详解
  13. Sniffer抓包教程
  14. 样本不平衡的解决方案(很详细)
  15. 统计学分析公式 MA移动平均线
  16. ride运行时报错ERROR
  17. arduino 读取模拟电压_基础部分-读取模拟电压
  18. R语言中的函数1:outer(张量积)
  19. html 实时曲线 js,基于d3.js实现实时刷新的折线图
  20. 四步迅速提高网站百度排名

热门文章

  1. 面经:总结面试经验分享,面试方法论!
  2. 如何判断自己的电脑有没有装java(windows7版本)
  3. Matlab Serial Port学习
  4. 遥感云大数据在灾害、水体与湿地领域典型案例实践及GPT模型
  5. 安卓ListView
  6. iptables mac地址过滤
  7. 思科三层交换机配置步骤实例
  8. 大数据应用开发工程师,主要负责的工作内容有哪些?
  9. 电子商务商业模式思考
  10. HTML5简易课程表(表格的制作)