GAMES101——Lecture 09: Shading 3 (Texture Mapping Cont)

  • 重心坐标,为了做插值
  • 纹理如何贴在物体表面
  • 纹理的其他应用

3.1 如何在三角形内部进行属性的插值?

3.1.1 重心坐标

为什么要在三角形内部做插值?

  • 当我知道三角形三个顶点的属性的时候,我们希望通过此知道三角形内部的一些值。并且插值可以做到平滑的过渡。

插值什么内容?

  • 可以在三角形顶点上定义各种不同的属性,可以通过插值此知道三角形内部点的属性。这些属性可以是位置,纹理坐标,颜色,法线,深度,材质等。

如何做插值?

  • 通过重心坐标。
什么是重心坐标?

重心坐标是定义在一个三角形上的。在三角形 ABC 所形成的平面内的任意一点(x, y) 可以表示成三个顶点坐标的线性组合。注意!需要满足一个条件:线性组合的三个系数加起来等于 1。这三个系数的组合就是点(x, y)在该三角形的重心坐标下的表示。

在这里插入图片描述

由于“系数加起来等于 1”这个条件的限制,因此重心坐标虽说是三个数,但只要知道两个数的话,就可以 1-这两个数的和,求得第三个数了。这样其实也是有道理的,毕竟我这是个二维平面,本来只用两个数就可以表示坐标了,不用非得三个数。

如果这个点在三角形内,还需要满足另一个条件:这三个系数都是非负的。当这个条件满足的时候,我们就知道这个点一定在三角形内。如果不满足这个条件,但满足三个系数和为 1 的话,那么表示这个点在三角形所在平面,但有可能在三角形外。

从定义,我们也可以知道△ABC 所在三个顶点的重心坐标:A(1, 0, 0)、B(0, 1, 0)、C(0, 0, 1)。

如何求得三个系数,也就是如何求得重心坐标呢?
方法一:面积比

**三角形内的点将三角形划分成了三个区域,利用这三个区域的面积比求得对应的三个系数。**顶点 A 对面的三角形区域被称为 A A A_A AA,顶点 B 对面的三角形区域被称为 A B A_B AB,顶点 C 对面的三角形区域被称为 A C A_C AC。如下图左。

三角形的重心有个特殊的性质,可以将三角形 ABC 分成面积相等的三部分。因此三角形重心的重心坐标是( 1 3 \frac{1}{3} 31, 1 3 \frac{1}{3} 31, 1 3 \frac{1}{3} 31)。

方法二:公式法

已知三角形的三个顶点坐标,就可以用公式求得这三个系数是多少。如下图右。

在这里插入图片描述

我们只要知道:对于三角形内任意一个点,都可以计算它的重心坐标就可以啦。

3.1.2 求得重心坐标后,如何用它进行插值呢?

求得了重心坐标后,就可以用重心坐标去计算三角形内部任意一个点的额插值。我要插值的属性同样也能用重心坐标去把它用线性组合表示出来。 意思是:假如三角形顶点有多个属性,那么我就可以通过三角形内部的一个点,它的重心坐标 (α, β, γ),将它的属性通过这个坐标给线性组合起来,得到该点的属性值。

总结:先算出重心坐标,然后用重心坐标和三角形顶点的属性信息做一次线性组合,得到我们想要的三角形内部点的属性信息。

但是请注意❗️在投影下,这个三角形的形状可能会发生变化,因此也是不能保证重心坐标不变的。总结:经过投影后,三角形的重心坐标有可能会发生改变。

因此,如果我们要插值三维空间中的属性,就应该取三维空间中的ABC坐标来算它的重心坐标是多少 ,再去做插值。不能在投影之后的坐标里面做插值。

特别说一下深度。光栅化的时候,三角形都被投影到屏幕上去了,三角形会覆盖很多的像素,像素都有中心。对这些中心点可以知道它在投影了的三角形的哪里,然后求出重心坐标,对投影了的三角形顶点的深度做插值。这个操作方式得出的结果是不对的。

正确操作姿势:找到像素中心点对应在三角形的位置它在三维空间中的坐标。然后在三维空间中对三角形顶点的深度做插值。(应用逆变换将已经投影到二维平面上的三角形再投影回去。)

