GAMES101: 现代计算机图形学入门(1)变换、光栅化、着色

1. 概述

GAMES101: 现代计算机图形学入门
链接:GAMES101

1.1 线性代数必备知识

1.1.1 向量

  • 向量的计算
  • 单位向量,向量的模
  • 向量的加法

1.1.2 点乘

  • 求取两个向量的夹角:先单位化点乘再反求ACOS即为夹角。
  • 用于投影的计算,如分解。
  • 判断两向量方向的关系:同侧反侧垂直,接近1则越接近。

1.1.3 叉乘

  • 始终规定右手螺旋定则
  • 判断多点位置的关系:在内部在外部,规定点逆时针,对判断点做叉乘,都为同一正负即判断点在规定点内部。
  • 相同向量叉乘为零向量。

2. 变换

变换直接通过矩阵的乘法实现。

2.1 变换的实现

2.1.1 放缩矩阵

2.1.2 倾斜矩阵

2.1.3 旋转矩阵

2维的旋转矩阵

3维的旋转矩阵

2.1.4 矩阵的齐次式

为了用一个矩阵清晰地又表示平移又表示变换,我们使用增加一个维度的方法,表示做出的平移变换。
我们规定 点:增加的一维永远是1向量:增加的一维永远是0

注意:在实际中我们通常先平移,再变换。

2.2 视图变换

视图变换,即相机的变换,把相机移动到规定方向和规定位置的过程。首先规定相机默认位置在原点,默认的向上方向为+Y轴,永远朝向-Z轴的方向。

2.2.1 把相机移动到原点

  • position:相机位置e
  • look-at direccction:相机的朝向g
  • up direcction:向上方向t

2.2.2 把相机转正

  • 第一步:平移,很简单
  • 第二步:旋转,看做是(x,y,-z)轴转向相机的(g,t)轴,即x轴(1, 0, 0)转向g叉乘t的向量,那么矩阵第一列为g叉乘t的向量,同理可得到剩下的列向量。再对得到的矩阵求逆,而旋转矩阵是正交矩阵,直接转置得到最后结果。

2.3 投影变换

投影变换的过程就是把在一定空间的物体显示在平面上。
投影变换分为正交投影和透视投影。

2.3.1 正交投影

  • 从理解上来讲,就是把z轴丢掉
  • 先平移
  • 再将生成的矩形平移并缩放为[-1, 1]

注意:此处为n-f,是因为我们用右手系,近是大于远的。

2.3.2 透视投影

透视投影其实就是根据z轴距离来做的比例变化。
特殊点的规定
(1)近平面上的任意一点永远不变。
(2)远平面上的z轴在挤压过程中不会变化
(3)远平面的中心点挤压后不会变化。

注意:对于任何一个深度的z轴,我们都能得到相似三角形的比例变化,即控件内部任何一个点被压到n平面所做的x, y轴变化。

至此,我们已经得到了变换矩阵的一部分。

第一,我们利用近平面的规定,点在近平面不会发生变化,因此我们能得到在近平面上变化后的具体点。

容易观察出z轴的变化一定与x,y轴的值无关,因此变换矩阵的第三行一定是(0 0 A B),A、B的数值未知。

第二,我们利用远平面的中心点的规定,远平面的中心点不会发生变化,我们得到另一组方程。

注意:我们通过两个特殊情况的方程,两个未知数,解出变换矩阵的值。

因此我们得到了压缩变换矩阵,我们做完压缩后,再做正交投影,最终得到透视投影矩阵。

注意:中间的某一个点,在通过挤压后,z轴会变大,即远离视点。具体思路是我们做个z的变换函数,求导即可。

3. 光栅化

经过变换后我们得到了[-1, 1]的立方体,我们需要将立方体画到屏幕上。
屏幕:(1)是一个二维数组;(2)每个元素都是一个像素;(3)一种经典的光栅成像设备。
光栅化:把东西画到屏幕上。
像素:由rgb混合而成。
屏幕空间:(1)像素的范围从(0,0)到(宽度-1,高度-1);(2)以像素块的左下角为坐标;(3)像素的中心在(x+0.5,y+0.5)。

3.1 将[-1, 1]变换到屏幕空间上

我们直接不管z轴,很容易做出到屏幕空间上的变换矩阵。

我们得到了在屏幕空间上的多边形对应的位置,但是这不够,因为之前我们的变换是相对于原来的,现在我们需要相对于屏幕空间,在这些像素上确认我们的显示。所以我们需要把这些多边形打碎成很多三角形,把这些多边形画在屏幕上,这就是我们所说的光栅化

3.2 采样

3.2.1 三角形的性质

(1)最基础的多边形,任何的多边形都能拆成三角形。
(2)三角形的内外部定义很清楚。
(3)在三角形内部的任意一点可以利用顶点来中心插值。

3.2.2 inside()函数

