GAMES101-现代计算机图形学入门-闫令琪(6-10)

第六节

上节课复习

MVP(model模型变换- view跟随摄影机变换到相对(0,0,0)位置 - project 投影变换(-1,1的3次方的空间))变换之后在进行视口变换(viewport,-1,1的3次方的空间变换到分辨率空间)

Antialiasing 抗锯齿

根据上节课的判断,我们可以得到某些像素带你的中心在三角形内部,某些不在,如图:

我们想得到的是这样的三角形:

结果把对应的像素填充完得到这样的三角形:

Aliasing 走样/锯齿

我们要想抗锯齿首先要明白锯齿是怎么来的。

采样是图形学中广泛存在的做法,光栅化的过程,其实就是在屏幕空间用一系列离散的点(也就是像素的中心)进行是否在三角形内这么一个函数的采样。

照片也是采样:一副照片,就是说所有到达这个感光元件的所在平面的光学信息,我们把它们离散成一系列图片上的像素的过程。

采样不光可以发生在不同的未知,也可以发生在不同的时间。
视频就是在时间上的采样。

采样是广泛存在的,采样的问题也是广泛存在的。
Artifacts(瑕疵)

采样的问题:

  1. 锯齿
  2. 摩尔纹(把采样的过程的奇数行和奇数列去掉在对起来就会产生这样的问题)
  3. 顺时针变成逆时针(采样速度太慢)
    信号变化太快,导致采样速度跟不上。
如何做抗锯齿,在采样之前先做个模糊或者滤波。 这样操作完之后效果还不错: 如果是先采样后模糊呢?结果是不行。

为什么?
为什么说采样的速度的跟不上信号变换的速度就会产生走样,
为什么先去做采样,后做模糊操作达不到反走样效果

为了解释为什么需要引入频率

Frequency 频率:
最简单的正弦余弦波: 差别就是相位不一样。 通过调整系数,会得到不同的余弦波,它们的不同在于频率不同,这里我们定义一个事情,cos2pifx, f就是频率,f就可以定义频率变换有多快,周期就是频率的倒数。 为什么要介绍这些呢? 微积分里面傅里叶基数展开:任何一个周期函数,都可以写成一系列正弦和余弦函数的线性组合以及一个常数项 所谓傅里叶变换,其实就是把函数给变成不同频率的段,并且把这些段显示出来。 小例子,相同的采样频率,对应上不同的变化频率,采样结果反应实际的效果越差。 由此我们可以看出来,采样的频率应该跟随被采样的频率变换。 如果同一个采样点,采样两种截然不同的函数,结果是一致的,像这样的情况就叫走样/混叠。 ##### Filtering 滤波: 所谓滤波,就是去掉一系列特定的频率。 傅里叶变换可以帮住我们理解这样事情。

左边的图经过傅里叶变换可以变成右边这幅图。
图像空间变成频率空间。
频率空间怎么理解呢,中心我们把它理解为最低频的区域,边缘是最高频的区域。在不同频率上有多少信息我们用亮度表示,可以看出这幅图大多数信息集中在低频上。对于自然的图片一般都是这样的。为什么会有水平和竖直的道呢?简单说原因是我们在分析一个信号时会认为它是一个周期性重复信号,对于不周期重复的信号,我们认为它是Tiling的,那我们会发现,正常的图片的左右和上下是会发生剧烈变换的,就会产生一个极高的高频。
图形的频率信息可以理解为图像相邻像素间色彩的变化。
傅里叶变换能够可以让我看到任何图像(任何信号)在各个不同的频率上长什么样,也叫做频谱图。