在三维空间中的属性就在三维空间中做插值,然后再把它对应到二维空间中去。

根本原因就是:重心坐标在投影操作下可能会发生变化。

3.2 如何将纹理应用在实际的渲染中?

每个三角形顶点都会对应一个纹理坐标(u, v),那么三角形内部的采样点就可以通过重心坐标插值来求得采样点自己对应的纹理坐标属性。然后再根据这个纹理坐标查看纹理图上该坐标对应的颜色,最后将这个颜色给涂在相应的采样点上。

3.2.1 纹理的放大(纹理太小了怎么办?)

看着很高清的一个墙,上面贴的图却很低清,这种情况怎么办呢?

纹理过小导致的问题描述

对于任何一个采样点,都可以找到它对应的纹理位置。但是这个位置可能不是整数,不是整数怎么办呢?就把它四舍五入为整数。那么在一定的范围内,我们要查找的是相同的纹理上的像素。

纹理上的像素,叫做纹理元素,或者纹素texel。

**会出现一种现象:一个像素的周围很多像素都会被映射到同一个纹素上。这就是因为纹理太小了。**这样的图片会看起来有一个个格子,效果并不是很平滑自然。

这就引出需要解决的一个问题:那当我们查到的纹理坐标是非整数的时候,应该如何处理呢?往下看解决方法:

双线性插值

首先利用水平的两个顶点的颜色,进行水平插值得到 u 1 u_1 u1 u 2 u_2 u2的颜色值;然后利用 u 1 u_1 u1 u 2 u_2 u2的颜色值进行竖直方向上的插值得到我们想要的红色点的颜色。

最终红色点所对应的颜色就综合考虑了周围四个点的颜色,可以在四个点所围成的区域内,取得一个平滑的颜色。

找最近的四个点做双线性插值就可以得到平滑过渡的结果。

在这里插入图片描述

双向三次的插值

不是取周围的 4 个纹素,而是取的 16 个。然后也是先做多次水平上的插值和垂直方向上的线性插值。

这种做法的运算量比双线性插值的要大很多,但是效果也会好很多。

在这里插入图片描述

3.2.2 如果纹理太大了怎么办?

纹理太大导致的问题描述

在这里插入图片描述

近处出现锯齿现象,远处出现摩尔纹。

近处一个像素覆盖的纹理区域其实相对较小;在远处,一个像素其实覆盖的是一片纹理,是比较大的区域。这告诉我们:屏幕上的像素覆盖的纹理上的区域大小是各不相同的。

如果说对于一个像素,它覆盖的纹理区域挺小,用这个像素的中心计算到的纹理坐标查询一下它的值。得到这个像素区域所覆盖纹理的值,可以近似的这样认为没问题。

但是当一个像素覆盖的是一片很大的纹理区域,它使用点采样所对应到的颜色值就可以代表这个纹理区域的平均颜色值吗?当然不可以,一个像素点是不能对应这么大一块纹理的,纹理中都有不同的颜色变化啊。因此当一个像素点对应的是比较大的一块纹理区域的时候就不能使用点采样。

在这里插入图片描述

**本质原因:当这个纹理特别大的时候,一个像素里面有可能包含特别大一块纹理。这块纹理一直是在变化的,也就是说这个像素内部频率变化很高,可是你只用了一个采样点(像素)去采样它,这样效果肯定不对。**应该需要一个更高频的采样方法。

解决方法一:超采样

将每个像素分成 512 个采样点,然后把每个采样点在纹理上对应的位置都计算出来。效果肯定特别好,但是计算量和花销也是相当大的。

解决方法二:Mipmap

提供另一个完全不一样的思路。避免采样,立刻就可以知道一个像素对应的这个纹理区域中的平均值是多少。

mipmap 允许做快速的、近似的、正方形的范围查询。

什么叫mipmap?

从一张图生成一系列图,每一张图都是上一张图缩小到一半。因此一共可以生成 l o g 2 N log_2 N log2N层。如下图,当原始图是128x128大小的时候,可以生成7层mipmap。

在这里插入图片描述

在渲染之前,将这些对应的 mipmap 都生成好存储起来。因此使用 mipmap 会占用更多的存储量,但只是多出了原始图片存储量的三分之一。