主要判断像素的中心点与三角形的关系,在三角形内部输出1,在三角形外输出0。
判断是否在三角形内部:利用向量的叉乘即可。

for(int x = 0; x < xmax; ++x)
{
    for(int y = 0; y < ymax; ++y)
    {
        image[x][y] = inside(tri, x + 0.5, y + 0.5);
    }
}

上述代码可以使用包围盒优化,只对x,y轴的最大最小所围成的矩形边界进行遍历。

for(int x = min{Px}; x < max{Px} + 1; ++x)
{
    for(int y = min{Py}; y < max{Py} + 1; ++y)
    {
        image[x][y] = inside(tri, x + 0.5, y + 0.5);
    }
}

3.2.3 采样带来的问题

  • 锯齿(走样,Aliasing)
  • 摩尔纹(去掉奇数行或奇数列)

出现问题的原因:信号变化的太快了导致采样跟不上变化的速度

3.2.4 滤波

傅里叶级数展开:任何的函数都能展开成正弦余弦的加权和的形式。

傅里叶变换:将函数展开的各个频率的函数拿出来。

注意:对于函数本身有一定的频率,对于采样也有一定的频率,因此是采样的频率对于原有函数的频率不一致而导致的问题。

上图,对于蓝线函数和黑线函数,我们用同样的采样方法无法区分,这就叫走样

滤波:去掉特定的频率。

图片中心亮,是因为信息都基本集中在低频。
(1)我们过滤掉低频信息,只保留高频信息,图片只会显示边界,称为高通滤波
(2)我们过滤掉高频信息,只保留低频信息,画面会变模糊,即看不见边界,称为低通滤波

3.2.5 锯齿的解决方案

锯齿产生的主要原因是因为采样的频率跟不上纹理的频率。

使用低通滤波的实现,我们可以通过卷积的方式(平均),因为卷积的矩阵只有低频,所以乘积后得到的就是低通滤波。

因此我们解决锯齿的方法,需要先模糊,再采样。

3.2.6 现代抗锯齿方法

(1)MSAA(MultiSample Anti-Aliasing)

利用更多的采样点进行反走样,将一个像素内部分成很多个采样点,并将采样结果在像素点内部进行平均。

注意:MSAA并没有提高分辨率,只是发生在采样之前,以细分像素的方式将每个像素求平均,因此MSAA就是模糊操作。MSAA之后再进行采样,采样的就是平均后的像素颜色

(2)FXAA(Fast Approximate AA)

进行图像的后期处理,即在采样之后进行的操作
输入一张有锯齿的图片,通过方法找到锯齿的边界,并将边界优化成没有锯齿的边界。
注意:这和我们先模糊再采样的解决思想相反。

(3)TAA(Temporal AA)

找上一帧的信息,用相同的点去感知不同时间的图像,再进行优化,即将原像素在时间上做平均

(4)超分辨率

DLSS(Deep Learning Super Sampling)通过深度学习的方法,把低分辨率的拉成高分辨率的,现在的图像高清修复就是这样做的。

3.3 处理画面的远近

画家算法:先画距离远的再画距离近的。
注意:实际中不能用画家算法,因为画家算法难以确认谁在前谁在后。
Z-Buffer(深度缓冲):我们不用先画远的,而针对于每一个像素,去记录它最浅的结果。
(1)深度图:储存每个像素对应的最浅深度。
(2)结果图:储存最终的结果。

4. 着色

我们将图像画在了屏幕上,但是这与真实情况不同,因为色彩需要考虑明暗、高光等信息,这就是着色处理的问题。
着色定义:对不同物体应用不同的材质。

4.1 光照模型

Blinn-Phong反射模型:高光(Specular highlights)、漫反射(Diffuse reflection)、环境光(Ambient lighting)
规定:着色只考虑着色点的法线(n),点到光源的方向(l),点到观测点的方向(v),其它均不考虑,如阴影等。

4.1.1 漫反射

漫反射:光线打到着色点上,会均匀的反射到各个方向去。
首先,我们定义着色点接收的能量,我们将光线打在着色点上视为一种能量,我们分析该能量的大小,与光线方向与法线方向的夹角有关。夹角越小,那么该点接收到光的能量越大。

定义点光源的强度,在单位1距离上光强度为I,与距离的平方成反比。

我们将光到达了多少能量,并且该着色点接收了多少能量,两者结合,得出了漫反射的表达式。

注意:漫反射各个方面接收的结果应该是一样的,所以与观察方向无关。
漫反射系数越大,表示表面越不吸收能量,所以全部反射出去了,也就越亮。

4.1.2 高光

高光的方向可以理解为镜面反射的方向,因此观察方向越接近镜面反射方向,就越看得到高光。转化成半程向量(光照方向与观察方向的角平分线方向)与法线方向的夹角,这就不用算镜面反射方向了,直接转化成两个向量的加法。

4.1.3 环境光

我们大胆假设,任何一个点接收到来自环境的光永远相同,因此与观察方向、光照方向都无关,即环境光为常数。用来保证场景中没有地方是完全黑暗的。

