思维导图:
纹理:一种容器,除了包含具体储存的信息,还包含纹理采样的一些设置
二维纹理:宽+高+想要储存的信息(比如RGBA值/高度/纹理通道/法线等,也可以采样时给个函数自己算)组成的三维数组、i和j对应像素点,k代表存储的信息
一维纹理:i=0或者j=0的二维数组
三维纹理:一层层二维子对象矩形构成的四维数组
投影函数:获取要渲染的位置,将它从模型空间投影到纹理坐标空间中,转化成纹理坐标(UV坐标),将结果存储在顶点数据中。
一般情况用不到,直接使用存储在模型顶点数据中的投影结果(特殊:环境贴图。需要特定的投影函数去逐项评估)
此处指纹理投影,不是摄像机投影,它常用于建模流程中的“展UV阶段”
建模程序中常用函数:球形、圆柱形、平面投影
通讯函数:将UV坐标进行灵活的扩展,比如平移缩放旋转/控制图像的应用方式,得到处理后的纹理坐标,去获取纹理值。
纹理采样:获取纹理值的过程。
着色器中的纹理通常以Sample Variable即采样器变量的形式存在,常见于代码里的Sample。是一种Uniform形的变量,即在处理不同片元时,变量一直不变。一个Sample对应一个Texture,是一种特殊的变量。二维纹理即Sample2D。
依赖纹理读取:当使用Texture2D或类似方式访问纹理时,如果由顶点着色器将没有经过修改的数值直接传给像素着色器,即这一数值还需要计算,就会产生“依赖纹理读取”,哪怕只是需要交换UV的两个坐标,也会影响性能。所以我们常把UV偏移相关的计算放到顶点着色器去完成。
大部分的实时渲染中用到的是图像纹理:用Lookup函数的方式来索引值;也有程序纹理,但它不涉及内存查找,只是函数计算,获取到的结果也常是RGBA值/表面粗糙度等。
模型上的一个点坐标为(x,y,z),经过投影函数换算后获得uv值(u,v);此时我们知道这张纹理的大小是256×256,就把uv坐标和256相乘;后续根据纹理采样的设置(包含在纹理对象里)获取具体纹素(纹理图像中的像素)的颜色。
Filter Mode:纹理因变化而拉伸时,采用哪一种滤波方式来调整它自身表现;或者说不同形状大小角度缩放比的情况下,如何应用纹理来使得采样更加合理。
即使在纹理像素大小和屏幕完全对应的情况下,也有可能没有对齐或发生旋转;或从某些角度来看,发生了一个像素覆盖多达四个相邻像素的情况,所以需要过滤。
最近邻:放大时每个像素读取最邻近纹素
缺点:多个像素会读取同一纹素,导致像素化
优点:每个像素只需要获取一个纹素,省消耗
双线性插值:每个像素点找到四个相邻像素点在二维空间上进行线性插值得到像素的混合值。计算量是最近邻的四倍,效果好一点。
立方卷积插值/双三次插值:效果好但代价高
光滑曲线插值与双线性插值很像,就是在纹理坐标带入到双线性插值的过程之前,额外做了一步处理
兰索斯插值:8×8=64个像素
奈奎斯特提出采样定理:对于一般纹理,每个像素最多应有一个纹理来避免混点。为了达到这个目的,要么提高像素的采样频率,要么降低纹理的频率。前两种方法只能有限地提高采样频率。
降低纹理频率最常见的方法就是mipmap:通过预处理纹理并创建数据结构来快速地计算一组纹素对一个像素效果的近似值。
原始图像零级作为金字塔底部,四个相邻纹理的平均值作为下一级的纹素值,所以新一级的子纹理时上一级的四分之一大。由于每一级的纹理大小都是上一级的四分之一,所以整套纹理只比原本的纹理多了三分之一的内存。
使用像素单元格所形成的四边形最长边来近似像素覆盖的范围,而四边形的两条边都可以用求偏导来获得。
GPU并不是一个一个像素执行的,而是把像素们分成2×2的一组,分块并行执行,目的是为了算ddx和ddy以及法线。
由于这样求出的不会是整数,而level要求是整数,就会造成静态的块间过度不平滑和动态纹理切换时的变化(比如帧之间用了不同的level)。三线性插值可以解决这个问题:对最接近的两层mipmap level分别进行双线性过滤,对结果再进行一次双线性插值。
Mipmap的优点:不需要实时累加所有影响像素的纹素,只需访问预处理的mipmap图集。所以无论得到的level是多少,访问消耗的时间相同。虽然整一套mipmap会使内存占用增加三分之一,增加包的体积,但消耗带宽会更少。
Mipmap的严重缺陷:过度模糊(overblur)。因为我们一直假设texture投射到屏幕空间时是各向同性的,而如果一个像素单元格在u方向覆盖了大量纹理而v方向上只覆盖了少量纹素时,按照mipmap取上一级平均的做法就会使纹素局限到正方形中。各向异性过滤(Anisotropic Filtering)可以部分解决这一问题。
各向异性过滤
Ripmap:不止做正方形的预处理,还有各种比例的矩形预处理(内存也会变成三倍)。但无法解决其他视角下(比如斜着)的情况。
EWA过滤:将像素影响区域用椭圆来近似,并经过多次查询加权平均数,质量虽好代价也大。
积分图SAT:有效地检索到非正方形区域上的纹素均值。做法就是创建一个和纹理大小相同但储存颜色精度更高的数组(比如转RGBA的8位提到16位或更高)。这个数组的每个位置以左上00为纹素的原点,每一个位置都要计算并储存这个位置和纹素原点形成的矩形所对应的纹素总和,查询时就可以通过这个表快速获取矩形的总颜色,除以宽高即得均值。28+5-8-17=1+2+1+0+2+2。套用公式,给四个点就可以实现对任意矩形得内部纹素平均值进行快速运算。精度问题:越靠右下角数据越大,需要很高的精度,导致内存增加。
屏幕像素反向投影到纹理空间,形成一个不规则四边形,选择要覆盖方块的最短边来确定level,较长边就会创建一条各向异性线,穿过方块的中心,按照过滤等级的高低沿线多次采样并合成,得到最终结果。随着各向异性越高,沿轴方向采样的次数就会越多。弥补了Ripmap的缺陷,内存也只需要多三分之一。
一般,各向异性过滤都是基于三线性过滤的,所以当u:v≠1:1时,各向异性过滤就要比三线性插值采用更多的点,具体采样多少取决于比例(最大16x=128)
在每次调用DrawCall之前,CPU会向GPU发送很多内容,比如数据状态等,而GPU处理速度比CPU快,所以如果DrawCall命令如果太多,会耗费CPU的处理时间。
降低DrawCall,避免渲染时频繁改变纹理带来的消耗
纹理图集(最常用):合并大量小纹理为一张大纹理图
纹理数组
无约束纹理
纹理除了存颜色还可以存别的信息:
适用于表达环境光照信息,常用于环境贴图
存储相对的高度。计算顶点光强时不直接使用顶点的原始法向量,而是加上一个从高度图找到的扰动来修改法向量,再经过光强计算就可以产生一个凹凸效果。
想要位移贴图有效,表面就必须由大量顶点构成。如果三角形顶点之间的间隔低于纹理定义的频率,则低于的部分就改变不了。解决方法:检测低于采样频率的地方,把大的三角形拆成很多个小的(DX提供的动态曲面细分)。