滤波就是去掉一些频率的内容。把低频去掉(就是把图像中变化缓慢的色块去掉), 在反傅里叶变换,可以得到一张显示物体的边界的图片。 这种滤波叫高通滤波(只有高频信息可以通过)。 为什么说高频信息对应上人物边界上呢,因为边界就是信息变化剧烈的地方, 把高频信息抹掉,只留下低频信息,在反傅里叶变换变回图片,可以得到一张模糊的图片 这种滤波叫低通滤波(只有低频信息可以通过)。 意味着边界被去掉了,模糊了。 保留了某一段频段,我们就会得到这样的效果。 滤波就去掉某一特定频率的信息。 滤波又等于平均又等于卷积。
Convolution 卷积:
什么是卷积? 其实就是加权平均。 所谓卷积是,在原始的信号附近取若干数用某一种滤波器的平均(图形学上简化的卷积)。 时域的卷积等于频域的乘积。(这块没搞懂,但是好像不影响) 例子: 任何一个像素都是它周围3*3像素的平均 拿到一幅图,先把它做傅里叶变换变成频域上的结果,3*3的卷积核也做傅里叶变换可以变成频域上,时域上做的卷积(上排乘起来),对应到频域上就是把两个信号乘起来,我们就发现大量的信息都变成黑的,只有中心一部分有信息,就像过了一个低通滤波。 卷积核,3*3乘以1/9,不乘亮度就爆了。等于一个像素+旁边八个像素在除以9。我们管他叫低通滤波器。 如果这个box变大变小呢? 更大box做卷积操作,得到的结果越模糊,频域上的结果就超级小,更小的box,比像素还小,等于没做滤波,把所有频率都留下来了, 从频域的角度看采样。 采样其实就是重复频率上的内容。 时域的卷积等于频域的乘积 采样就是重复一个原始信号的频谱。 频谱上采样间隔越大,重复的频谱就越密,密到叠到一起就是走样。
Antialiasing 反走样:
做法: 1. 680*440分辨率的显示器光栅化一个三角形,一个像素非常大,那当然看起来锯齿很严重,如果视网膜显示屏分辨率很高,对应像素很小,意味着像素和像素的间隔小,意味着采样间隔小,意味着在频谱上,频谱的搬移间隔大不容易混叠到一起,所以走样小。但是受制于物理空间实际因素,不可能开启某个反走样的效果提高分辨率。 2. **先做模糊,后做采样。**对应到频率上来说,模糊就是低通滤波,去掉高频信号,在采样,就不会发生混叠了。 实际操作中,我们用什么滤波器来进行卷积操作呢? 对应到三角形,我们怎么把三角形变成模糊三角形? 用一个一定大小的低通滤波器对它进行卷积。 反走样的基本方法: 对原始的三角形做以像素大小为卷积核的操作,或者说求个平均,然后在做采样。 如图对于任何一个像素,就是这些情况。

一个三角形对它覆盖的每个像素求个平均,怎么才能把一个三角形在某一个像素里面覆盖的区域算出来?
一个近似的方法。

一个像素划分成4*4个点,算出近似覆盖率,



MSAA解决了对信号的模糊这一步操作,采样是隐含在里面的,MSAA 并没有提高分辨率,而是为了得到一个近似合理的覆盖率。


结果还是挺明显的。

没有免费的午餐,增大了计算量,工业界会使用不同排列的采样点,而且每个采样点会得到复用,所以实际上的计算量没有那么高。
抗锯齿有很多种方法,最有代表的就是FXAA(图像后期处理)和 TAA(复用上一帧得到感应到结果)
超分辨率:
从低分辨率到高分辨率
本质上还是 采样不足的问题,用深度学习猜出来。

第七节

上节复习

  1. Rasterization 光栅化
  2. 采样
  3. 反走样
    先采样后做模糊为什么是错的?先采样就是在频谱上进行搬移,产生混叠。再模糊等于对画面施加了一个低通滤波。

Visibility/Occlusion 可见和遮蔽

Z-buffering
先绘制远的,再绘制近的,近的遮挡远的,就像油画绘制方式,这种算法叫做画家算法。 怎么计算深度(先画哪个)其实挺难的,特别是互相覆盖的情况。 为了解决这个问题,引入了Zbuffer算法(广泛采用) 对三角形不好排远近顺序,但是对像素排序好排序,每一个像素在这个像素内去记录所表示的几何最浅深度(离我们最近的距离)。 为了完成深度缓存,通常的做法是,渲染结果图片的同时生成深度图(深度缓冲)。 然后我们利用深度缓存去处理遮挡关系。

重要的说明,我们在变换中假设摄影机放在原点朝-Z看,所以Z越小离摄影机越远,越大离摄影机越近。
为了方便ZBuff计算,现在我们重新定义Z越小离摄影机越近,Z越大越远。

越近值越小,越黑,越大值越大,越白。

假如有一个像素,我先画了地板,后面又被物体覆盖了,那么在这个像素上,我们查看一下两个东西在这个点的深度,小的覆盖大的。并且更新右边深度图。
每一个像素内记录最浅深度。

深度缓存: 首先认为所有像素记录的深度都是无限远的,然后把一个个三角形以任意顺序做光栅化变成像素,再在任意三角形覆盖的任意像素的深度信息跟原有的zbuffer作比较,如果比它小,就更新这个像素的颜色和深度到图片和zbuffer上。

