Games101计算机图形学入门基础之二:光栅化_图形学 光栅化-程序员宅基地

技术标签: c#  算法  unity  人工智能  计算机图形学  开发语言  

引言

首先发出疑问:光栅化是什么?为什么要光栅化?怎么进行光栅化,光栅化的步骤是什么?
答:

  1. 光栅化通俗点讲就是把东西画在屏幕上的一个过程。
  2. 光栅化具有把顶点数据转换为片元的过程,具有将图转化为一个个栅格组成的图象的作用。
  3. 要进行光栅化需要进行采样,但是在采样的过程中会带来一些瑕疵,俗称走样,为了更好的观感,我们要进行反走样的操作。总结一下采样–>走样(无法避免,只能消除)–>反走样

三角形的离散化

我们知道光栅化就是将物体绘制到屏幕上的一个操作,但是应该怎么进行绘制呢?上节我们说到要将拍摄到的画面绘制到一个平面上我们要进行“MVP”变幻,其实后面还有一步就是将拍到画面绘制到屏幕上的一个过程,简称视口变换。
既然我们要把物体画到屏幕上来,那就需要先了解屏幕(Screen)是什么?在图形学中我们可以把它抽象为一个二维数组,其中二维数组中的每个元素是像素( pixel )。这其实也很好理解,我们平常大家看到屏幕都会说它的分辨率(resolution )是1920 * 1080,这就是表示这个屏幕有这么多像素并且这些像素形成了一个二维数组。
然后我们还需要定义什么是屏幕空间(screen space),我们可以从下面的图中看到:屏幕覆盖的范围是从(0, 0) 到 (width, height),而像素可以用一个个的方格表示它们索引的范围是从(0, 0)到 (width - 1, height - 1),比如这个蓝色的像素就可以用(2,1)表示,并且像素 (x, y)的中心在(x + 0.5, y + 0.5),其中x和y都是整型的。
在这里插入图片描述
在了解完了什么是屏幕(Screen)和屏幕空间( screen space)以后,我们就要解决怎么把这个单位立方体映射到屏幕空间(Canonical Cube to Screen)?我们知道单位立方体的x和y在平面上的范围是 [-1, 1]2,而像素平面的范围是[0, width] x [0, height],为了把单位立方体里面的所有物体画在屏幕上(Canonical Cube to Screen)我们首先必然要做一个视口变换,但是怎么确定这个视口变化矩阵(Viewport transform matrix)呢?这也很好理解原来我们单位立方体的x和y都是2,现在x要变成width那就需要乘width /2,而y要变成height那就需要乘height/2,而z保持不变则为1(这个z值后面会详细介绍用来做深度测试),然后我们不要忘了此时立方体的中心点还在(0,0)而屏幕的中心点在(width /2,height/2)因此还需要平移(width /2,height/2)个单位(概括为先缩放再平移)。这样我们就可以算出视口变化矩阵(Viewport transform matrix)(如下图):
在这里插入图片描述
通过视口变化矩阵(Viewport transform matrix)把单位立方体映射到屏幕空间(如下图)
在这里插入图片描述
然后绘制出类似于这样一张图片:

为什么经过一系列的空间变换和视口变换得到的是一系列屏幕空间的三角形 ?这是因为三角形在图形学中可以看做是几何体的基本形状(Triangles - Fundamental Shape Primitives),那可能又有人会想为什么是三角形呢?因为三角形在图形学中有很多很好的性质:(1)三角形是最基本的多边形,并且任何其他的多边形都可以拆分为三角形。(2)三个点可以保证他在一个平面如果是四边形四个点就不能保证。(3)它可以很好地用叉积判断一个点是不是在三角形内部(三角形的内外定义特别清晰)[4]。
在这里插入图片描述

