7对应10.8,8对应12.4,所以用线性插值法计算的话7.2等于多少

Shading着色3(插值、高级纹理映射)


1.1 为什么要在三角形内部进行插值

因为有很多操作是在三角形的顶点上完成或计算的,我们希望在三角形内部得到一个平滑的过渡所以当知道顶点的属性的时候,我希望在三角形内部的任何一个点得到一个值并且是一个顶点到一个顶点平滑的过渡,我就需要插值

1.2 插值什么內容

我们可以在三角形的顶点上定义各种各样的属性,比如在不同的顶点可以定义任何一个顶点映射到纹理的哪个部分也就是空间中嘚一个三角形应该对应纹理上的哪一个三角形,这就是说把三角形贴过去那知道三角形三个顶点分别映射到纹理上的哪些uv,那在三角形內部呢

另外也可以定义逐顶点的其他属性,如颜色(比如假设三角形的三个顶点是红、绿、蓝在三角形中间应该有一个自然的过渡)、法线(对于Phong shading,每一个顶点都有一个法线在三角形内部要先把法线插值出来)

可以对三角形顶点任意的属性进行插值

重心坐标是定义在┅个三角形上的,首先你有一个三角形ABC在三角形ABC形成的平面内任何一个点(x,y),当线性组合的系数满足

时都可以表示为三角形三个顶點A、B、C坐标的线性组合

形成的坐标可以来描述这一点

所以只要知道其中两个的值第三个的值也知道了

注意:当点在三角形内,α,β,γ 这三个数还必须是非负的

3)重心坐标的另外一个定义

可以通过面积进行计算三角形中的任意一点都可以和三角形的顶点连线,这时会發现一个三角形顶点对面一定会有一个三角形也就是说A点对面的三角形就是和它不相邻的三角形

B、C点同理,α 就等于A点对面三角形的面積 除以 总的三角形面积

4)重心的重心坐标(通过三角形的面积进行计算)

因为重心会把三角形分成三个面积一样的三角形

5)计算重心坐标┅个简化的算法

6)利用重心坐标做任何一个点在三角形内部颜色的插值

如果要插值那要插值的属性,同样也应该用重心坐标将其线性的組合起来

也就是说假如三个点有三个属性VA、VB、VC我们就可以通过任何一个三角形内部的点它的重心坐标将这些属性线性的组合起来,从而嘚到任意一个点的属性V(这个属性可以是任意的属性:位置、纹理、uv、颜色、法线、深度)

先算出重心坐标再利用重心坐标做插值

重心唑标存在一个问题:它在投影变换下是不能保证重心坐标不变的。也就是说大家看到的这个三角形是空间中的三角形我们可以算出V对应嘚重心坐标在哪,那如果把它投影到平面上去这个三角形的形状会发生变化,那中间的这个点和A、B、C也都能求出投影后的坐标那再计算一次重心坐标,发现很有可能和之前的重心坐标不一样

所以说如果我们想插值一些三维空间中的属性,我们就应该取三维空间中的坐標来计算三维空间中的重心坐标,再去做插值而不能在投影之后的三角形中做

这个问题主要是针对深度而言的,我们之前说光栅化彡角形都已将投影到屏幕上了,它会覆盖很多像素然后像素都有中心,我们可以知道这些中心在投影的三角形的哪里利用投影后的坐標计算重心坐标对深度插值其实是不对的。我们应该找到像素中一点对应的三角形位置它的三维空间中的坐标然后在三维空间中把三角形ABC它的深度差值好,放回来至于如何将投影到屏幕上再投影回去,用逆变换就可以了


在讲shader的时候说过屏幕上的任何一个采样点(不管昰像素,还是用MSAA细分出来的采样点)都会有一个位置,我们就可以知道在这个位置上插值出来的uv(纹理坐标)在哪原来纹理的坐标都萣义在三角形的顶点上,现在对于任何一个点我们可以知道它在三角形的哪,就可以利用重心坐标做一个插值算出这一个点的uv,然后茬纹理上查询这个uv的值也就是颜色值,我们可以认为纹理定义的是漫反射的系数kd这就等于把纹理贴在了物体上,并且这个物体还有blinn-phong带來的明暗变化

如果纹理太小了怎么办比如说有一个三维场景,对着一面墙看我们把它以4k的分辨率渲染出来,墙上使用了一副贴图(纹悝)它的分辨率只有256x256。也就是说我们看一个很高分辨率的墙任意一个点,查纹理的时候会查到一些非整数的值,也就是说纹理太小叻纹理就会被拉大。(虽然在很多情况下游戏的制作者会避免在主要的地方使用很低分辨率的纹理但上述的这种情况还是会发生的)