用 mipmap 做一个近似的在一个正方形区域内做范围查询,要立刻得到这个区域内的平均值。

如何知道要查询的正方形区域有多大?

**有一个近似正方形区域的方法:通过使用屏幕上的相邻采样点的纹理坐标,计算纹理坐标之间的距离,将较大的距离作为正方形的边长,用这个正方形来近似该采样点的纹理区域。**具体理解如下:

当一个三角形覆盖了一堆采样点,可以取采样点自己的中心和采样点邻居的中心,分别投影到纹理上去。

将采样点上面和右边的点投影到纹理上,然后分别计算,在纹理空间中,采样点到采样点上面和右边的距离。当然这两个距离会有所不同,简单起见,就取较大的距离值来近似正方形的边长。这个正方形的面积对应的就是像素点投影在纹理上的面积大小。

在这里插入图片描述

如何用mipmap来查询正方形区域的平均值?

重要的是:当我们将像素点覆盖的纹理区域近似成一个正方形之后,如何根据之前预计算好的mipmap,来查询这个边长是L的正方形区域的值的平均值是多少?

如果这个区域的大小就是1x1 一个像素,那就可以在没有做mipmap的最原始的那张纹理上找对应的像素就是它的值。如果这个区域的大小是4x4,那么第1层就是2x2,这个区域到了第2层上就一定会变成1个像素。查询第2层的像素,就可以知道4x4所对应的一个像素颜色是多少。

总结:L x L这个大小的区域,到了第 l o g 2 L log_2 L log2L 层就一定会对应到一个像素上去。直接去查这一层的像素,就可以立刻得出L x L这个区域的平均值是多少。

每个像素都会投影到纹理上,计算这个像素对应多大区域,然后确定应该去第几层去找这个平均值。

用三线形插值(正方形)来平滑过渡层与层之间

下图做了一个可视化。离屏幕比较近就会看到很多细节,应该在较低层去找,离屏幕比较远的话,应该去较高层查询颜色的平均值。但是从图中可以看出,颜色之间的衔接处比较生硬,我们所期望的应该是一个渐变的颜色,得到平滑的过度,这里就可以采用插值的方式。

在这里插入图片描述

有第1层和第2层,想查询第1.8层的值。做法:对第1层和第2层的内部,分别做双线性插值。接着将插值后的两个结果放在一起,在层与层之间再做一次线性插值。这样不管是整数还是浮点数层,就可以通过“三线性插值”的方式,只做查询一次,得到它覆盖的纹理区域面积颜色的平均值。

三线性插值的应用十分十分广泛,因为它可以得到一个完全连续的表达。它本身的开销挺小的,就是做两次查询和一次插值。
在这里插入图片描述

各向异性过滤 Ripmap(矩形区域)

用mipmap是否能完全解决问题?

并不能。对于远处会有过度模糊的问题。

各向异性过滤的效果会比三线性插值要好。比mipmap多了不均匀的水平和竖直方向上的压缩。

屏幕上的像素映射到纹理上,不一定都是一个规律的形状,很有可能会出现一些不规则的,斜着的形状。如果还用之前的正方形来近似这样的不规则形状就会十分不准确,会造成过度模糊的问题。

引入了各向异性过滤后,对于这种长条状的图形,会得到一个几乎完美的解决。各向异性过滤允许我们对这样的长条形区域做一个快速的范围查询,这样就不用限制在只能用正方形来近似纹理区域。

但是各向异性过滤其实只解决了一部分问题,对于矩形的查询是可以的,比正方形的mipmap查询效果要好很多。但是对于斜着的这种区域没有办法。
在这里插入图片描述

EWA过滤(不规则形状,椭圆)

假设你有任意的一个不规则的形状,可以把不规则的形状给拆成很多不同的圆形去覆盖这个不规则的形状。

这个椭圆,可以拆成3个不同形状的圆形去覆盖它。多次查询,自然就可以找到覆盖这样的不规则形状的椭圆。

代价就是多次查询。质量越好,代价越大。

在这里插入图片描述

整体思路:先算出mipmap等级,然后算出椭圆系数,最后根据椭圆系数确定椭圆的包围盒。对于包围盒内的纹素进行遍历并作加权平均,得出结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值