使用 ShaderToy 时,我经常看到人们使用类似的东西:
vec2 uv = fragCoord / iResolution;
vec2 centerPoint = vec2(0.5);
vec2 distanceVector = uv - centerPoint;
float dist = sqrt(dot(distanceVector, distanceVector));
OpenGL 的
distance
功能:
vec2 uv = fragCoord / iResolution;
vec2 centerPoint = vec2(0.5);
float dist = distance(uv, centerPoint);
我只是好奇为什么会这样(我的猜测是它与速度或对
distance
的支持有关)。
我粗略地理解,如果参数相同,点积的平方根等于向量的长度:距离?
本质上做同样的事情,人们通常出于以下两个原因之一选择 sqrt 选项: 1.他们不知道/记得距离函数 2. 他们相信自己和自己的数学来证明这不是导致错误的问题(避免 OpenGL 问题)
有时为了优化早期退出,例如轻量:
float distSquared( vec3 A, vec3 B )
{
vec3 C = A - B;
return dot( C, C );
}
// Early escape when the distance between the fragment and the light
// is smaller than the light volume/sphere threshold.
//float dist = length(lights[i].Position - FragPos);
//if(dist < lights[i].Radius)
// Let's optimize by skipping the expensive square root calculation
// for when it's necessary.
float dist = distSquared( lights[i].Position, FragPos );
if( dist < lights[i].Radius * lights[i].Radius )
{
// Do expensive calculations.
如果您稍后需要
distance
,只需:
dist = sqrt( dist )
编辑:另一个例子。
我最近学到的另一个用例,假设您想要有两个位置:
vec3 posOne
和vec3 posTwo
,并且您想要到每个位置的距离。最简单的方法是独立计算它们:float distanceOne = distance( posOne, otherPos )
和float distanceTwo = distance( posTwo, otherPos )
。但您想利用 SIMD!所以你这样做: posOne -= otherPos; posTwo -= otherPos
这样你就可以通过 SIMD 计算欧氏距离: vec2 SIMDDistance = vec2( dot( posOne ), dot( posTwo ) );
然后你可以使用 SIMD 求平方根: SIMDDistance = sqrt( SIMDDistance );
其中到 posOne 的距离位于 SIMDDistance 的 .x 分量上变量和 .y 分量包含到 posTwo 的距离。
distance()
和length()
在内部使用sqrt()
。使用 sqrt()
以及依赖它的所有功能可能会很昂贵。如果可能的话就使用dot()
!
我猜
sqrt()
是数学计算,但是dot()
是GPU擅长的向量计算。
使用 dot 为您提供了一种快速实验距离的二次/线性函数的方法。
我经常做的是以下(示例):
vec3 vToLight = light.pos - cam.pos;
float lengthSqToLight = dot(vToLight, vToLight);
if (lengthSqToLight > 0 && lengthSqToLight <= maxLengthSq) {
float lengthToLight = sqrt(lengthSqToLight); // length is often needed later
...
vec3 vToLightNormalized = vToLight / lengthToLight; // avoid normalize() => avoids second sqrt
...
// later use lengthToLight
}