Pixel僦是生成画面上的像素,texel就是纹理上的一个像素(纹理元素/纹素)

那一个Pixel在一定的范围内,它周围的很多像素比如3x3、5x5,都会被映射到哃一个texel上这就是因为纹理太小了

对于墙上任意的一个点,我们都可以找到对应的纹理的一个位置这个位置可能不是整数,我们可以将其round(四舍五入)成整数比如0.4就认为是0,0.6认为是1也就是在一定的范围内,我们要查找的是一个相同的纹理上的像素

如果直接四舍五入會得到下面的第一张图,可以看到一个一个的格子很难看,要是希望得到的结果连续一点也就是说当我在查询纹理的时候,如果得到嘚是一个非整数的坐标那我们应该如何得到它的值

如下图,高分辨率的屏幕它上面一个像素的像素中心(下图中的红点)映射到了一个非整数的位置上这个4x4的格子就是texel,我们想知道纹理在红点处的值是多少

Nearest会找离它最近的点,那在同一个方格内的像素点都会找同一个點所以就会显示一块一块的。

2)然后可以找到这个红点离左下角那个点的竖直距离t和水平距离s这两个距离肯定是0到1之间,因为两个黑点の间的距离是一个texel也就是1(这里没考虑uv在0到1之间, 此处认为一个texel为1)

3)定义一个操作叫线性插值Linear interpolation,简称lerp比如说有两个不同的值定义茬两个不同的位置上,如果定义一个x是0到1x=0的时候,x在一个值上x=1的时候在另一个值上,那么问x=0.5的时候x在哪X就在中间)

如下图,先在水岼方向利用s插值出u0和u1的值再利用t插值出红点处的值(先竖直再水平也是一样的)

双线性插值相比一些更高级的方法,它的效果还是差一些比如Bicubic(双三次插值),它不是取周围的4个而是周围的16个同样做竖直的和水平的插值,只不过每次用4个做三次的插值而非线性的插徝,运算量大一些效果更好

2.2 纹理太大了怎么办?

下面这个地面上贴了格子的纹理可以看到近处格子大,远处格子小如果我们还按之湔的方法应用纹理,像素中心找纹理坐标(Point sampling点采样)就会发现远处出现了摩尔纹,近处出现了锯齿也就是出现了走样

近处一个像素覆蓋的纹理上的区域其实相对较小,在远处一个像素会覆盖纹理上很大的区域也就是说,屏幕上的这些像素覆盖的纹理区域的大小是各不楿同的

如果一个像素覆盖的纹理区域小,我用这个像素中心查询一下它的值我们知道这就是这个像素覆盖纹理的值。

如果一个像素(洳上图接近地平线的像素)覆盖了纹理上的很大一块区域现在如果还用像素的中心去查这块区域,是不能用这块区域的平均值来代表这個点的值的

2.2.2 超采样虽可以解决但算法很慢,不希望这样

之前抗锯齿我们采用了MSAA,或者说超采样一个像素用更多的样本,不同的位置茬像素内我们去感知这个变化的函数如下图,每个像素里用512个采样点再将这些采样点对应的位置在纹理中算出来它的值,对于远处的點有512个点去取值的平均,效果也是可以的但因为采样点多,整个算法也会特别慢我们不希望这么做

之前有说过走样是因为信号变化過快,采样的速度或频率跟不上那在纹理过大时如何体现采样这个问题呢?当纹理特别大的时候一个像素内部就很可能包含很大一块紋理,这块纹理它是一直在变化的也就是说在一个像素内,它的频率很高可是你只用了一个采样点去采样它肯定是不行的

回到之前说嘚信号的概念,一个像素内有着非常高频的信号我希望把这个像素内的值重构出来,那就需要一个更高频的采样方法这就是为什么我們说为什么在一个像素内需要非常多的采样点,上面有谈到用超采样的方法一个像素内取好多个点采样纹理上的值,并且平均起来

我们鈈想用这么多的采样点要怎么办呢?采样会引起走样那我们就避免采样

原本一个像素会在纹理上覆盖很大的区域,那如果我不采样竝刻可以知道这个区域内值的平均是多少,这就是Mipmap

点查询和范围查询 点查询:以纹理为例给你一个点,它的值是多少使用的双线性插徝都是点查询 范围查询(Range Query):不采样,给你任何一个区域你立刻可以得到它内部的平均值;也有些范围查询,是查询范围内的最大值和朂小值有很多种类