下面是伪代码。

1
2
3
4
5
6
7
for t in [所有三角形列表]:#在所有三角形列表内循环
for pixel in t:#在三角形的所有像素列表内循环
if pixel.z < Zbuffer[pixel.xy]:#判断像素的深度信息是否小于zbuffer如果小于就更新 图片颜色 和zbuffer深度.
framebuffer[x,y] =pixel.rgb
Zbuffer[pixel.xy] = pixel.z
else:#否则啥也不干
pass
例子: 一开始所有的像素对应的深度缓存,存的都无限大的数直(R)后面一个三角形的深度在5,5比无限大小,所以先画图像再更新Zbuffer,然后第二个三角形的深度对比进来,对比每一个像素的深度值,小的像素绘制然后更新,大的就不绘制不更新(5遮挡8),通过这种办法我们可以维护一个逐像素的深度缓存正确。最后我们会得到正确的图像和深度缓存。 总结一下: 画家算法,要花O(Nlog(N))。 深度缓存算法,O(n) N=三角形个数。 这里这里我们并没有排序所有像素,我们只是记录。等于对每个像素求一堆三角形里面的最小值。

假设不会有两个三角形在同一个像素有完全一致的深度,那么如果先画紫色或者先画红色三角形,结果都是一致的。
对于反走样的MSAA的方法,对于一个像素中间采样多个点,对于Zbuffer也要对每一个采样点求一个值。

注:Zbruffer处理不了透明物体。

Shading 着色

为什么要学着色?


引入明暗和颜色。
这门课里,对不同物体应用不同材质的过程叫做shading。

简单的反射(着色)模型
高光-漫反射-间接光照 把这三部分都分析表示出来,自然可以做出一种材质。使得它长得跟杯子很相似,在开始之前我们需要定义一些东西。 1. 表面方向:在一个小范围可以认为面是平面。面朝向的方向是法线n(表示方向用单位向量)。 2. 观测方向:从shading point 往相机连一条线就是v(表示方向用单位向量)。 3. 光源方向:动shading point 往光源连一条线就是I(表示方向用单位向量)。 4. shading point这个点的本身物体表面属性:颜色,亮度等。 Shading in Local:目前来说我们所说的着色,我们考虑只考虑这个点和其它我们定义的方向属性,但是先不考虑是否在阴影内,也不考虑其它物体是否存在。
Diffuse Item 漫反射的表示:
什么叫漫反射,有一根光线打到物体的某一个点上,会被均匀的反射到各个方向上就叫漫反射,我们把这个表诉出来就行。 Lambert's Cosine Law 同样的光照到不同角度的物体上亮度为什么会不同,第一张图接受6根光线,第二张图表面旋转60度接受3根光线,通过观测可以推导出物体表面法线和光线来源的夹角可以决定物体表面有多亮。 一个科学解释:首先要考虑光是能量,我们要看到的是这些物体接收了多少能量,接收能量当然跟面积有关,我们考虑shading point接受多少能量,指的是一个单位面积接收了多少能量。 能力的衰减,光源在中心,根据能量守恒定律,小的球壳上必然每个点的能量很多,大的球壳,球壳表面积增加,必然每个点分担的能量变少。 那我们定义半径为一的球壳上一个点能量为I,那根据能量守恒我们可以算出来半径为r的一个点能量为I/r的平方。 结论 1. 光线在传播过程中,如果考虑单位面积,在任何以为所能接收到能量,和光线传播的距离成平方反比,(对于任何一点,光线传播越长,单位面积上所接收到的能量衰减越多) 2. 假设知道点光源,知道shading point离点光源有多远,我们就知道有多少能量传播到shading point ,又根据Lambert's Cosine Law 我们知道有多少光会被接收,到达和接收算出来那我们就可以得到diffuse的表示方法
  1. 点光源距离shading point 为2,我们知道点光源强度I知道r就可以算出来在shading point shang 到达了多少能量。
  2. 根据Lambert’s, n点乘i可以得到余弦(为了避免得到没有物理意义的负数,光线从下面射到上面,这里用了一个max函数),知道了会接收多少能量。
  3. shading point为什么会有颜色,是因为它本身吸收了一定能量,然后反射出来是它不吸收的能量,也就是被看到的颜色,不同的点有不同的吸收率也就得到不同的颜色,这里我们定义了一个系数kd(表示diffuse),如果kd是1(灰度)就表示所有能量都被吸收了,没有能量被反射出去,如果是一个3维向量(rgb)就表示了颜色。
  4. 光线打到shading point 反射光是均匀的半球,所以从哪看都一样,所以跟v完全没关系。

