在我正在处理的延迟着色引擎中,我目前将法向量存储在内部格式为GL_RGBA16F
的缓冲区中。
我一直都知道这不是最好的解决方案,但我没时间处理它。
最近我读了"Survey of Efficient Representations for Independent Unit Vectors",这启发我使用八面体法向量(ONV)并将缓冲区更改为GL_RG16_SNORM
:
对法线向量进行编码(vec3
至vec2
):
// 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;
}
解码法线向量(vec2
到vec3
):
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
缓冲区时提高可靠性。
答案 0 :(得分:1)
以下考虑纯粹是数学上的,我没有实践经验。但是,我认为特别是选项2可能是一个可行的候选人。
以下两个选项的共同点是它们如何说明问题:给定法线(可以使用ONV重建),如何使用单个数字对切线进行编码。
第一个选项非常接近meowgoesthedog建议的内容。定义任意参考向量(例如(0, 0, 1)
)。然后将切线编码为角度(标准化为[-1, 1]
范围),您需要围绕法线旋转此向量以匹配切线方向(当然,在切线平面上投影之后)。您将需要两个不同的参考矢量(甚至三个),并根据法线选择正确的参考矢量。您不希望参考向量与法线平行。我认为这在计算上比第二种选择更昂贵,但需要测量。但是你会得到一个统一的错误分布作为回报。
让我们考虑与切线正交的平面。该平面可以由切线或位于平面中的两个矢量定义。我们知道一个向量:表面法线。如果我们知道第二个向量v
,我们可以将切线计算为t = normalize(cross(normal, v))
。为了对这个向量进行编码,我们可以规定两个组件并解决剩下的一个组件。例如。让我们的向量为(1, 1, x)
。然后,为了对矢量进行编码,我们需要找到x
,这样cross((1, 1, x), normal)
与切线平行。这可以通过一些简单的算法来完成。同样,您需要一些不同的矢量模板来考虑所有场景。最后,你有一个方案,其编码器更复杂,但其解码器不会更简单。错误分布不会像选项1那样统一,但对于合理选择的矢量模板应该没问题。