八面体(Octahedron)及其坐标映射
Table of Contents
八面体(Octahedron)及其坐标映射
八面体是一种 正八面体几何结构,它可以用于 映射方向向量(3D)到2D,以便进行 高效的纹理存储和查询。在 Impostor 渲染 中,八面体映射(Octahedral Mapping)是一种将 球面方向向量 投影到 二维平面 的方法,广泛用于 环境贴图、光照计算、Impostor 纹理存储 等。
1. 为什么使用八面体坐标?
通常,我们用一个 单位向量(( x, y, z ))表示方向。但如果要将方向存储在 2D 纹理(如 Impostor 贴图)中,我们需要找到一种 从 3D 到 2D 的映射方法。
常见的映射方法:
- 球面坐标(Spherical Coordinates):容易产生极点扭曲(pole distortion)。
- 立方体映射(Cubemap):存储效率不高,容易出现采样接缝(seams)。
- 八面体映射(Octahedral Mapping):
- 相比 球面坐标,它没有极点扭曲问题。
- 相比 立方体映射,它存储利用率高达 100%,没有浪费区域。
- 计算简单,适用于 GPU 计算。
因此,八面体映射是一种高效的 3D → 2D 映射 方法。
2. 八面体坐标转换
八面体映射的核心思想是 将单位球的上、下两个半球折叠到一个正方形上。
(1) 3D 方向向量到八面体坐标(VectortoOctahedron)
假设有一个 单位方向向量: [ V = (x, y, z) ] 步骤:
-
投影到 XY 平面 [ P = \frac{(x, z)}{|x| + |y| + |z|} ]
- 这里分母是向量 绝对值求和,用于归一化,确保八面体映射不会超出范围。
- 结果 ( P ) 是 [-1,1] 范围 内的二维坐标。
-
折叠负 Y 轴部分
if (y < 0) { P = (1 - abs(P.yx)) * sign(P.xy); }
- 如果 y < 0(即方向朝向球的下半部分),我们将它折叠回 上半球,确保所有数据都在 一个正方形区域内。
- 这样,我们得到了最终的 八面体 UV 坐标。
最终的八面体坐标范围是:
( (x’, z’) ) ∈ [-1,1]²,表示一个 正方形 内的 2D 坐标。
(2) 八面体坐标到 3D 方向向量(OctahedronToVector)
要将八面体坐标还原回 3D 方向向量:
-
先计算: [ y = 1 - |x’| - |z’| ] 这样,我们得到了向量的 Y 分量(因为 X 和 Z 分量已经在 UV 坐标中)。
-
如果 y < 0,意味着这个点原本在折叠的区域,需要展开:
if (y < 0) { x' = (1 - abs(z')) * sign(x'); z' = (1 - abs(x')) * sign(z'); }
这样,我们可以还原出原始的 3D 方向向量。
3. 半球八面体映射(Hemi-Octahedral Mapping)
通常情况下,八面体映射可以表示整个单位球(full sphere),即它可以存储所有方向 ( (x, y, z) ),其中 ( y ) 可以是 正的(上半球)或负的(下半球)。
然而,在某些情况下,我们只关心上半球方向(例如环境光遮蔽、Impostor 只存储朝上的视角等)。此时,我们可以使用 半球八面体映射(Hemi-Octahedral Mapping)。
(1) 半球八面体的转换
如果我们知道方向总是位于 上半球(即 ( y > 0 )),可以对 八面体坐标 进行如下优化:
-
计算: [ P = \frac{(x, z)}{|x| + y + |z|} ]
- 这里的分母变成 ( |x| + y + |z| ),保证数据只映射到上半球。
-
无需折叠 y < 0 的情况,因为所有数据都位于上半球。
(2) 优势
- 更高的精度:因为它的分布不会受到负半球的影响。
- 更紧凑的存储:可以将 完整的 2D 八面体映射空间 用于表示 上半球方向。
(3) 半球八面体的逆变换
如果我们存储了半球八面体坐标,我们可以直接恢复 3D 方向:
- 计算: [ y = 1 - |x’| - |z’| ] 这里不需要处理 ( y < 0 ) 的情况,因为所有数据本来就来自上半球。
4. 八面体 vs. 半球八面体
特性 | 八面体映射(Octahedral Mapping) | 半球八面体映射(Hemi-Octahedral Mapping) |
---|---|---|
存储范围 | 上下半球(全方向) | 仅上半球(( y > 0 )) |
映射公式 | ( (x, z) / ( | x |
存储区域 | 正方形([-1,1]²) | 正方形([-1,1]²),但数据密度更高 |
是否需要折叠 | 需要折叠 ( y < 0 ) 的区域 | 不需要折叠 |
适用场景 | 全方向 Impostor、Cubemap 纹理存储 | 仅用于半球方向(如光照、环境遮挡) |
5. 代码示例
(1) VectortoOctahedron(八面体映射)
float2 VectortoOctahedron(float3 v) {
v /= abs(v.x) + abs(v.y) + abs(v.z);
if (v.y < 0) v.xy = (1 - abs(v.yx)) * sign(v.xy);
return v.xz;
}
(2) OctahedronToVector(八面体反变换)
float3 OctahedronToVector(float2 uv) {
float3 v = float3(uv.x, 1 - abs(uv.x) - abs(uv.y), uv.y);
if (v.y < 0) v.xz = (1 - abs(v.zx)) * sign(v.xz);
return normalize(v);
}
(3) VectortoHemiOctahedron(半球八面体映射)
float2 VectortoHemiOctahedron(float3 v) {
v /= abs(v.x) + v.y + abs(v.z);
return v.xz;
}
6. 总结
- 八面体映射 是一种高效的 3D → 2D 方向存储方法,适用于 全方向数据(如全景环境贴图、Impostor)。
- 半球八面体映射 仅存储 上半球数据,适用于光照计算、半球环境贴图等,精度更高,存储更紧凑。