真实世界任何物体都是有特征的,比如:人脸上的皱纹,塑料上的凹凸不平等等。这些现象在计算机领域被称为:spatially varying surface properties
spatially varying surface properties :指那些表面属性在不同的地方变化,但并不真正以有意义的方式改变表面的形状。
To allow for these effects, all kinds of modeling and rendering systems provide some means for texture mapping: using an image, called a texture map, texture image, or just a texture, to store the details that you want to go on a surface, then mathematically “mapping” the image onto the surface.
为了实现这些效果,所有类型的建模和渲染系统都提供了一些纹理映射的方法: 使用一个图像(也就是texture image)来存储你想在表面上使用的细节,然后从数学上将图像“映射”到表面上。
这种机制出现之后,我们不仅可以使用纹理来表示表面细节,还可以来制造阴影和反射,提供照明,甚至定义表面形状。
这一章讨论了使用纹理来表示surface detail, shadows, 和 reflections。
纹理映射主要的难题有:1. 纹理很容易变形,设计将纹理映射到表面的功能是具有挑战性的。2. 纹理映射是一个重采样过程,就像缩放图像一样。重采样很容易引入混叠现象。纹理映射系统的许多复杂性都是为了解决这些伪影的混叠。
Looking Up Texture Values
参考了该处:https://zhuanlan.zhihu.com/p/369977849
设想一个场景:有一个木地板,我们希望地板的漫反射颜色由一个显示木地板纹理的图像来控制。
不管我们是使用光线追踪还是光栅化,计算the color for a ray-surface intersection point 或the color for a fragment generated by the rasterizer 的shading code 都需要知道the color of the texture at the shading point,以便将其用作Lambertian shading model中的 diffuse color。
为了获得这种颜色,着色器(shader)进行纹理查找: 它在纹理图像的坐标系统中找出对应于着色点的位置,并读取图像中该点的颜色。这种颜色随后被用于shading。代码可能如下:
纹理映射的第一个关键元素是:我们需要一个从surface映射到texture的函数。纹理映射函数 ?\phi?:将物体表面上每个点映射成纹理map上的每个点:
集合T,通常称为“纹理空间”,其只是一个包含图像的矩形,通常使用单位平方 (u,v)∈[0,1]2(u,v)∈[0,1]^2(u,v)∈[0,1]2 。其和viewing中的投影是类似的,都是3D到2D的映射,并且都是rendering(渲染)所需要的。视觉投影π几乎总是一个透视或正投影,而φ可以有多种形式;一个图像只有一个可视投影,而场景中的每个物体都可能有一个完全独立的纹理坐标函数。
对于地板映射这个场景,如果地板恰好处于恒定的z轴,并且其与x轴和y轴对齐,我们可以使用映射(这个也叫投影映射 Projector)
u=ax;v=byu = ax; v = byu=ax;v=by
类似于正交投影,抛弃一个轴,另外两个轴缩放一下。但是Projector 只适用于简单情况,对于更复杂的几何体贴图,往往需要用到 UV Mapping。
Another problem that arises from the simplest form of texture mapping is illustrated dramatically by rendering at a high contrast texture from a very grazing angle into a low-resolution image.
从这个最简单的纹理映射形式中产生的另一个问题是:从一个非常接近的角度绘制一个高对比度的纹理到一个低分辨率的图像。如图所示:
图中显示了一个更大的平面纹理,使用了相同的方法,但使用了高对比度的网格模式和朝向地平线的视图。可以看到,当没有使用适当的过滤器时,它包含混叠的伪影。
因此基本纹理映射中的两个主要问题:
? 定义纹理坐标函数
? 查找纹理值而不引入太多的混叠。
Texture Coordinate Functions
设计好纹理坐标函数φ是获得良好纹理映射效果的关键。定义这个函数需要考虑的几个问题:
-
Bijectivity 双射性 : 这样可以保证表面上的每个点都会映射到纹理空间中的不同点。如果你想让纹理在表面上重复(想想墙纸或地毯的重复图案),那么有意引入从表面点到纹理点的多对一映射是有意义的
-
Size distortion. 纹理的比例在整个表面上应该是近似恒定的。也就是说,表面上任何距离相同的近距离点应该映射到纹理中距离相同的点。对于函数φ来说,φ导数的大小不应变化太大。
-
Shape distortion 纹理不应该被扭曲。也就是说,绘制在表面上的一个小圆应该映射到纹理空间中的一个合理的圆形形状,而不是一个被压扁或拉长的形状。就φ而言,φ在不同方向上的导数不应相差太大。
-
Continuity 不应该有太多的接缝:表面上相邻的点应该映射到纹理上相邻的点。也就是说,φ应该是连续的,或有尽可能少的不连续。在大多数情况下,一些不连续是不可避免的,我们想把它们放在不显眼的地方。
广义上讲,定义纹理坐标有两种方法:
- 从surface point 的空间坐标进行几何计算。
- 对于网格表面,在顶点上存储纹理坐标的值并在整个表面上插入它们。
Geometrically Determined Coordinates
几何决定的纹理坐标用于简单的形状或特殊的情况。
Planar Projection 平面投影
平面投影如图所示:
orthographic viewing boils down to multiplying by a matrix and discarding the z component,generating texture coordinates by planar projection can be done with a simple matrix multiply:
就像正投影归结为乘上一个矩阵并丢弃z组件一样,通过平面投影生成纹理坐标可以通过简单的矩阵乘来完成:
最后一行也不一定是[0,0,0,1],也可以为这样:
其中纹理矩阵MtM_tMt?表示仿射变换,星号表示我们不关心第三个坐标的结果。
这对于平面表面来说是非常有效的,其surface normal 没有太多的变化,并且通过取平均法线可以找到一个好的投影方向。然而,对于任何类型的封闭形状,Planar Projection都不是单射的,即: 正面和背面的点将映射到纹理空间中的相同点
Projective texture coordinates在阴影映射技术中非常重要。
Spherical Coordinates
球体在两极附近有很多失真,但是它覆盖了整个球体,只有沿一条维度线不连续。
球体表面可以使用纹理坐标函数来参数化。该函数使用径向投影(radial projection)将表面上的一点映射到球体上的一点:从球体的中心取一条线,这条线通过表面上的点,并找到与球体的交点。这个交点的球坐标是你表面上的开始点的纹理坐标。
另一种说法是,用球坐标(ρ, θ, φ)表示曲面点,然后去掉ρ坐标,将θ和φ分别映射到范围[0,1]。这个公式依赖于球坐标的约定
A spherical coordinates map 除了极点处外都是双射的。它在两极附近继承了与球体上的经纬度图相同的失真
对于这个模糊的球形物体,将每个点投影到以物体中心为中心的球体上提供了一个单射映射,这里用于放置与球体图像相同的贴图纹理。注意,当表面离中心较远时,区域会被放大(表面点在纹理空间中聚集在一起),而当表面离中心较近时,区域会收缩。
Cylindrical Coordinates 圆柱坐标
对于柱状而非球面的物体,从轴向外投影到圆柱体可能比从点投影到球面更好。类似于球面投影,这相当于转换为柱坐标并丢弃半径:
Cubemaps 立方体贴图
使用球坐标参数化球形或球形形状会导致形状和极点附近区域的高度失真,这通常会导致可见的伪影。
使用Cubemaps 会在立方体的所有边缘上都引入了不连续,但它保持了形状和面积的低失真。
计算立方地图纹理坐标也比计算球坐标容易,因为投影到一个平面上只需要一个分割——本质上与viewing中的透视投影是类似的。例如,对于一个投射到立方体的+z面上的点:
(x,y,z)?>(xz,yz)(x,y,z) -> (\frac{x}{z}, \frac{y}{z})(x,y,z)?>(zx?,zy?)
Cubemaps的一个问题是:how the u and v directions are defined on the six faces. 通常的惯例是u轴和v轴的方向是顺时针的。在OpenGL中是这样的:
累了,这几个几何体的除了第一个看得懂,后面都扑街了。。。
The subscripts indicate which face of the cube each projection corresponds to. For example, φ?x is used for points that project to the face of the cube at x = +1. You can tell which face a point projects to by looking at the coordinate with the largest absolute value: for example, if |x| > |y| and |x| > |z|, the point projects to the +x or ?x face, depending on the sign of x.
A texture to be used with a cube map has six square pieces. (See Figure 11.10.) Often they are packed together in a single image for storage, arranged as if the cube was unwrapped.
Interpolated Texture Coordinates
为了对三角网格表面上的纹理坐标函数进行更细粒度的控制,我们可以显式地将纹理坐标存储在每个顶点上,并使用重心插值在三角形上对它们进行插值。一旦定位了顶点,其余的就由三角形的线性(重心)插值处理就可以了。
一种在整个网格上可视化纹理坐标的常见方法: 简单地在纹理空间中绘制三角形,将顶点放置在它们的纹理坐标上。只要网格中的三角形共享顶点,纹理坐标映射总是连续的。
当纹理空间中的三角形面积与其三维空间中的三角形面积成比例时,尺寸失真较小。
例如,如果使用连续的纹理坐标函数映射脸的时候,那么角色的鼻子就会被压缩到一个相对较小的纹理空间中。虽然鼻子上的三角形比脸颊上的三角形小,但在纹理空间上,结果是鼻子上的纹理被放大了,因为一小块纹理必须覆盖大面积的表面(纹理范围过小,导致纹理被拉大)。
解决方法:双线性插值 / 精准插值 Bibubic
类似地,纹理缩小问题:近处纹理覆盖范围小,远处纹理覆盖范围大,导致走样。解决办法: 双向插值
(好想实践一下啊)
Tiling, Wrapping Modes, and Texture Transformations
我们通常允许纹理坐标超出纹理图像的边界,因为纹理坐标计算中的舍入误差可能会导致恰好落在纹理边界上的顶点稍微偏外。在这种情况下,允许纹理坐标超出纹理图像的边界,纹理映射机制才不会失败。
如果一个纹理只覆盖surface的一部分,但是纹理坐标已经设置好是映射整个surface的。有两种方法解决该问题:1. 准备一个纹理图像,大部分是空白,只有一小部分有纹理。但这需要一个非常高分辨率的纹理图像来获得相关区域的足够细节。2. 缩放所有纹理坐标,使其覆盖更大的范围。
对于这样的情况,纹理图像所覆盖区域之外的纹理查找应该返回一个恒定的背景颜色。一种是设置一个背景色用于返回查找,一种是返回纹理图像最接近的边缘。
当纹理有重复的时候,可以使用环绕索引(wraparound indexing)。(即也就是取余的方法)
The choice between these two ways of handling out-of-bounds lookups is specified by selecting a wrapping mode from a list that includes tiling, clamping, and often combinations or variants of the two.
当调整纹理的比例和位置时,可以在使用纹理坐标采样之前对纹理坐标应用一个矩阵变换。这样就避免了改变生成纹理坐标的函数,或者存储在网格顶点上的纹理坐标值。
其中φmodelφ_{model}φmodel?是模型提供的纹理坐标函数,MTM_TMT?是一个3 × 3的矩阵,表示二维纹理坐标使用齐次坐标的仿射或投影变换
Perspective Correct Interpolation
通过在三角形上插值纹理坐标来实现正确的透视有一些难题,这些问题可以在光栅化的时候被解决
just interpolating texture coordinates in screen space results in incorrect images,如图所示:
因为随着与观众距离的增加,视角中的物体会变得更小,所以在3D中间隔均匀的线条在2D图像空间中应该会被压缩。为了实现这一点,需要更仔细地插值纹理坐标。
哎,纹理这一块一直没看懂,看不下去了。这次看到了11.2.4,记录一下,什么时候有信心再接着看吧。
看完mapping之后看第七章 viewing