4.1.4 模型的应用

我们将环境光、漫反射和高光全部叠加,最终即为着色的光照效果。

4.2 着色频率

  • 以平面为单位着色
  • 以顶点为单位着色
  • 以像素为单位着色

注意:这与shader编程类似,只是以不同的单位进行着色。

  • 如何求顶点的法线:根据该点的相邻面的法线,平均出该点的法线。
  • 如何求面上某一像素的法线:根据面的顶点的法线,用中心插值的方法求出面上某一像素点的法线。

4.3 实时渲染管线(图形管线)

现在的实时渲染管线已经全部GPU内完成好了的,但也可以自定义编程,如顶点处理等。

  • Shader 编程:此处不介绍…
  • Shader在线编程网站Shadertoy

4.4 纹理映射

4.4.1 纹理的定义

光照模型中都有光照的系数,而那个系数是根据物体上着色点的属性所决定的,因此我们需要每个点都有自己的基本属性,即纹理映射。注意,任何一个三维物体的表面是可以展开成二维的
纹理:纹理就是一张uv坐标的图,首先我们不管怎么展成纹理uv坐标的,我们认为每个三维表面的任意点都在uv上对应。
注意:(1)纹理一般可以反复运用,无缝衔接。(2)定义的区别:纹理是希望着色的不同而定义的每个点的基本属性;材质就是着色方式的体现,材质的不同就是着色方式的不同

4.4.2 重心插值

首先我们属性是定义在顶点上的,因此我们要把各个属性在三角形内部进行插值处理,如纹理坐标、颜色、法向量等。
重心坐标:在三角形内的任意一点都可以表示为三个顶点的线性组合。

注意:如果三个系数都为正,则该点一定在三角形内,重心坐标系数可以通过面积比计算出来。

我们利用向量的叉乘,很容易就能得到每个点的重心坐标。
我们把各个属性都可以用重心插值的方法在各个点上表示出来。

注意:对于投影重心插值无法直接运用,需要现在三维中插值出来,再投影。

4.4.3 纹理太小

纹理图如果很小,在很高分辨率的地方要显示出来,我们采用双线性插值的方法。

双线性插值:取周围的4个点进行插值。
双三线性插值:取周围的16个点进行插值。

4.4.4 纹理太大

纹理太大,会发生走样问题

  1. Mipmap:范围查询,只能做近似的正方形的范围查询。

注意:(1)分辨率每减少一层,像素缩减至原来的1/4,也就是利用四个像素平均成一个像素。(2)mipmap存储会增加原来图像1/3的存储量

我们把每一个像素的区域通过微分的方法近似成纹理坐标uv中的正方形区域
如果近似出的正方形区域是1×1,mipmap就在原始的图上选取,如果正方形区域是4×4,mipmap就在level2的图上选取,因此是在logL层去选取该区域应该近似的颜色。
越远的地方,一个像素覆盖的纹理很多,所以正方形很大,需要在很高层去查询,越近的地方,一个像素就是一个像素,在原本图像去查询。因此把每一个像素区域都投影到uv坐标上,并且使用mipmap去查询。
层与层之间的三线性插值:在各层上都使用双线性插值,然后对现在的层进行线性插值。即计算小数层上的纹理。

mipmap的缺点:都是正方形的,因此矩形或者斜着的区域会过度模糊,如下图所示。

  1. 各向异性过滤

如果要查询矩形的纹理,mipmap就很困难了,因此要存储矩形的图形,也就是各向异性过滤
各向异性过滤即考虑不同方向带来的问题,各向异性过滤的开销是原来的3倍,因此各向异性过滤只与显存有影响。
2X各向异性过滤:考虑各个方向只压缩1次。4X各向异性过滤:考虑各个方向压缩2次。

  1. EWA过滤

如果要解决斜着的矩阵,我们可以使用EWA过滤,使用圆形进行不断的查询。

4.5 纹理映射的应用

4.5.1 环境映射

Spherical Map:将环境光反射在球上,直接存储在球上。

Cube Map:将球分为六个面,存储在六个面上。

4.5.2 凹凸/法线贴图

通过凹凸贴图来算假的法线,即可达到从视觉上改变高度差的感觉。

对于二维来讲,我们先求切线(求导数),再求法线。对于实际当中,我们分别求出u、v方向的偏导,即可求出法线(梯度)

4.5.3 位移贴图

位移贴图会真实改变模型的形状。

位移贴图需要模型足够细分,并且需要很高的采样。
在贴图过程中,改变纹理三角形细分的程度,可以通过DirectX的动态曲面细分来完成细分。

4.5.4 三维纹理

  • 三维纹理定义了空间中任何一个点的纹理。
  • 利用三维空间中的噪声函数来进行纹理映射,比如凹凸等都可以得到。
  • 预先使用环境光遮蔽图计算模型阴影。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值