跳转至

4 Texture Mapping

文本统计:约 2676 个字 • 4 行代码

Every 3D surface point also has a place where it goes in the 2D image 将三维表面映射到二维表面

Each triangle vertex is assigned a texture coordinates \((u,v)\) [ \(u\) and \(v\) are both in range \((0,1)\)]

4.1 Barycentric coordinates

引入重心坐标的目的是在三角形之间进行插值。为什么要进行插值?因为我们想要在三角形上通过顶点处的值得到平滑的变化值。纹理坐标,颜色,法向量都是我们可以插值的。

A coordinates system for triangles \((\alpha,\beta,\gamma)\).

Points \((x,y)\) can be presented by \((\alpha,\beta,\gamma)\) , if \((x,y)=\alpha A+\beta B+\gamma C\) and \(\alpha+\beta+\gamma=1\).

Inside the triangle if all three coordinates are non-negative.

By barycentric coordinates, we can linearly interpolate values at vertices. 通过重心坐标,我们可以根据顶点处的值对三角形内部进行线性插值。

\[ V=\alpha V_A+ \beta V_B + \gamma V_C \]

\(V_A,V_B,V_C\) 可以是位置,纹理坐标,颜色,法线,深度,材质属性…

However, barycentric coordinates are not invariant under projection,所以我们在处理3D的图像时,应该先算重心坐标(无论是深度还是什么属性,然后再投影)

透视插值可参考:[数学] 重心坐标插值与透视校正插值 - 知乎

4.2 Applying Textures

简单的纹理应用:定义漫反射系数 Simple Texture Mapping: Diffuse Color

for each rasterized screen sample (x,y):        //Usually a pixel's center
    (u,v) = evaluate texture coordinate at (x,y) //Using barycentric coordinates
    texcolor = texture.sample(u,v);
    set samples color to texcolor; // usually the diffuse albedo Kd

4.3 Texture magnification

下面所说的纹理大还是小,可以理解为纹理的分辨率大还是小。

4.3.1 What if the texture is too small?

我们模型的分辨率非常高,但是我们的贴图的分辨率比较低,就会导致我们一个点映射到纹理上的 \((u,v)\) 很有可能并不是整数,我们有几种处理方法。

Note

纹理上的像素被称为 texel

  • Nearest

We just can take the nearest integer point.

  • Bilinear interpolation 双线性插值

取4个最近的样本位置,纹理值已被标记在图上。同时得到分数偏移量 \((s,t)\)

Linear interpolation (1D) 一维插值

\[ lerp(x,v_0,v_1)=v_0+x(v_1-v_0) \]

先进行两次水平的插值

\[ u_0 =lerp(s,u_{0,0},u_{10}),\quad u_1 =lerp(s,u_{01},u_{11}) \]

最后进行依次竖直插值得到

\[ f(x,y)=lerp(t,u_0,u_1) \]
  • Bi-cubic:以16个最近的样品点为例,成本很高。

4.3.2 What if the texture is too large?

如果纹理比较大会造成严重的走样现象

原因是近处一个像素覆盖的纹理区域相对较小,远处一个像素覆盖了很大一片纹理。屏幕上各个像素覆盖的纹理区域各不相同。如果一个像素覆盖的纹理区域较小,用这个像素的中心查询一下纹理的值,没有什么问题。但是一旦一个像素覆盖了很大一片区域的纹理(比如远处接近地平线的纹理),现在我还用一个像素的中心去查这块儿区域,用一个点的值去认为是这一个区域的值,那显然是不对的。用一个点的值无法代表一块纹理区域的颜色变化。

增加取样点显然有效,但是花费变得非常高。一个像素内纹理的变化过快,需要更高的采样频率

Point Query vs. (Avg.) Range Query

点查询:以纹理为例,给你一个点,它的值是多少,可以用双线性插值来解决,这是点查询。

范围查询:给你任何一个区域,你立刻可以得到这个范围内的平均值,这是范围查询。(范围查询还有查询一个范围内最大值和最小值,这类问题跟现在所说的完全不一样)

4.3.3 Mip-map

—— Allowing (fast, approx., square) range queries 做快近似方形的范围查询

我们生成一系列的纹理,像下图所示【可以提前生成】

How to compute Mip-map level D ?

Estimate texture footprint using texture coordinates of neighboring screen samples

\[ D=log_2max(\sqrt{(\frac{du}{dx})^2+(\frac{dv}{dx})^2},\sqrt{(\frac{du}{dy})^2+(\frac{dv}{dy})^2}) \]

我们可以用如上图所示的一个正方形框来近似这样一个不规则的区域。

当我们把一个像素在纹理上覆盖区域近似成一个正方形时,如何根据我刚刚已经算好的Mip-map,去查询这样一个边长为L正方形的区域的平均值是多少?

如果这个正方形区域就是1×1,那么就表明一个像素正好对应一个边长为L的正方形区域,也就可以直接在最原始(第0层,D=0)的纹理上找对应的像素,就是它的值。

如果这个正方形区域是2×2,那么这个区域会在第1层(D=1)上对应一个像素

如果这个正方形区域是4×4,那么这个区域会在第2层(D=2)上对应一个像素

对于L×L大小的正方形,一定会在 \(D=\log_2L\) 层上对应到一个像素。