到这一步的时候大家不要忘了我们此时得到的不过是屏幕空间中的一些三角而已(这个过程叫做三角形的离散化),但是这远远不够我们需要把这些三角形打碎打成像素并且告诉每个像素的值是多少然后显示在屏幕上,这一个完整的过程我们称之为光栅化(Rasterization)。
需要把屏幕空间的一系列三角形打碎成一个一个的像素并告诉它们像素值,那这一步怎么判断三角形里面有哪些像素呢?也就是说我们给定一个屏幕空间中三角形的三个顶点坐标怎么确定里面有哪些像素呢?(这样是光栅化中最重要的一个概念,判断一个像素与三角形之间的关系,更确却的来说我们考虑像素的中心点与三角形的位置关系)。
在这里插入图片描述

采样

采样就是给定一个连续的函数,在不同的点求它的值,也可以认为,采样是把一个连续的函数离散化的过程。
这里的采样是指利用像素的中心对屏幕空间进行采样。因此我们需要定义一个 inside(tri, x, y)函数判断这个像素的中心是否在三角形内,这个函数的定义这就涉及到 上节讲的叉积了(Cross Products)——如果这个点一直在三角形三条边的左边或右边那么就可以认为这个点在这个三角形的内部。
在这里插入图片描述
这里大家忘记了可以看一下前一节
如果这个点在三角形的内部那么我们就定义它为1,不在内部就定义为0(这里的像素只考虑是显示还是不显示的问题,不考虑像素内部颜色的变化,像素内部本身还是很有讲究的)。这样我们就可以确定inside(tri, x, y)这个函数了(如下图)
在这里插入图片描述
接下来我们就可以用一个最笨的方法就是遍历整个屏幕中的像素,然后判断像素的中心点是否在三角形内部,如果在就赋值为1并显示。
在这里插入图片描述
可能大家一样就看的出来遍历整个屏幕空间的像素太浪费了,因为三角形可能只是屏幕中很小的一部分,因此我们可以进一步优化,只要判断这个三角形包围盒里面的像素就可以了(如下图)
在这里插入图片描述
采样完成后,我们自然知道有哪些像素的中心点在三角形内,那我们自然而然的可以把这些像素填成三角形对应的颜色,我们又知道像素是一个小方块并且它内部的颜色是均匀的,所以下图左边三角形内的像素中心点对应在不同的像素上会变成右边这样(如下图)
在这里插入图片描述
这样我们就会发现一个问题,我们想要得到的是一个三角形,但是实际得到的不完完全是一个三角形,不过的它的形状和三角形大概类似,这就是我们所说的锯齿(Jaggies!)。有两个原因导致锯齿:一是像素本身有一定的大小,二是采样的速度更不上信号变化的速度(高频信号采样不足)

走样

上述操作后得到的结果和预期结果有一定的差距,这一现象我们称之为走样。具体走样还带来了什么现象呢?

走样带来的瑕疵

  1. 锯齿
    在这里插入图片描述

  2. 摩尔纹
    这里是因为剔除了奇(或偶)数行的列和行。
    这里

  3. 车轮现象
    在这里插入图片描述
    接着是车轮效应,当我们顺时针旋转纸片时,如下图所示:有些条纹显得在逆时针旋转,在生活中我们也经常能看到类似的现象,比如高速行驶的汽车,其轮子看上去在反向旋转,这是因为人眼在时间中的采样速度更不上运动的速度造成的。

反走样

有几种方法来解决走样问题:

①增加采样率
例如屏幕的分辨率提高,像素变小,那么就相当于采样更密集,采样率提高,但是受限于物理限制,我们在处理屏幕时并不能随心所欲的修改分辨率,所以这个方法只能是一个终极方法。

②先做模糊(低通滤波)再采样。
如图先把高频信号砍掉,得到一个五边形形状的信号,然后再采样就不会发生混叠了。
在这里插入图片描述

先模糊再采样

如何进行反走样呢?可以在采样之前先做模糊再采样(模糊也可以认为是低通滤波器,把三角形边界的这种高频信号给过滤掉)。

在这里插入图片描述

傅里叶变换

上面我们出现了走样(Aliasing)问题,我们是通过先模糊后采样的进行反走样(Antialiasing )去优化它的。这里我们不仅不思考几个问题: 1、为什么采样速度更不上信号变化的速度就会产生走样问题?2、为什么先模糊后采样就可以解决锯齿问题?

