(个人笔记,由于刚开始学习再加上英语不太好,所以有的地理解的可能不太对,望指正)
Chapter 11. Texture Mapping
11.1 Looking Up Texture Values
ϕ
\phi
ϕ表示将表面坐标S转换到纹理坐标T当中
ϕ
:
S
→
T
:
(
x
,
y
,
z
)
↦
(
u
,
v
)
\phi : S \to T \\ : (x, y, z) \mapsto (u, v)
ϕ:S→T:(x,y,z)↦(u,v)
texture mapping的需要解决的两个:
- 定义纹理坐标函数
- 查找纹理值并避免产生过多的走样
11.2 Texture Coordinate Functions
texture mapping需要满足的几个目标
- Bijectivity(双射性)
- Size distortion:相邻点的距离在影射前后的距离不能发生太大变化
- Shape distortion:映射前后形状不能发生太大变化
- Continuity: ϕ \phi ϕ应该是连续的
测试纹理图
Planar Projection
影射函数可以表示为:
ϕ
(
x
,
y
,
z
)
=
(
u
,
v
)
w
h
e
r
e
[
u
v
∗
1
]
=
[
x
y
z
1
]
\phi (x,y,z) = (u,v)\qquad where \qquad \begin{bmatrix} u \\ v \\ * \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix}
ϕ(x,y,z)=(u,v)where⎣⎢⎢⎡uv∗1⎦⎥⎥⎤=⎣⎢⎢⎡xyz1⎦⎥⎥⎤
测试结果
Spherical Coordinates
球面影射就是将球面坐标 ( ρ , θ , ϕ ) (\rho, \theta, \phi) (ρ,θ,ϕ)丢弃 ρ \rho ρ坐标并将 θ \theta θ跟 ϕ \phi ϕ映射到[0, 1]之间,影射函数可以表示为
ϕ ( x , y , z ) = ( [ π + a t a n 2 ( y , x ) ] / 2 π , [ π − a c o s ( z / ∥ x ∥ ) ] / π ) \phi (x, y, z) = ([\pi + atan2(y,x)]/2\pi, [\pi-acos(z/\begin{Vmatrix} x \end{Vmatrix})]/\pi) ϕ(x,y,z)=([π+atan2(y,x)]/2π,[π−acos(z/∥∥x∥∥)]/π)
示意图跟实验结果如图所示
球坐标说明
https://blog.youkuaiyun.com/reborn_lee/article/details/82497577
Cylindrical Coordinates
圆柱影射函数
ϕ ( x , y , z ) = ( 1 2 π [ π + a t a n 2 ( y , x ) ] / 2 π , 1 2 [ 1 + z ] ) \phi (x,y,z) = (\frac{1}{2\pi}[\pi + atan2(y,x)]/2\pi,\frac{1}{2}[1+z]) ϕ(x,y,z)=(2π1[π+atan2(y,x)]/2π,21[1+z])
实验结果
Cubemaps
对于正方体也可以使用球形影射,但是在极点部分会有严重的形变。采用正方体影射(cubemap)可以解决这个问题,代价是较高的不连续性。计算cubemap可以保证低形变,并且计算量比球形影射也要低。
( x , y , z ) ↦ ( x z , y z ) (x,y,z) \mapsto (\frac{x}{z}, \frac{y}{z}) (x,y,z)↦(zx,zy)
当从正方体内部向外看时经常会用到cubemap,OpenGL的计算方法为:
ϕ − x ( x , y , z ) = 1 2 [ 1 + ( + z , − y ) / ∣ x ∣ ] , ϕ + x ( x , y , z ) = 1 2 [ 1 + ( − z , − y ) / ∣ x ∣ ] , ϕ − y ( x , y , z ) = 1 2 [ 1 + ( + x , − z ) / ∣ y ∣ ] , ϕ + y ( x , y , z ) = 1 2 [ 1 + ( + x , + z ) / ∣ y ∣ ] , ϕ − z ( x , y , z ) = 1 2 [ 1 + ( − x , − y ) / ∣ z ∣ ] , ϕ + z ( x , y , z ) = 1 2 [ 1 + ( + x , − y ) / ∣ z ∣ ] , \phi_{-x}(x,y,z)=\frac{1}{2}[1+(+z,-y)/|x|], \\ \phi_{+x}(x,y,z)=\frac{1}{2}[1+(-z,-y)/|x|], \\ \phi_{-y}(x,y,z)=\frac{1}{2}[1+(+x,-z)/|y|], \\ \phi_{+y}(x,y,z)=\frac{1}{2}[1+(+x,+z)/|y|], \\ \phi_{-z}(x,y,z)=\frac{1}{2}[1+(-x,-y)/|z|], \\ \phi_{+z}(x,y,z)=\frac{1}{2}[1+(+x,-y)/|z|], \\ ϕ−x(x,y,z)=21[1+(+z,−y)/∣x∣],ϕ+x(x,y,z)=21[1+(−z,−y)/∣x∣],ϕ−y(x,y,z)=21[1+(+x,−z)/∣y∣],ϕ+y(x,y,z)=21[1+(+x,+z)/∣y∣],ϕ−z(x,y,z)=21[1+(−x,−y)/∣z∣],ϕ+z(x,y,z)=21[1+(+x,−y)/∣z∣],
下标表示投影的面, ϕ − x \phi_{-x} ϕ−x表示投影到正方体x=+1的点。三个维度的绝对值最大的值决定投影的平面,示意图如下所示:
在很多情况下,接缝是不可避免的,一些纹理映射方法本身就会有接缝的存在:在球坐标或者是圆柱坐标当中会在角度从π到-π的变化时,出现接缝;cubemap在六个面连基础也会产生接缝。解决方式是例如从179到-179转化为179到181。
11.3 Antialiasing Texture Lookup
纹理反走样是纹理映射中的另一个重要问题。使用超采样的方式可以提供一个比较好的结果,但是它的计算速度很慢,所以高效的计算纹理的平均值是反走样的关键问题。
纹理图像通常通过栅格图像来定义,所以仍有一个重建的问题需要考虑,解决方案是使用一个重建过滤器在纹素之间插值。
11.3.1 The Footprint of a Pixel
使纹理反走样比其他类型的反走样更复杂的原因是渲染图像与纹理之间的对应关系是实时变化的。如果表面颜色是来自于纹理,那么它对应一部分纹理的平均值,称为纹理的texture space footprint。
要找到像素足迹需要理解两个映射的组合,首先 π \pi π的逆操作将渲染结果的坐标映射到物体场景表面上,之后使用 ϕ \phi ϕ将它转换到纹理空间中
ψ = ϕ ∘ π − 1 \psi = \phi \circ \pi^{-1} ψ=ϕ∘π−1
计算像素足迹的均值是一个复杂的工作,对于一个远处具有复杂表面的物体,纹理足迹可能覆盖了很大的区域或者几个不连续的区域,但是对于一般情况来说,一个像素覆盖的区域会映射到表面的一个纹理中的一部分平坦的区域。
ϕ \phi ϕ中包含了从渲染图像到表面和表面到纹理两个映射,所以像素足迹同时依赖于观察条件和纹理坐标函数。但Øe是为了提高算法的效率,一些近似手段也需要被采用:当函数是平滑的,通常可以使用线性近似,在纹理反走样中这意味着将从渲染图像空间到纹理空间的映射 ϕ \phi ϕ近似为从2D到2D的线性映射。
ψ ( x ) = ψ ( x 0 ) + J ( x − x 0 ) \psi(x) = \psi(x_0) + J(x-x_0) ψ(x)=ψ(x0)+J(x−x0)
如果将图像空间的坐标表示为 x = ( x , y ) x=(x,y) x=(x,y),纹理空间的坐标表示为 u = ( u , v ) u=(u,v) u=(u,v),那么矩阵J为
[ d u d x d u d y d v d x d v d y ] \begin{bmatrix} \frac{du}{dx} & \frac{du}{dy} \\ \frac{dv}{dx} & \frac{dv}{dy} \end{bmatrix} [dxdudxdvdydudydv]
导数矩阵J很有用,因为它讲述了整个图像纹理空间足迹变化的整个过程。较大的到数值意味着较大的纹理空间的足迹大小, u x u_x ux和 u y u_y uy的大小关系表明了足迹的形状。
根据上文描述,可以得到结论:在给定图像空间位置的滤波纹理采样(filtered texture sample)应该是由纹理坐标处导数形成的平行四边形面积的足迹所覆盖的纹理值求均值得到的。
11.3.2 Reconstruction
当足迹的大小小于一个纹素时,当它映射到纹理上时我们需要放大纹理,这种情况类似于对图像进行提升采样(upsampling)。
由于高质量的重建滤波使用起来的消耗是很高的,所以纹理中常用的最高质量的滤波是双线性插值(bilinear interpolation)。
Color tex_sample_bilinear(Texture t, float u, float v) {
u_p = u * t.width - 0.5
v_p = v * t.height - 0.5
iu0 = floor(u_p); iu1 = iu0 + 1
iv0 = floor(v_p); iv1 = iv0 + 1
a_u = (iu1 - u_p); b_u = 1 - a_u
a_v = (iv1 - v_p); b_v = 1 - a_v
return a_u * a_v * t[iu0][iv0] + a_u * b_v * t[iu0][iv1] +
b_u * a_v * t[iu1][iv0] + b_u * b_v * t[iu1][iv1]
}
双线性插值说明 https://blog.youkuaiyun.com/lovexlsforever/article/details/79508602
11.3.3 Mipmapping
当像素足迹包含了很多纹素时,需要对这些纹素进行求均值的操作罗,但是随着足迹区域的增加,需要读取的纹素值也会随之增加,导致计算消耗增加,一个较好的解决方案是提前计算好不同位置的各个大小区域纹素的平均值,这种思想被称为mip mapping。
最初的全分辨率的纹理图像被称为base level或者level 0,level 1通过对纹理图像进行缩减采样(downsampling),缩减的幅度是对于长宽方向进行减半,因此得到一个原图1/4大小的问题,后面的level以此类推,level k的纹理将对原图缩减采样2的k-1次,level k的每个纹素对应原图 2 k ∗ 2 k 2^k*2^k 2k∗2k个纹素,这一些列纹理图像结构称为image pyramid。
11.3.4 Basic Texture Filtering with Mipmaps
利用mipmap纹理的滤波就可以通过在不同level的纹理上进行了,找到对应level的mipmap纹理,对其进行采样就减少了运行时的计算量,但是当纹理足迹与mipmap纹素对应的形状(一般就是正方形)不同时,也会产生一些走样。如果先不考虑这种情况,那么当像素足迹在原图上的区域边长为D时,使用的level值为
k = l o g 2 D k = log_2D k=log2D
一般来说,这个值不是一个整数所以取临近的两个mimap,可以取离k最近的一张或者对两张进行线性插值。
为了计算k值,首先要确定D的值,可以使用最长边来替代
D = m a x { ∣ ∣ u x ∣ ∣ , ∣ ∣ u y ∣ ∣ } D = max\{||u_x||,||u_y||\} D=max{∣∣ux∣∣,∣∣uy∣∣}
11.3.5 Anisotropic Filtering
对于长条状的足迹最好使用短边而不是长边进行查找。