八面体(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) ] 步骤:

  1. 投影到 XY 平面 [ P = \frac{(x, z)}{|x| + |y| + |z|} ]

    • 这里分母是向量 绝对值求和,用于归一化,确保八面体映射不会超出范围。
    • 结果 ( P ) 是 [-1,1] 范围 内的二维坐标。
  2. 折叠负 Y 轴部分

    if (y < 0) {
        P = (1 - abs(P.yx)) * sign(P.xy);
    }
    
    • 如果 y < 0(即方向朝向球的下半部分),我们将它折叠回 上半球,确保所有数据都在 一个正方形区域内
    • 这样,我们得到了最终的 八面体 UV 坐标

最终的八面体坐标范围是:
( (x’, z’) ) ∈ [-1,1]²,表示一个 正方形 内的 2D 坐标。


(2) 八面体坐标到 3D 方向向量(OctahedronToVector)

要将八面体坐标还原回 3D 方向向量

  1. 先计算: [ y = 1 - |x’| - |z’| ] 这样,我们得到了向量的 Y 分量(因为 X 和 Z 分量已经在 UV 坐标中)。

  2. 如果 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 )),可以对 八面体坐标 进行如下优化:

  1. 计算: [ P = \frac{(x, z)}{|x| + y + |z|} ]

    • 这里的分母变成 ( |x| + y + |z| ),保证数据只映射到上半球
  2. 无需折叠 y < 0 的情况,因为所有数据都位于上半球。

(2) 优势

  • 更高的精度:因为它的分布不会受到负半球的影响。
  • 更紧凑的存储:可以将 完整的 2D 八面体映射空间 用于表示 上半球方向

(3) 半球八面体的逆变换

如果我们存储了半球八面体坐标,我们可以直接恢复 3D 方向:

  1. 计算: [ 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)。
  • 半球八面体映射 仅存储 上半球数据,适用于光照计算、半球环境贴图等,精度更高,存储更紧凑。
kumakoko avatar
kumakoko
pure coder
comments powered by Disqus