要探究其根本原因,我们就需要了解一点信号原理里面的傅里叶变换(Fourier Transform),傅里叶变换可以将信号分解为频率,并且能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合(如下图)。
在这里插入图片描述
这里我们只需要知道傅里叶变换可以将信号分解为频率,并且能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合,具体的深入学习可以去学习一下高数积分这一块的内容。
上面我们说了出现走样的原因之一是因为对高频信号采样不足,但是有人会说高频信号采样不足为什么会导致走样(Aliasing)呢?我们看下面这张图你就可以恍然大悟了,对于蓝色的高频信号与黑色的低频信号它们有相同的采样点,这个时候就出现了高频信号采样不足:采样错误地显示为来自低频信号,在给定采样点的时候无法区分的两个频率速率称为“别名”。
在这里插入图片描述
这里可以看到有不少的高频波上有的点并没有被采样到。

低通滤波

前面我们提及到反走样常见的办法之一就是在采用之前进行模糊,然后有人会问为什么模糊(预过滤)然后采样可以进行抗锯齿?
我们开始提及到傅里叶变换可以把函数从实域变到频域,如下图所示,左图的人像是实域,右图是傅里叶变换后对应的频率。高通滤波器(High-pass filter)就是只保留高频信号(可以理解为图形内容中的细节,或者说边界-变化大的地方);反过来如果把高频信号全部去除只保留低频信号,我们就会得到一张相对模糊的图。我们之前光栅化抗锯齿做的先模糊就是采用了低通滤波器(Low-pass filter),使其高频信号边界去除然后图形就会变得模糊。
在这里插入图片描述

卷积

我们说的滤波其实就是剔除特定频率的信号,从另一个角度看,滤波Filtering也可以看做卷积(Convolution)。或者说是平均。我们前面提及到的模糊就是采用了低通滤波器,也可以认为是一种平均操作。平均我们都知道,卷积是什么呢?

如下图所示,对于一系列信号,滤波器就好像一个窗口,它可以左右移动,窗口的大小是一个3*3的格子,这其实是要做一个卷积(Convolution)操作,也就是说移动窗口的时候,窗口三个数所对应的信号的三个数做一个点积的操作。
在这里插入图片描述
我们或许可以了解一点卷积理论来深入了解Convolution,在实域上的卷积等同于在频率上的乘积,反之亦然(Convolution in the spatial domain is equal to multiplication in the frequency domain, and vice versa)

下图我们用了一个33的卷积核在实域做了一个卷积,因此图片变得模糊了。如果我用一个更大的卷积核(9*9)做卷积图片会变得更模糊(我们这里可以用极限的思想来想一想,如果这个卷积核无限大比图片像素还大,那是不是每个点积值几乎都是一样的,保留的信息就越少了越接近低频。如果我们用一个无限小比图片像素还小的卷积核去做卷积,那是不是意味着它相当于没有做滤波,值没有改变,也就是说把所有的频率信息都保留下来了)。
在这里插入图片描述

多重采样抗锯齿(超采样)

我们开始在光栅化的时候利用一个二值函数f(x,y)来判定像素只有在三角形内和外,这也就是会导致边界的锯齿。如果我们对每一个像素进行卷积操作,我们的卷积核是像素本身一样的大小,而卷积的值就是等于每个像素的覆盖面积。
在这里插入图片描述
然而怎么计算出每个像素的覆盖面积?实现起来并不容易,因此人们研究出一种近似算法也就是所谓的超采样MSAA,MSAA只是一个反走样的近似,并不能严格的解决反走样的问题。

MSAA的算法是将一个像素划分成许多个小像素,每个小像素都有一个中心,算面积覆盖率就是可以等同于有几个采样点在这个三角形,如果像素点足够多的话就可以取得比较好的结果。

