求知若饥,虚心若愚
纹理三大问
认知事物:为什么?是什么?怎么样?
纹理的概念
- 通俗来讲是一张图片(实际还有程序化纹理这种解析生成的)
- 对于程序来说:可供着色器读写的结构化存储,常见有一维(NPR会用到一张映射Lut)、二维(最常见)、三维纹理(需要看内部结构,或者一些预计算需要三维参数时)
- 纹理提供了四个通道RGBA,但并不是只能存颜色值,还可以存高度、纹理通道、法线等各种信息
1
Image[height][width][4]
纹理为何重要?
- 通过纹理可以减少几何细节来得到高质量的渲染结果
- 建模工作量减少
- 储存的空间也减少
- 读取的速度加快
- 使用图像函数或其他一些预处理的数据源来修正这个模型的表现,从而模拟物体表面。(即用一些预先处理的数据,来减少实时渲染的压力,同时还能得到很好的效果(欺骗人眼))
纹理管线
投影映射
本质都是将三维的空间坐标点转化为二维的纹理坐标点
- Projector:对于一些简单的几何体,通常用投影的方式,有Spherical、Plane, Cubic 和 Cylindrical形式
- UV Mapping:适用于更复杂的几何体贴图,用于将3维模型中的每个顶点与2维纹理坐标一一对应(展UV),UV map 则需要建模师精心制作,将 uv 坐标保存在顶点信息中(OBJ\FBX等格式中记录),光栅化时会通过插值得到每个片元对应的纹理坐标。(环境光贴图则渲染时计算投影坐标)
变换函数(通讯函数)
- 一般可以进行 坐标范围处理、坐标自由变换、转到纹理空间
- 坐标范围处理:纹理坐标约定是 [0,1] 范围内的值,但也可能超过这个范围,需要定义超出以后怎么处理。(下一节给出)
- 坐标自由变换:进行旋转,平移,缩放等操作,比如想让纹理随着时间运动起来,那么就可以逐帧变换 uv 值。对 uv 坐标施加的变换矩阵,往往是实际想要变换的逆矩阵。可以理解成窗口去观察这张纹理,uv是窗口,窗口往左,纹理就相当于往右。
- 转到纹理空间:[0,1]空间根据纹理大小,扩展到具体的纹理坐标
- 依赖纹理读取:如果采样纹理使用的不是顶点记录的纹理坐标,而是自由变换后的坐标,则会造成性能损耗,所以一般变换操作会放在顶点着色器进行
Wrap Mode 包装模式:Repeat / Mirror / Clamp / Border
定义UV值超出[0,1]时的表现,在DirectX也叫做纹理寻址模式(Texture Addressing Mode)
- Repeat: uv = mod(uv, vec2(1.0,1.0))
- Mirror: uv = abs(mod(uv, vec2(2.0,2.0)) - 1.0)
- Clamp : uv = clamp(uv, vec2(0.0,0.0), vec2(1.0, 1.0))
- Border: 其余地方用背景色填充
纹理采样
理想情况下,屏幕上的像素刚好对应纹理上的纹素,读取对应像素覆盖的那个纹理值即可。但存在角度、远近、分辨率不同等情况,如果只有一张纹理的情况,则会导致一个像素对应多个纹理坐标,又或者多个像素对应一个纹理坐标等情况。
- 纹理分辨率过小,需要放大 (Magnification),上采样
- 纹理分辨率过大,需要缩小 (Minification),下采样
放大 (Magnification)
上采样涉及到重建和采样,重建时需要选择滤波器 (filter),常见的有box、tent、sinc滤波器
对应纹理采样技术中以下三种
-
最邻近采样 (nearest neighbor)
- 用纹理坐标最近的纹素进行纹理采样,容易多个像素读取同一个纹理值,出现块状像素
- 只需要采样一次,效果好
- 适合像素游戏
-
双线性采样 (bilinear)
- 使用最近的四个纹理坐标的纹理值根据纹理坐标差进行插值
- 需要采样四次,并进行三次插值
- 多次采样来减少走样(模糊),画面上币最近邻好一些
-
三次卷积采样 (cubic convolution)
- 考虑周围四个直接相邻像素点的影响
- 考虑它们变化率的影响
- 性能影响较大,需要采样16次
-
Quilez光滑曲线
- 三次卷积采样虽然效果好,但是性能代价也是明显的
- 在双线性采样前对纹理坐标进行光滑处理,来拟合三次卷积采样的结果
- 使用smoothstep 和 quintic对 sinc 函数的近似
效果对比
缩小 (Minification)
将高分辨率的纹理贴到低分辨率的画布,即一个 pixel 覆盖了多个 texel,直接使用原先的采样方法会导致导致的颜色丢失和闪烁情况
根据奈奎斯特定理:当采样频率大于信号最大频率 2 倍的时候,就可以不失真采样。所以我们要提高采样频率或者降低纹理频率
对于纹理采样抗锯齿,其基本思路都是:对纹理进行预处理,建立一种数据结构,在采样时尽可能对 pixel 覆盖的 texel 区域的平均值进行估计,以下有三种方法
-
MipMap 多级渐远纹理
-
每一级是由上一级相邻四个像素平均成一个像素得来,即每一级是上一级的1/4存储空间,整张mipmap会比原来多占1/3空间,可用等比数列极限求得(也可以把第0级复制三份按正方形的前三格放置,这时候第四格是空的,将第1级也复制三份也按正方形放置在第四格,持续下去发现每次都差一小格,但是这个格越来越小,最后新增的部分相当于初始第四格即第0级的大小,但我们是将新增的大小*3,所以除以3就是新增了约1/3)
-
Mipmap 的建立取决于两个因素:滤波方式(有box和高斯滤波,后者效果较好)和伽马校正(纹理一般存储非线性空间,转换回线性再缩放)
-
Mipmap的使用,求第几层,一种确定 d 的方法是:求出像素覆盖四边形最长边的长度,然后取 2 的对数,可以得到 d 的值,一般是个小数。也有求取 d 的其他方法,如:求纹理坐标对横纵两个方向差分的最大值,以替代最长边的长度。由于是小数,后面使用三线性插值来获取具体纹理值,也就是在相邻两层使用双线性插值求纹理值,最后在两层之间插值。
-
这里视频中提到GPU是每四个像素一组并行执行,也是为了能方便算出差分值
-
优点:占用内存固定,且使用时计算量固定
-
缺点:各向同性,当同一个 pixel 在横纵方向覆盖的 texel 的个数不同时,却对横纵两个方向计算了相同的纹理细节等级。横纵差别过大时会过度模糊。使用Ripmap也可以某种程度解决这个问题,但会造成存储量剧增
-
-
积分图 Summed-Area Table
-
是一种各项异性技术,其思想为:确定该 pixel 在纹理空间上覆盖 texel 区域四边形的轴向最小 包围盒 (Axis-Aligned Bound Box, AABB),然后求取该包围盒的纹理值的平均值,作为该 pixel 的采样纹理值 c
-
由于使用类似前缀和的技术,后面的数字会很大,所以需要提高储存精度
-
SAT 技术用 AABB 去近似 pixel 覆盖的 texel 区域,可以预想,当该区域很狭长且沿对角线朝向时,误差会很大,还会引起过度模糊
-
优点:解决了横向和纵向过度模糊与抗锯齿的平衡问题
-
缺点:SAT 纹理内存占用高;不能解决对角线方向的过度模糊问题
-
-
各向异性过滤 (Anisotropic Filtering)
-
由于大部分硬件都支持了 Mipmap 的特性,所以很多各向异性过滤技术也是依赖 Mipmap 算法进行改进的。
-
其中一种算法 Texram 是:沿着主方向增加采样点,再平均混合
-
使用四边形的短边来进行Mipmap层级计算,避免过渡模糊,同时用长边确定主方向,根据长短边比值来决定在长边采样几次,最后进行混合
-
游戏中各项异性选项有 x4、x8 和 x16,该选项决定了在主方向最大采样点的个数,即CLAMP
-
效果对比
纹理的优化与应用
DrawCall的概念与数组图集(CPU优化方面)
- 避免GPU花费太多时间等待CPU提交DrawCall(阻塞)
- 常见的纹理优化有纹理图集(碎图打包成大合集,从而不用频繁切换纹理)、纹理数组、无约束纹理
GPU渲染优化:带宽入手 / 纹理压缩
- 减少纹理占用的内存
- 减少包体大小
- 减少带宽计算压力
- 内存使用效率更高
Cubemap 立方体贴图
- 工程使用详见入门精要第十章,以及知乎文章https://zhuanlan.zhihu.com/p/370927083
- 科学原理可以看GAMES202关于环境光的讲解,讲到了几个方法,常用的是Split Sum
凹凸贴图 Bump Mapping 和 位移贴图 Displacement Mappping
- 位移贴图需要顶点数多的时候才能更好发挥作用,可以用曲面细分来新增顶点
- 工程使用详见入门精要第七章,以及知乎文章https://zhuanlan.zhihu.com/p/370927083
- GAMES101几何部分
参考链接
- 课程PPT:https://docs.qq.com/doc/DUFdKZE1oVFd3ZlBs
- 图形学基础 - 纹理 - 纹理映射流程:https://zhuanlan.zhihu.com/p/369977849
- 图形学基础 - 纹理 - 纹理映射盘点:https://zhuanlan.zhihu.com/p/370927083
- 图形学基础 - 着色 - 采样理论:https://zhuanlan.zhihu.com/p/361383661
- GAMES101:采样和几何部分