GAMES101 学习9——着色(插值、高级纹理映射)

举报
lutianfei 发表于 2022/05/06 17:40:59 2022/05/06
【摘要】 参考资料:https://www.yuque.com/sugelameiyoudi-jadcc/okgm7e/62a9fc0423336a4def6040ac111cf7f7https://blog.csdn.net/qq_36242312/article/details/105861471 一、重心坐标(为了在△内做插值)上一篇文章说到纹理映射,实质上纹理映射就是把uv坐标下的纹素映射到图...

参考资料:
https://www.yuque.com/sugelameiyoudi-jadcc/okgm7e/62a9fc0423336a4def6040ac111cf7f7
https://blog.csdn.net/qq_36242312/article/details/105861471

image.png

一、重心坐标(为了在三角形内做插值)

关于重心坐标更完整的介绍资料见如下链接:https://zhuanlan.zhihu.com/p/58199366

上一篇文章说到纹理映射,实质上纹理映射就是把uv坐标下的纹素映射到图像像素的过程。而我们在画三角形的过程中通常只是定义了三个顶点的属性,其内部属性(法向量颜色值深度值,还有纹理坐标等)往往需要通过三个顶点插值得来到。所以我们需要一个插值的方式来解决这个问题,一般常用重心坐标进行插值。

所谓重心:就是找到三角形ABC内部的一个点 P ( x , y ) P(x,y) ,有 ( x , y ) = α A + β B + γ C (x, y)=\alpha A+\beta B+\gamma C , 且 α + β + γ = 1 \alpha +\beta +\gamma = 1 ,那么 ( α , β , γ ) (\alpha ,\beta ,\gamma) 就是点P的重心坐标,如下图:

image.png

1.1 重心坐标的另外一个定义:重心坐标可以通过面积求出来

Geometric viewpoint — proportional areas
这里 A A , A B , A C A_A, A_B, A_C 分别表示A、B、C点对面的三角形
image.png

1.2 针对任意一个点的公式

image.png

对于刚好在重心(质心)这一特殊点上的坐标:
image.png

1.3 重心坐标不能保证投影后不变

所以三维的要在三维中找到重心坐标再做插值,而不能投影后做插值

1.4 重心坐标的用处

通过坐标A,B,C 其实可以想到,如果把A,B,C换成 法向量(nx,ny,nz),纹理坐标(ux,uy)都是成立的
α \alpha 看做 W v 1 W_{v 1} , β \beta 看做 W v 2 W_{v 2} γ \gamma 看做 W v 3 W_{v 3} ,可得如下方程:
image.png

求解可得:
image.png

image.png

二、应用纹理Applying Textures

简单的纹理映射的过程:在光栅化过程中,对当前扫描到的点的uv坐标进行采样,得到的颜色直接赋给物体

for each rasterized screen sample (x,y):
	(u,v) = evaluate texture coordinate at (x,y)
	texcolor = texture.sample(u,v);
	set sample’s color to texcolor

2.1 简单的纹理映射: 漫反射颜色Diffuse Color

一个点/像素 →(通过重心坐标插值)uv → 查询找到对应texcolor代替kd

image.png

2.2 Texture Magnification纹理放大

应用于纹理太小的情况(纹理尺寸小,像素尺寸大)
当纹理分辨率过小,而需要用来覆盖的物体过大时,多个像素坐标(x,y)都会采样到同一个纹素,产生一些锯齿的效果,这时我们就有一些解决方法,如:最近邻插值,双线性插值,双三次插值,如下图
image.png

2.2.1 最邻近插值

假设红点是像素坐标点对应的纹理像素坐标,那么最近邻插值就是把离它最近的纹素分配给当前像素点,这就容易导致锯齿的出现:
image.png
上图的结果就是红点的纹素应该是它右上角黑点纹素对应的值

2.2.2 双线性插值

双线性插值就是找它周围四个点对应的纹素进行插值,如下图:

  • 找相邻的4个点
  • 做上下双线性插值(水平两次,竖直一次)
    image.png