注:这是一个经验模型,并不是完全符合物理的模型。

第八节

上节课复习
上节课学了Blinn -Phong着色模型,在一定的光照下,希望算清楚一个点是什么样的亮度。有什么样的颜色。分析了茶杯图片,着色分为漫反射项,高光项,和环境光项。 有两个事情再强调下: 1. 我们所谓的着色是在某一个shading point,要想得到一整张图要在很多地方应用很多次 2. lighting 的方向,是指从shading point 指向光源,从光源的位置减去shading point的位置在归一化变成的单位向量。
Specular Term 高光项的表示:
什么是高光: 平面比较光滑,接近镜面(如果是镜面就是根据入射角,法线很容易算出出射角如图V,如果是接近镜面就是在镜面反射的出射角附近如图R),如果能观察到高光那么观测角就在R接近,其他时间肯定看不到。 聪明的Blinn-Phong模型: 当我的观察方向和镜面方式R接近的时候,其实就说明法线方向和所谓“半程向量”很接近。 求两个单位向量的角平分线,很简单,两个向量一加,根据平行四边形法则,加出来的方向一定在中心,在做一下归一会就是h了。 h(半程向量)和n(法线)接近就说明V(观测方向)和R(反射方向)接近。 这样的好处就是如果要判断我是否看的到高光只需要看n(法线)和h(半程向量)是否接近。 (怎么样衡量两个向量是否接近,就看两个向量的点乘的结果是否接近1)
  1. ks 表示镜面反射系数。
  2. (I/r的平方)多少能量到达。
  3. 这里为啥不考虑多少能量接收,因为blinn phone是一个经验模型
  4. n和h点乘判断我的视角和高光有多接近,(为了避免得到没有物理意义的负数,光线从下面射到上面,这里用了一个max函数)
  5. 指数P是因为点乘的容忍度太高,向量偏离很大点乘数还是很高,如图,如果直接拿来用的话高光范围太大到45度还能看到,给定一个指数可以控制曲线也就是控制高光范围比如系数64到15度的时候就没有高光了,blinn pong模型经常用到100-200. 列是镜面反射系数变换,行是系数变换。 ##### Ambient Term 环境光照的表示:
观察得到茶杯的背面可以接收到环境光 1. 环境光不讲究从哪个地方照射 2. 环境光跟观测方向也没关系 3. 跟物体表面法线也没关系 blinn phong 模型里面的环境光是常数(一个颜色),保证场景内没有黑的地方,把所有其他的项都加起来提示一个亮度 Ia是环境光的强度 Ka是环境光的系数
所有的项都加起来
环境光(和法线方向无关,和光照方向无关,和观测方向无关) + 漫发射(和观测方向无关)+高光(和三个都有关) = Blinn-Phong Reflection模型。 任何一个shading point(一个点)知道了之后,对所以的点做一遍操作,整个场景就计算出来了。
Shading Frequencies 着色频率
三个几何表示完全相同的球,为什么着色了之后结果各不相同呢? 着色频率:就是把着色应用到哪些点上。 第一个球是着色应用在面上(根据面法线) 第二个球是着色应用在顶点上(vertexes),也就是一个三角面应用三次着色(然后根据插值算出来面) 第三个球是着色应用在每一个像素上(也就是根据每一个三角形顶点法线,进行插值,求出每一个像素的法线,然后着色,)

把以上三种方法做一个正规的定义

  1. Flat Shading(把三角形面的面法线求出来,算出来一个shading的结果应用到这个三角形上)
  2. Gouraud shading(每个顶点(vertexes)的法线求出来,对三个点着色之后再插值到整个面上)
  3. Phong Shading (根据每一个三角形顶点法线,进行插值,求出每一个像素的法线,然后着色)注意不要和Blinn Phong 反射模型混淆 不同模型不同着色频率的对比图 ##### 不同的法线怎么算 逐顶点的顶点法线怎么算? 结论:任何一个顶点都会和很多个不同的三角形有所关联,那么顶点的法线就是它相邻面的面法线求个平均(加一下三角形面积的权) 逐像素的像素法线怎么算? 结论:知道两个顶点的法线,怎么插值出像素的法线呢?这里要用到重心坐标