因此我们只需要算出D,即在第几层正方形的区域对应一个像素,就可以得出这个区域内平均值是多少。

我们发现这个图并不是连续的,还是使用插值方法 (Tri-linear interpolation) 使图像连续。

Tri-linear interpolation

所谓三线性插值,就是在向下取整的D level上进行一次双线性插值(前文提过),再在D+1 level之上进行一次双线性插值,这二者数据再根据实际的连续D值在向下和向上取整的两个不同level之间的比例,再来一次线性插值,而这整体就是一个三线性插值了。

Mip-Map limitations

我们发现使用Mip-Map有一个问题就是在远处的所有细节都丢失了 OverBlur

造成 Overblur 的原因在于 Mip-Map 采取的是方形求平均,面对 footprints 为长方形等等,我们就会取一个更大的正方形,这样就会造成overblur

Solution : Anisotropic Filtering (各向异性过滤),我们不仅仅解决方形的,我们还解决长方形的。我们如下图构建 Rip-Map (存储的内容就变成原来的三倍了)

可以查找轴对齐的矩形区域

其实各向异性过滤仍不能解决 diagonal 的 footprint,因为各向异性只能解决水平或竖直的不同大小的矩形footprint

我们还有其他的方法,比如说 EWA filtering ,可以解决对角线的情况

4.4 Applications of texture

texture = memory + range query (filtering) 在GPU中我们可以把纹理理解为一块内存,并且可以做相应的点查询与范围查询

4.4.1 Environment Map

环境贴图,制造环境光,描述来自不同方向的光照信息,照亮整个环境中的物体,也可以被环境中的物体反射。环境光只记录光的方向,即假设环境光来自无限远处,从同一个方向照过来的光强度都是一样的

如何记录环境光?

(1) Spherical Environment Map

可以把整个环境光记录在球上,用球去存储环境光的信息

缺点在于我们来看一个环境光球展开后的顶部和底部,可以发现出现了不同程度的变形。

(2) Cube Map

用一个立方体盒子,把这个球包围起来。当光从中心打到包围的球上时,我们不管,让光继续走,直到碰到立方体的一个面,记录下来信息。这样可以减少扭曲。

4.4.2 Texture can affect shading

贴图不光可以表示颜色信息,还可以表示高度信息。高度贴图可以定义任意一个点,沿着他的法线向上向下走的相对高度。如右图,原本就是一个很普通的球,可以用很少的三角形来表示,如果想通过直接改变模型的方法得到右边那种凹凸不平的效果,会用很多三角形,开销会很大,那么在这里通过凹凸(高度)贴图,改变高度信息,高度信息变化,就会使法线信息变化,从而使着色结果发生变化(人们看到的明暗对比一定程度上就是因为法线变化),得到右图的效果。

纹理贴图并未改变几何特性,只是通过改变高度从而影响法线的方向以改变着色结果。

Bump Mapping

在不添加更多的三角形的情况下,添加更多表面的细节

  • Perturb surface normal per pixel
  • “Height shift” per texel defined by texture
  • Modify normal vector

(1)在二维平面上,怎么对法线进行扰动?[flatland case]

设原先表面的法线为 \(n(p)=(0,1)\),扰动后计算切线(利用相邻两点的切线)

\[ dp=c*[h(p+1)-h(p)] \]

然后将切线旋转90度,得到相应的法线

\[ n(p)=(-dp,1) \]

最后再归一化就可以了

(2)在三维立体空间中,怎对法线进行扰动?

设原先表面的法线为 \(n(p)=(0,0,1)\),求出相应的梯度

\[ \begin{aligned} \frac{dp}{du} = c1*[h(u+1)-h(u)]\\ \frac{dp}{dv} = c2*[h(v+1)-h(v)] \end{aligned} \]

从而得到扰动后的法线为

\[ n=(-dp/du,-dp/dv,1) \]

最后再归一化就可以了

c1,c2 表示纹理定义的法线对真实物体法线的影响系数(程度)

我们可以构建一个局部的坐标系,这样我们的法线都是 \((0,0,1)\) ,之后得到原先坐标系的法向量只需要做一个坐标变换就可以了。

Displacement mapping —— a more advanced approach

改变了几何的形状!改变了顶点的位置。

真实改变高度,解决了前一个方法边缘处以及凸起投影到自身无法显现的不足。但是我们需要原本几何体的三角形足够细,因为改变的是三角形顶点的高度,模型需要跟得上纹理的变化速度。

4.4.3 3D Procedural Noise + Solid Modeling

定义了三维空间中的噪声函数,并没有真正生成一张图像

函数可以经过各种处理变成自己想要的样子,如使用纹理表示山脉起伏的信息

4.4.4 Precomputed Shading

环境光遮蔽(Ambient occlusion)可以==提前==计算好储存在贴图中,使用时将其他着色结果乘以环境光遮蔽的结果得到带有环境光遮蔽的结果

4.4.5 Solid modeling and Volume rendering

三维纹理广泛应用到体渲染之中,比如在医学领域使用核磁共振得到三维空间中的密度信息进行渲染

评论区

对你有帮助的话请给我个赞和 star => GitHub stars
欢迎跟我探讨!!!