打包法向量和切向量

时间:2017-10-14 14:46:06

标签: opengl glsl shader

在我正在处理的延迟着色引擎中,我目前将法向量存储在内部格式为GL_RGBA16F的缓冲区中。 我一直都知道这不是最好的解决方案,但我没时间处理它。

最近我读了"Survey of Efficient Representations for Independent Unit Vectors",这启发我使用八面体法向量(ONV)并将缓冲区更改为GL_RG16_SNORM

对法线向量进行编码(vec3vec2

// Returns +/- 1
vec2 signNotZero( vec2 v )
{
    return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
}

// Assume normalized input. Output is on [-1, 1] for each component.
vec2 float32x3_to_oct( in vec3 v )
{
    // Project the sphere onto the octahedron, and then onto the xy plane
    vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
    // Reflect the folds of the lower hemisphere over the diagonals
    return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p;
} 

解码法线向量(vec2vec3

vec3 oct_to_float32x3( vec2 e )
{
    vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
    if (v.z < 0) v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
    return normalize(v);
}

由于我现在已经实现了各向异性光模型,因此必须存储切向量以及法向量。我想将两个向量存储在帧缓冲区的一个相同的颜色附件中。这让我想到了我的问题。将单位法向量和切向量打包到缓冲区中有什么有效的折衷方案? 当然,使用纸上的算法将RG通道中的法线向量和BA缓冲区的GL_RGBA16_SNORM通道中的切线向量存储起来很容易,这是我的目前的实施也是如此 但由于法向量和切线向量总是正交,因此必须有更优雅的方式,这可以提高准确性或节省记忆。

所以真正的问题是:我如何利用我知道2个向量是正交的这一事实?我可以将两个向量存储在GL_RGB16_SNORM缓冲区中,如果不存储,我可以在将它们打包到GL_RGBA16_SNORM缓冲区时提高可靠性。

1 个答案:

答案 0 :(得分:1)

以下考虑纯粹是数学上的,我没有实践经验。但是,我认为特别是选项2可能是一个可行的候选人。

以下两个选项的共同点是它们如何说明问题:给定法线(可以使用ONV重建),如何使用单个数字对切线进行编码。

选项1

第一个选项非常接近meowgoesthedog建议的内容。定义任意参考向量(例如(0, 0, 1))。然后将切线编码为角度(标准化为[-1, 1]范围),您需要围绕法线旋转此向量以匹配切线方向(当然,在切线平面上投影之后)。您将需要两个不同的参考矢量(甚至三个),并根据法线选择正确的参考矢量。您不希望参考向量与法线平行。我认为这在计算上比第二种选择更昂贵,但需要测量。但是你会得到一个统一的错误分布作为回报。

选项2

让我们考虑与切线正交的平面。该平面可以由切线或位于平面中的两个矢量定义。我们知道一个向量:表面法线。如果我们知道第二个向量v,我们可以将切线计算为t = normalize(cross(normal, v))。为了对这个向量进行编码,我们可以规定两个组件并解决剩下的一个组件。例如。让我们的向量为(1, 1, x)。然后,为了对矢量进行编码,我们需要找到x,这样cross((1, 1, x), normal)与切线平行。这可以通过一些简单的算法来完成。同样,您需要一些不同的矢量模板来考虑所有场景。最后,你有一个方案,其编码器更复杂,但其解码器不会更简单。错误分布不会像选项1那样统一,但对于合理选择的矢量模板应该没问题。