Graphics Pipeline

已知:

  1. 着色模型
  2. 着色模型怎么用
  3. 三维模型
  4. 不同的光照条件
    求:
    渲染结果。

这种从三维场景到一张图片中间的过程叫做渲染的图形管线(老说法)现在叫做实时渲染管线。

  1. Application 应用阶段: 输入一堆空间中的点(3D 空间)
  2. Vertex Processing 阶段: 把顶点变换到屏幕空间
  3. Triangle Processing 阶段: 在屏幕空间中形成三角形
  4. Rasterization 阶段: 通过光栅化离散成屏幕上的像素(fragment类比与像素)
  5. Fragment Processing 阶段: 判断像素或者fragment是否可见
  6. Framebuffer Operations 阶段: 对fragment进行着色求出所有的像素
  7. Display 阶段: 得到一张二维的图片
    这些操作都是在显卡上进行的。

之前都是在说把三维空间中的三角形投影到屏幕上,这里为啥说把三维空间中的点投影到屏幕上在连成三角形呢?
这个问题是因为我们如何定义空间中的模型?
我们可以定义所有三角形的顶点,然后定义哪三个顶点会形成三角形。
这样点投影完之后形成三角形,就跟直接三角形投影就没有区别了。

例子:




  1. MVP变换,对每一个顶点
  2. 对应的三个顶点连接成三角形
  3. 光栅化
  4. 判断像素或者fragment是否可见
  5. 这里根据 着色频率不同会产生差异
    • 如果着色频率是Gouraud shading(每个顶点进行着色),着色会发生在vertex processing顶点阶段,Fragment Processing 阶段插值
    • 如果着色频率是Phong shading(每个像素进行着色), 就是在Fragment processing 像素或者Fragment阶段
  6. 纹理映射稍后说
shader概念完善
**在现在的GPU渲染管线里面有些部分是允许编程的(顶点阶段和像素阶段),这个阶段决定像素和顶点如何运作,也就是shader。** shader 的功能是顶点和像素的着色 shader 是指能在硬件上执行的语言 shader 是通用的 每一个 顶点(或者fragment 或者像素 )都会执行一次,**不用for循环** 如果写的是顶点shader 这个shader就叫做 vertex shader , 如果写的是像素或者片段shader就叫做fragment shader 或者pixel shader

对于一个像素或者顶点shader,它需要完成这个像素/顶点最后的颜色是什么,要写清楚怎么样算,然后把它输出出去,这就是一个着色的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
uniform sampler2D myTexture; //uniform指的是 全局变量 纹理
uniform vec3 lightDir; //全局变量 关照方向

varying vec2 uv; //忽略uv
varying vec3 norm; //插值出来像素的法线 opengl下不用管有顶点法线方向,它会自动插值出每个像素法线

void diffuseShader()
{
vec3 kd;
kd = texture2d(myTexture, uv); //漫反射系数
kd *= clamp(dot(–lightDir, norm), 0.0, 1.0); //认为入射向量朝内,所以有负号,这里就是求了L和N的点积,然后限制到0,1
gl_FragColor = vec4(kd, 1.0); //固定的值 表示这个像素颜色是什么
}
纹理映射
如果只考虑球的着色,两个点光源的着色效果加起来就可以了, 但是有贴图的话其实就是物体每一个点的漫反射系数(点的属性)发生改变。 接下来的问题就是怎么定义一个点的属性呢? 首先如果要定义一个点属性,就需要知道这个点在哪里,在这里当然是在物体表面。 以上证明了任何一个三维物体的表面都是二维的。 例子: 怎么把几何上的所有三角形毫无拉伸(大的三角形还是大的,小的还是小的)无缝衔接的放到二维上面,是一个研究方向叫做Paremter Geomerty (几何参数化) 怎么把一个空间中的三角形映射到二维图片上?不是图形学范畴,这里认为已经有了这样一个映射关系。 任何一个三维空间中的点已经有了二维纹理上的坐标(UV) U在0,1,V也在0,1,约定俗成。 不同的三维位置可以映射到相同的二维纹理上,但是为什么会没有接缝呢? 当然是因为tiling贴图啊!!