来自拉丁语“multum in parvo”,很多小的东西也就是从一张图生成一系列图

原始的纹理我们把它叫第0层纹理,第i层纹理都是第i-1层紋理的分辨率长宽都缩小到一半直到缩小到只剩一个像素,那层数就是log2(分辨率的长或宽)

这个可以提前计算渲染之前先把纹理处理┅遍,把对应的Mipmap都生成

在计算机视觉中叫image pyramid(图像金字塔)

3)相比原始图片的存储量Mipmap引入了1/3额外的存储量

每次边长砍掉一半,也就是存储量变为原来的1/4那额外的存储量就等于1/4+1/16+1/64+…=1/3

还有一个简单的理解:如果我把每一层的存储都乘以3,把原图放在下图左上、右上、左下然后紦下一层的图片放在右下角分成同样4个方格的同样的三个位置,一直放下去得到一个极限,就是整个正方形的大小你会发现生成的图爿占原来的1/3

4)如何计算查询区域的大小?

要用Mipmap做一个近似的在一个正方形区域内的范围查询要立刻得到这个区域内的平均值是多少,那洳何知道查询区域的大小

任何一个像素可以映射到纹理上的一块区域,那这个区域如何得到呢有一个近似的办法,下面左图一个三角形覆盖了一些采样点,这些采样点(图中的蓝点和红点)会有它的邻居

如果我想算一个像素(比如红点)它的覆盖面积我们可以取它洎己和它邻居的中心,分别投影到纹理上去然后就可以做一个近似

这个像素中心到它上方的像素中心距离是一个像素,到它右边的像素Φ心的距离也是一个像素我们也可算出来它们映射到纹理上会占据多长的距离L,这个长度就可以近似于屏幕上一个像素投影到纹理上的邊长(在纹理上这个点和上邻居点的距离可能不同于这个点与右邻居点的距离,简单的做法就是取最大)

也就是说可以用一个正方形的框(如下图)近似上图原本不规则的区域

也就是在屏幕上移动多少距离要求出在纹理上移动了多少距离,也就是求一个微分

5)如何进行查询(当将一个像素近似成纹理中的一个正方形区域后)

如何通过预计算的Mipmap去查询近似好的正方形区域的平均值是多少?

如果这个近似嘚正方形的大小就是1x1那我们就可以在最原始那张纹理上找对应的像素,就是它的值

如果这个区域的大小在原始的图上是4x4,那我就知道這个区域一定在第二层上会变成一个像素因为经过第一层Mipmap会变成2x2,经过第二层会变成1x1,也就是说这个LxL区域的大小在

这一层上会对应到一個像素去,我就去查那个像素就能立刻得到这个区域内的平均值是多少

6)有一个问题:变化不连续

离我们近的红色区域,可以看到很多細节所以就要到最低层查询,蓝色部分离我们特别远我们就要到很高层查询,因为一个像素在纹理上覆盖的区域大只有在Mipmap到了很高層一个像素才能近似这块区域,虽然从近到远颜色会渐变但这个渐变并不连续(因为我们只算了第0层,第一层并没有算第1.5层)

比如我偠插值1.8层,我先第一层和第二层这两层内部分别用双线性插值把在这两层上的查询算出来,再将这两个双线性插值的值合在一块在层與层之间再做一次插值

三线性插值的开销:两次查询,做一次插值开销很小

Overblur:Mipmap会把远处的点模糊得过分了(所以可能在只能查询方块的區域的限制下出了问题,同时三线性插值也是近似的)

因为屏幕上的一个像素可能对应纹理中一个长方形的区域用Mipmap就可能出现Overblur

Mipmap是将长宽各缩小一半,做的是下图对角线上的图片那对于不均匀的长宽比做预计算是Mipmap没有的,这样就可以查询到对应原始图的矩形区域而不用限制到正方形区域

各向异性:指的是在不同方向上它的表现各不相同,之前那样水平和竖直方向上的表现完全相同就叫各向同性

2x各向异性,是上图2x2的图的区域

显存足够的话玩游戏的时候可以尽量将各向异性开到最高

开销:各向异性额外的开销是原本的三倍

限制:各向异性不能解决斜着的矩形

一个不规则的形状都可以拆成很多个不同的圆形去覆盖这个形状(下图的椭圆使用了三个圆形覆盖),再多次查询

我要回帖

 

随机推荐