MSAA(多重采样抗锯齿)是MultiSampling Anti-Aliasing的英文缩写,指多重采样抗锯齿,原理是寻找出物体边缘部分的像素,然后再把画缩放到当前的显示器上。
在这里插入图片描述

MSAA可以取得不错的反走样,但是使用了更多的点来计算三角形的覆盖面积,付出了很多倍的计算量。所以对计算机计算力有一定的考验。

深度缓存

可见性与遮挡(Z-buffering)

根据常识,我们很容易的想到画家在作画的时候如何体现遮挡。在作画时一般都是先画远处的物体,之后再画近处的物体,这样画出的画就可以体现出遮挡,该算法称为画家算法。
在这里插入图片描述
画家算法需要先对所有物体的深度进行排序(最少需要n log ⁡ n n\log nnlogn的时间复杂度),然后依次按顺序画在屏幕上(光栅化)。画家算法虽然思想简单,但并不能解决所有的遮挡问题,比如下图的情况
在这里插入图片描述
为了解决画家算法的缺点,在图形学中引入了深度缓存算法(Z-Buffer)。

Z-Buffer算法同时维护一个渲染图(Rendering)和深度图(Depth / Z buffer),如果某一三角形深度较浅,则在光栅化时就会遮挡到之前画的像素。下图是Z-Buffer算法维护的两张图,右侧的是深度图,即物体中的三角形距离我们越近颜色就越黑,越远颜色就越白。
在这里插入图片描述
在算法处理到其他物体时,Z-Buffer算法会根据当前物体深度信息更新渲染结果并更新深度图。算法的伪代码大致如下:

// 初始时,zbuffer数组为∞
for (each triangle T)
	for (each sample (x,y,z) in T)
		if (z < zbuffer[x,y]) // closest sample so far
			framebuffer[x,y] = rgb; // update color
			zbuffer[x,y] = z; // update depth
		else
			; // do nothing, this sample is occluded

在这里插入图片描述
其中R表示无限大,各自中的数字表示该物体某一局部的深度信息,数字越小则距离摄像机越近,越不容易被遮挡。z-buffer算法在计算当前像素是否被下一个图形像素覆盖的过程就称为深度测试。

对于由n nn个三角形组成的场景中,z-buffer算法的复杂度为O ( n ) O(n)O(n)
补充
如果我们使用了MSAA算法,则z-buffer计算时就不仅仅是考虑每个像素做深度测试了,而是要对每一个子像素做深度测试。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/superheromen/article/details/126010745

智能推荐

hdu 1229 还是A+B(水)-程序员宅基地

文章浏览阅读122次。还是A+BTime Limit: 2000/1000 MS (Java/Others)Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 24568Accepted Submission(s): 11729Problem Description读入两个小于10000的正整数A和B,计算A+B。...

http客户端Feign——日志配置_feign 日志设置-程序员宅基地

文章浏览阅读419次。HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息。FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。BASIC:仅记录请求的方法,URL以及响应状态码和执行时间。NONE:不记录任何日志信息,这是默认值。配置Feign日志有两种方式;方式二:java代码实现。注解中声明则代表某服务。方式一:配置文件方式。_feign 日志设置

[转载]将容器管理的持久性 Bean 用于面向服务的体系结构-程序员宅基地

文章浏览阅读155次。将容器管理的持久性 Bean 用于面向服务的体系结构本文将介绍如何使用 IBM WebSphere Process Server 对容器管理的持久性 (CMP) Bean的连接和持久性逻辑加以控制,使其可以存储在非关系数据库..._javax.ejb.objectnotfoundexception: no such entity!

基础java练习题(递归)_java 递归例题-程序员宅基地