下一个问题是,我如果知道三角形三个顶点对应的纹理坐标,那我如何知道三角形内部任意一个点对应的纹理坐标?
这里还是使用插值算法。
我如果已经知道三角形三个顶点有各自不同的属性,那么我如何把这个属性在三角形内部做一个平滑的过度,给我任何一个三角形内部的点,我都可以知道对应的属性插值之后的值。
这里用到了重心算法,下节讲。
纹理和着色的关系:
纹理是定义着色的时候需要的各个不同点的属性。也就是不希望着色的时候每个点都以相同的方式来着色,然后我就用纹理方式改变一些逐点的属性。
着色和材质有什么区别:
不同的材质就是不同的着色方法。

第九节

前情提要
着色的主要内容 1. 学习了着色模型,特别是Blinn phong,还手推了Blinn Phong着色模型,包括三个不同的项,高光项、漫反射项和环境光项,然后把三个项都合在一起就得到了一个材质,结论是我们如何去定义物体的表面与光线如何在物体表面作用,我们就可以得到某一种外观,最重要的是光线如何与材质如何作用。
  1. 学习了着色可以应用到不同的地方,应用在面上就是flat shading、应用在顶点上就是Gouraud shading、应用在像素上就是phong shading。这中间都涉及到大量的插值,如果是Gouraud shading,我们可以算出三角形三个顶点的着色结果,并且要在三角形内做插值,如果是phong shading,我们需要三角形三个顶点的法线方向然后对三角形内部的顶点做插值算出来任意像素的法线方向,然后shading。

  2. 学习了在硬件中是如何实现的,整个一套渲染管线,最重要的顶点的处理和 像素的处理。这两个都是可以编程的,如果是对顶点进行编程就是vertex shader,如果是对像素就是 fragment shader 或者 pixel shader。

  3. 学习了简单的纹理映射的知识,把三维空间中实际是二维的物理表面贴上一张图,提到了物体表面上各个不同的位置如何去映射到不同的二维空间(实际上是提前得到的)。

插值之前要明确的

为了学会如何在三角形内部进行不同属性的插值,我们需要学习重心坐标。

在学习之前这些问题需要搞明白:

  1. 我们为什么要在三角形内部进行插值?
    我们为了完成着色,很多内容是在三角形顶点上进行定义和计算的,在三角形内部任何一个点也想获得一个值,如果每个点的值都知道也就是得到从一个点过度到另外一个点的平滑的过度。
  2. 我们插值什么内容?
    定义在顶点上有各种各样不同的属性,比如纹理(之前定义了三个顶点分别映射到纹理上面哪三个坐标,那三角形内部映射到哪里呢?)颜色(三个顶点分别是红绿蓝,那么在三角形内部是怎么样的过度?),法线(三个顶点的法线插值出三角形内部像素的法线)
    基本上来说可以对三角形顶点上任意的值进行插值。
  3. 怎么做插值?
    用重心坐标做插值。
    Barycentric Coordinate 重心坐标
    重心坐标:
  4. 定义在一个三角形上的,换了三角形重心坐标就变了
  5. 在三角形ABC所形成的平面内,任何一个点(x,y)都可以表示成三个顶点abc的线性组合(x,y) = αA+βB+γC (ABC指的都是坐标),线性组合的系数要满足a+b+r =1
  6. 给我任意三个点ABC,不管ABC是在什么坐标系里面表示三角形,只要有一个点在ABC所在的平面上,我都可以通过α+β+γ=1这三个数做一个线性的平均,然后得到这个任意点在重心坐标下的表示。
  7. 实际上由于α+β+γ=1这个关系,其实知道两个数就知道了第三个数。
  8. 如果这个点在三角形内,这三个系数不为负,如果有某一个以上的数为负但同时满足α+β+γ=1,就证明这个点在三角形所在平面但是不在三角形内。
A点自己的重心坐标是什么呢? (α,β,γ)=(1,0,0) (x,y) = αA+βB+γC

为什么α+β+γ=1呢?
这个是重心坐标规定的事情,如果加起来不等于就证明这个这个点不在三角形所在的平面内,这个相对复杂,不多解释,拿来用即可。
结论: α+β+γ=1限制了你所要的点在三角形所在的平面内。

从重心坐标的定义我们已经知道了ABC三个点的坐标是什么。
那如果要求任意一个点的重心坐标是什么呢?这里重心坐标给出了另外一个定义:
任意点的重心坐标其实是可以通过面积比求出来。
面积比指的是 对面的三角形面积比整个三角形面积。
例如,我要求α的值,就是求点A对面的AA三角形面积比上三角形总面积的值。
从这个定义我们就知道了如果已知三角形三个点,那么可以非常轻松的得到三角形的重心坐标(就是把三角形均匀分成三个等面积的三角形的点)。