具体而言:
image.png

  1. 先考虑一维上的线性插值,假设有一个起始点V0和终止点V1,那么V(x)在这两点间的插值就是:
    lerp ( x , v 0 , v 1 ) = v 0 + x ( v 1 v 0 ) \operatorname{lerp}\left(x, v_{0}, v_{1}\right)=v_{0}+x\left(v_{1}-v_{0}\right)
    双线性插值就是在横坐标(竖)上先做两次插值,再在竖坐标(横)上做一次插值即可

2.2.3 双向三次插值Bicubic Interpolation

这里的双三次不同于三维空间中的三线性插值,应该叫做双三次插值(Bicubic),它是对二维空间的插值,考虑了邻近十六个采样点的插值,具体方法同双线性插值。

2.3 纹理分辨率过大

同样,当纹理分辨率过大时,如果以某种角度或者由于物体过远,对纹理进行相同的采样率得到的结果却会远远不同(往往会很差)。通俗来说就是如果我们由近到远看一个物体,同时它的纹理分辨率十分高。当它离我们比较近时,它在我们人眼中的占比非常大,我们需要对纹理有很高的采样率才能把它的很多细节看得清楚。但是当它离我们很远时,假如在肉眼中只有非常小的比例,我们就不需要看到它的很多细节,但是这时我们还是用之前的采样率去对它纹理进行采样,得到的结果就会“乱掉”(摩尔纹),可以看下图:
image.png

实际上一个像素,所覆盖的纹理区域也是不同的,所以当发生这种变化或者一个像素覆盖纹理区域过大时,我们也不应该用同样的采样率对纹理进行采样。

image.png

如何解决这种问题?
解决方法1:超采样-MSAA
但问题是,计算消耗代价太大

解决方法2:不采样:Mipmap

点查询:已知一个点,快速查出其结果是多少。对应纹理上来说就是给定像素点,查得最近邻,双线性,双三次插值的结果
范围查询:不知道哪个点,但是给定了你一个区域,可以直接得到对应的值(平均值之类的),而mipmap就是基于范围查询的

2.3.1 Mipmap

是一种快速,近似,仅限于正方形范围的范围查询
Mipmap就是通过一张纹理,生成许多高层(分辨率每次缩小到一半)的纹理,如下图:
image.png

类似图像卷积中的图像金字塔的概念
image.png

  • 思考:图像金字塔引入了多少额外存储量?
    它的开销也是十分小的,因为每次只存储了上一幅图像的1/4,对于额外开销S ,可以通过错位相减得到 3 S = 1 1 4 n 3S=1-\frac{1}{4^{n}}
    image.png
    又因为 1 1 4 n 1-\frac{1}{4^{n}} 取极限为1,所以S=1/3,所以总开销为 4/3。

  • 如何确定某个像素P对应的Mipmap层级呢?
    首先找到该点和相邻像素点在纹理上的映射点,计算边长L,所以层级可以由 D = l o g 2 L D = log_{2}L 算出(因为随着层数增加,纹理分辨率是以4倍的速度减小,所以L是以2倍的速度增加的)

下图为屏幕上相邻像素点(pixel)映射到纹理上(texel)的对应关系
image.png

当我们得到了层级后,在这一层上进行纹理插值即可。但是我们发现,层级之间是离散的,也就是说,一些像素在0层插值,一些像素在1层插值,这种变化也会使得纹理“割裂”:

image.png

解决方法就是,每次插值,先在D层插值纹理(可以用双线性之类的方法),再在D+1层插值纹理,把两层结果再进行插值(把log的结果去和前一层和后一层进行插值比较),这样的结果就是平滑的了:

image.png

2.3.2 Mipmap的局限性:Overblur过度模糊

Mipmap也会有缺点,因为它的范围只是限定在正方形范围内的,所以会产生over-blur的问题

image.png

解决方法

  1. 各向异性过滤Anisotropic Filtering
    各向异性滤波能够部分解决这种问题,因为它的范围不是限定在正方形范围内的,而是矩形范围。如果说下图中Mipmap的图片缩小是沿着对角线进行的,而各向异性滤波的压缩就是沿着水平和竖直线进行的

image.png

所以它是使得原图的像素可以查询纹理中一些矩形区域,这样就能部分解决一些形变压缩的纹理查询问题:
image.png

  1. EWA过滤
    image.png
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。