文章浏览阅读1.5k次。基础java练习题一、递归实现跳台阶从第一级跳到第n级,有多少种跳法一次可跳一级,也可跳两级。还能跳三级import java.math.BigDecimal;import java.util.Scanner;public class Main{ public static void main(String[]args){ Scanner reader=new Scanner(System.in); while(reader.hasNext()){ _java 递归例题

面向对象程序设计(荣誉)实验一 String_对存储在string数组内的所有以字符‘a’开始并以字符‘e’结尾的单词做加密处理。-程序员宅基地

文章浏览阅读1.5k次,点赞6次,收藏6次。目录1.串应用- 计算一个串的最长的真前后缀题目描述输入输出样例输入样例输出题解2.字符串替换(string)题目描述输入输出样例输入样例输出题解3.可重叠子串 (Ver. I)题目描述输入输出样例输入样例输出题解4.字符串操作(string)题目描述输入输出样例输入样例输出题解1.串应用- 计算一个串的最长的真前后缀题目描述给定一个串,如ABCDAB,则ABCDAB的真前缀有:{ A, AB,ABC, ABCD, ABCDA }ABCDAB的真后缀有:{ B, AB,DAB, CDAB, BCDAB_对存储在string数组内的所有以字符‘a’开始并以字符‘e’结尾的单词做加密处理。

算法设计与问题求解/西安交通大学本科课程MOOC/C_算法设计与问题求解西安交通大学-程序员宅基地

文章浏览阅读68次。西安交通大学/算法设计与问题求解/树与二叉树/MOOC_算法设计与问题求解西安交通大学

随便推点

[Vue warn]: Computed property “totalPrice“ was assigned to but it has no setter._computed property "totalprice" was assigned to but-程序员宅基地

文章浏览阅读1.6k次。问题:在Vue项目中出现如下错误提示:[Vue warn]: Computed property "totalPrice" was assigned to but it has no setter. (found in <Anonymous>)代码:<input v-model="totalPrice"/>原因:v-model命令,因Vue 的双向数据绑定原理 , 会自动操作 totalPrice, 对其进行set 操作而 totalPrice 作为计..._computed property "totalprice" was assigned to but it has no setter.

basic1003-我要通过!13行搞定:也许是全网最奇葩解法_basic 1003 case 1-程序员宅基地

文章浏览阅读60次。十分暴力而简洁的解决方式:读取P和T的位置并自动生成唯一正确答案,将题给测点与之对比,不一样就给我爬!_basic 1003 case 1

服务器浏览war文件,详解将Web项目War包部署到Tomcat服务器基本步骤-程序员宅基地

文章浏览阅读422次。原标题:详解将Web项目War包部署到Tomcat服务器基本步骤详解将Web项目War包部署到Tomcat服务器基本步骤1 War包War包一般是在进行Web开发时,通常是一个网站Project下的所有源码的集合,里面包含前台HTML/CSS/JS的代码,也包含Java的代码。当开发人员在自己的开发机器上调试所有代码并通过后,为了交给测试人员测试和未来进行产品发布,都需要将开发人员的源码打包成Wa..._/opt/bosssoft/war/medical-web.war/web-inf/web.xml of module medical-web.war.

python组成三位无重复数字_python组合无重复三位数的实例-程序员宅基地

文章浏览阅读3k次,点赞3次,收藏13次。# -*- coding: utf-8 -*-# 简述:这里有四个数字,分别是:1、2、3、4#提问:能组成多少个互不相同且无重复数字的三位数?各是多少?def f(n):list=[]count=0for i in range(1,n+1):for j in range(1, n+1):for k in range(1, n+1):if i!=j and j!=k and i!=k:list.a..._python求从0到9任意组合成三位数数字不能重复并输出

ElementUl中的el-table怎样吧0和1改变为男和女_elementui table 性别-程序员宅基地

文章浏览阅读1k次,点赞3次,收藏2次。<el-table-column prop="studentSex" label="性别" :formatter="sex"></el-table-column>然后就在vue的methods中写方法就OK了methods: { sex(row,index){ if(row.studentSex == 1){ return '男'; }else{ return '女'; }..._elementui table 性别

java文件操作之移动文件到指定的目录_java中怎么将pro.txt移动到design_mode_code根目录下-程序员宅基地

文章浏览阅读1.1k次。java文件操作之移动文件到指定的目录_java中怎么将pro.txt移动到design_mode_code根目录下

推荐文章

热门文章

相关标签