对于任意的一个点我们怎么计算呢?

  1. 利用面积算
  2. 简化版本
使用重心坐标
用重心坐标做任意一个三角形内部的点的插值 已知三角形内部任意点都可以用αA+βB+γC来表示,那么如果要插值某个属性,那么其也可以用线性坐标给组合起来。 例如,已知V的表示方法,那么把对应VA,VB,VC换成 ABC的属性值(颜色,坐标,法线,深度,材质属性)就是这个点的插值出来的属性值。

注意:经过投影,三角形三个点坐标发生变换,重心坐标会改变。
这个事情告诉我们要插值三维空间中的属性,就要用三维空间内的坐标,不能在投影之后的三角形坐标上做.
深度的问题:光栅化之后三角形已经投影到屏幕上了,会覆盖很多像素,像素都有中心,像素这些中心的这些点,我可以知道它投影了的三角形的哪里,在投影了的三角形做深度的插值是不对的。
应该找到像素中心点对应的三角形在三维空间的位置,在三维空间中做插值以三维空间的坐标做深度插值,然后再换回来。
怎么样把投影到屏幕上的在投影回去,应用逆变换即可。

Texture Magnification 如果纹理很小怎么办
屏幕上不管是像素还是用了MSAA有一个采样点,肯定有一个位置,有了这个位置我们可以插值出来这个位置的在UV空间上的纹理坐标。有了这个坐标去纹理纹理空间查询一下它的值,然后把这个值应用到漫反射系数上,就等于是把这张图给贴在了物体上。 但是这样会带来几个问题: 1. Texture Magnification 纹理的放大 如果这个纹理太小怎么办? 比如一个很大面积的墙,给一张很小的贴图。 那我们模型上任意一个点我都可以找到对应的纹理空间位置,然后这个位置可能不是一个整数(也就是对应的不是一个整像素),那我们就把他四舍五入成整像素,相当于在一定的范围内查找一个相同的**纹理上的像素**(texel纹理元素 纹素), pixel:生成的画面上的一个像素 texel:纹理上的一个像素 因为纹理太小,如果做简单的四舍五入,那么一个3*3 或者5*5的pixel像素范围可能会被映射到一个texel像素上,例如Nearest。 如果希望结果是模糊一点,那如果在查询纹理的时候你给我一个非整数的坐标,那应该如何得到它的值呢? Bilinear 双线性插值: 如图,每个格子是一个像素,每个黑点是像素中心,如果pixel采样到红点位置,如果是Nearest算法就是返回一个那个像素的颜色,如果是Bilinear 双线性插值算法就是如果不是刚刚好落在一个像素上,就做以下操作: 首先是拿到临近四个Texel纹素的值,然后投影出来s和t的值(s和t必小于1,因为纹理空间一个像素的距离是1,不到一个像素就是肯定小于1) 然后做一下线性插值Linear interpolation。 线性插值:有两个不同的值,定义在两个不同的位置上。如果有X是0到1,如果认为X等于0的时候在一个位置上,x等于1的时候在另一个位置上,那么求x等于0.5的时候在哪?肯定在这两个位置的中间。以此类推,这就是线性插值,简称Lerp. Lerp(X,v0,v1) 等于线性插值v0和v1,分别定义在位置0和位置1上,x是一个0到1之间的值,x = 0的时候,右边的值等于v0,x = 1的时候,右边的值等于v1,x = 0.5的时候,右边的值正好等于两者中间。 用线性插值,可以算出u0在两个像素(u00和u10)中间的颜色,同样道理可以算出u1在两个像素(u01和u11)中间的颜色,同样道理在做一下垂直的插值就可以算出来红点出的颜色了。现在这个点的颜色就综合考虑了它周围四个 点的颜色了(等于加了权重)。因为做了两次线性插值(水平和垂直)就叫做双线性插值。 注意,这里水平或者垂直先后顺序相互没有影响。 缺点,跟Bicubic双向三次的算法质量比还是差一些。 Bicubic:取周围16个像素,也是做水平和垂直插值,每次用四个做一个三次插值(不是线性),运算量大,效果更好。 ##### Texture Magnification 如果纹理很大怎么办 纹理大了会引起更大的问题。如图: 如果简单的应用纹理(像素中心找到纹理坐标,求出纹理上的值,已经学了插值所以任意的纹理的值都知道),就会得到上图右边的效果。
  • 为什么会出现这个问题?

    首先近处的一个像素覆盖的纹理范围是相对较小,如上图左边方块,在远处,一个像素覆盖了很大的纹理。 这个情况告诉我们,屏幕上的像素覆盖纹理的大小是各不相同的。 那么对于一个像素,如果它覆盖的纹理范围很小,那通过像素中心的纹理坐标查询一下它纹理空间的纹理对应值,没有问题,但是如果一个像素覆盖了很大的纹理范围,那我通过这个纹理的中心(蓝点)拿到对应的纹理坐标一个纹理像素的值,就会产生错误,因为一个纹理像素代表不了一个范围。
  • 如何解决?
    MSAA或者超采样,都差不多一个概念,
    一个像素用更多的样本去采样纹理空间,如果每个像素用512个采样点,把这点对应的纹理空间值平均一下。就会得到下面的结果。

