Skip to content

本节是Games101 rasterization部分的学习笔记

光栅化

前边的部分我们学习了MVP矩阵,如何把一个物体投影到一个规则立方体内([-1,1]3),如何把这个规则立方体内点的物体投影在屏幕上,转变为二维图像,这就是光栅化的过程。 image.png 当我们把这个规则立方体的物体投影在屏幕上(width,height)时,还需要做一次视口变换: image.png

接下来要做的就是光栅化了,实际使用中很多几何体都使用三角形来表示,为什么选择三角形?

  • 很多基础的形状都可以用三角形来光栅化;
  • 三角形有很多独一无二的特性:
    • 保证可以平面化
    • 比较好定义三角形的内外()
    • 三角形内部比较好插值(重心插值)

那么我们如何在屏幕中光栅化出三角形呢?屏幕是由很多的像素点组成的,我们逐个的判断像素点的中心是否在三角形内,从而判断是否对这个像素点着色,最终完成三角形的光栅化。 image.png 判断点在三角形中的中心可以通过向量叉积: 对于下边这个三角形,可以通过QP0 × QP1QP0 × QP2 QP1 × QP2 ,判断三者叉乘获得的Z轴方向是否相同,相同则在三角形内部, image.png

反走样

采样是广泛存在的,采样的问题我们叫做Sampling Artifacts (Artifacts,中文翻译瑕疵,在图形学中表示一切不准确或我们不希望看到的结果)

Sampling Artifacts :(产生Sampling Artifacts ,或者说走样的本质原因是:信号(函数)变化太快,以至于采样速度跟不上(这里涉及到频率的概念**))**

走样的类型

  • 锯齿 jaggies

image.png 上边两张图,左边是原始三角形,右边是屏幕采样后的结果,右边这张图很明显出现了锯齿。

  • 摩尔纹 moire

image.png

  • **车轮效应:**高速向前旋转的车轮,我们人眼看上去好像是反向的旋转(人眼的采样频率跟不上车轮的转速)
  • 。。。。。。

反走样的一个基本思想是采样之前先做模糊再做计算。

直观上去理解,就像下边这幅图,我们先将三角形的边缘进行模糊操作,在进行采样,得到了一个边缘模糊的三角形,锯齿感就不那么强了。

image.png

从数字信号的角度看走样的问题

为什么我们先做模糊在做采样?这就涉及到数字信号、频率方面的知识了。

这一部分闫老师快速的讲了很多的结论性的东西,从数字信号的角度看采样和走样的问题,主要是为了让我们更好的理解一些反走样的方法(MSAA等)的原理,想要深入的理解需要去学习数字信号处理的内容。

这一部分我并不是很理解,这里就简单记录下课上的内容。

反走样的很多方法是借鉴与数字信号方面,比如说音视频领域,可以理解为我们从屏幕上取像素点类似于从连续的声波里间隔的取一些值存储起来。image.png

傅里叶变换,可以把任意函数图像转换为若干个三角函数的组合,也就是我们可以把频率曲线转换为用傅里叶函数来取代; image.png

从数字信号的角度看图像,每张图像发生颜色突变的地方(边界),就是频率突变的地方;我们可以用高通滤波器(high path ),只过滤出高频的部分,结果就是图像的边界部分; image.png 反之用低通滤波器,只过滤出低频的部分,结果可以让图像更模糊; image.png

反走样的方法

第一种,最简单有效的办法当然是直接增加采样频率,但这样会增大开销,所以并不是好用的方法。 **第二种办法是在采样前过滤掉高频信息,也就是让图像的边界变模糊。**具体思路是对每一个像素,根据这个像素中三角形所占的比例,分配不同的亮度。 image.png

MSAA

MSAA方法的基本思想采用超采样,对每一个像素区域,增加多个采样点,然后对这些采样后的值取平均作为这个像素区域的颜色。 image.png

MSAA直观上看起来会增加很多倍的计算量,但在实际的工业上使用,人们并不会把一个像素区域分割成规则的很多正方形,而是会根据权重采取一些不规则的形状,从而减少计算量。

FXAA 快速近似抗锯齿

快速近似抗锯齿(FXAA,fast approximate antialising)方法是MSAA的原理不同,与采样无关,它是采用图像的后处理,对产生了锯齿的图像进行一系列操作,来达到抗锯齿效果是一个偏经验性的算法。

TAA

基本原理是结合时间片段的采样,复用上一帧的采样结果,类似把MSAA采样的结果分布在时间上,这样就可以减少采样工作流。

深度缓冲

image.png 深度缓冲是用来解决三维空间中遮挡的问题,思路是建立一个深度缓冲区depthbuffer,深度缓冲区内记录屏幕中每一个像素深度最小的值(距离屏幕最近)。渲染时,遍历相机空间下所有的物体,判断像素点对应的该物体的深度是否大小depthbuffer中的深度,从而决定是否要渲染该像素。

渲染时会始终同步生成最终的渲染j结果(frame buffer)和深度缓存(depth buffer)。 frame buffer里存储每个像素的颜色,depth buffer里存储每个像素的最小深度。

伪代码如下:

javascript
for(let i = 0;i<scene.primitive;i++){
  // 判断像素点
	for(let x=1;x<width;x++){
    for(let y = 1 ;y< height;y++){
      // 比较物体深度值与深度缓冲区的深度值,当小于时,渲染该像素,并替换深度缓冲区该值
      if(scene.primitive[i].depth < depthbuffer(x,y){
          render(x,y)
          depthbuffer(x,y) = scene.primitive[i].depth
      }
         
    }
  }
}