走样的问题:信号变换过快,采样速度(频率)跟不上。 当像素采样的时候,因为对应的纹理大,就是每一块纹素小,很多块纹素包含在同一个像素中,这些纹素颜色都是不相同的,变化频率高,而采样频率低,当然就会走样。如果要不走样就需要提高采样频率。但是造成速度慢。 另外的思路: 如果不想走样,就不采样。 我们可以直接拿到像素对应纹素范围的平均值。 经典问题:点查询问题,和范围查询问题。多个领域都有应用。 说白了就是一张图,指定任意范围,能不能快速返回平均值给我。 如图,同一个像素对应不同的纹素范围。
  • Mipmap
    Mipmap 允许快速的、大约的、正方形的范围查询。又叫图像金字塔。
    什么叫Mipmap:从一张图生成一系列图。 这里为了显示,所以拉大了图片. 可以产生log2(图片像素)个mipmap,生成的mipmap占用了1/3原始图片的大小。

怎么知道要查询的区域有多大?
近似的办法:任何一个像素都可以映射到纹理,如图:红蓝点分布对应

在Pixel 上距离为1的像素,对应到Texel上的距离是可以求出来的。如图:

du和dv是纹理空间的两个像素采样点的距离,dx和dy表示pixel空间距离。
max(横向两个像素对应纹理实际空间长度,竖向两个像素对应纹理实际空间长度)。
然后近似营造一个正方形空间。

当我们可以把一个像素覆盖的区域,近似成正方形算出来之后,就是如何根据预计算好的Mip查询这个边长是L的正方形平均值是多少。
举个例子,如果这个L是4,那么我们就知道它肯定在第二层mipmap的时候变成一个像素。
也就是说根据这个区域,可以求出它在第几层会变成一个像素的大小,然后直接查询那个mipmap的像素的值就好了。

总结,通过计算一个pixel映射的texel近似范围,可以求出这个范围在第几层mip map是一个像素,然后拿到这个像素的值。拿空间换时间的技术。
近大:使用底层mipmap纹理,远小:使用高层纹理mipmap对应。

变换不连续,只算了整数层的mipmap效果。

三线性插值:为了避免出现这样的结果,可以利用插值求出非整数层。
比如1.8层,我们可以先找第一层做对应范围双线性插值,再找第二层做对应范围双线性插值,然后把这个结果当做层与层直接在做一次线性插值。这样我们利用三线性插值可以在任意的浮点数位置求出对应的平滑的像素数值,而且在层与层也可以求出平滑的像素数值。

MipMap能完全解决问题吗?

如图,mipmap跟超采样对比。
mipmap远处会出现过度的模糊(overblur)。主要因为采样的毕竟是近似的正方形。

各向异性过滤比mimmap效果要好。
什么叫各向异性:在不同的方向上它的表现各不相同,考虑各个方向,之前认为在水平和竖直上完全相同(各向同性)。
为什么会这样呢?

屏幕上的像素映射到纹理上,很有可能会出现超级狭长的斜着的范围,把它近似的算成一个框,那就会求很大的一个范围的平均值,必然就overblur,
各向异性允许我们在一个矩形范围内快速查询,自然得到的结果就会好很多,但注意还是近似。各向异性开销是MipMap的三倍。
各向异性在游戏引擎中一般还有选项X,2X就是横向竖向压缩两倍,以此类推。
存储量随着X的上升会逐渐达到3倍。也就是跟计算力没有关系,主要跟显存的大小有关系,如果显存足够,可以把X开到最高。

EWA过滤器,利用圆形多次求取值。

Applications of texture 纹理的其他应用

以后再说。