3d 三维柏林噪声解析导数

3d 三维柏林噪声解析导数,3d,perlin-noise,derivative,3d,Perlin Noise,Derivative,我目前正在使用着色器模型4(DirectX 10 HLSL)实现3D柏林噪波凹凸贴图。产生噪声本身并不是一个大问题(有大量的教程和代码),但我没有发现3D柏林噪声的解析导数 唯一考虑衍生品的营业点是和相关的。问题是,在第一个链接中,噪声是基于值的,而不是基于梯度的(这是我的要求),在第二个链接中,只有2D梯度噪声导数 请注意,我不是在寻找数值导数,因为这些导数需要生成4个相邻的噪声样本,这会带来太多的开销 有人计算过这些衍生品吗?有没有使用它们的参考实现?我今天在web上也找不到解决方案,所以我

我目前正在使用着色器模型4(DirectX 10 HLSL)实现3D柏林噪波凹凸贴图。产生噪声本身并不是一个大问题(有大量的教程和代码),但我没有发现3D柏林噪声的解析导数

唯一考虑衍生品的营业点是和相关的。问题是,在第一个链接中,噪声是基于值的,而不是基于梯度的(这是我的要求),在第二个链接中,只有2D梯度噪声导数

请注意,我不是在寻找数值导数,因为这些导数需要生成4个相邻的噪声样本,这会带来太多的开销


有人计算过这些衍生品吗?有没有使用它们的参考实现?

我今天在web上也找不到解决方案,所以我尝试推导它

首先定义了三维柏林噪声的符号

符号 假设3D柏林噪声通过三线性插值计算为

n = Lerp(
        Lerp(
            Lerp(dot000, dot100, u),
            Lerp(dot010, dot110, u),
            v), 
        Lerp(
            Lerp(dot001, dot101, u), 
            Lerp(dot011, dot111, u),
            v),
        w)
其中,
u
v
w
是分数坐标五次多项式的插值因子(即改进的柏林噪声):

dot\uuuuuuu
s是梯度向量
(gx\uuuuuuuuu,gy\uuuuuuuuuu,gz\uuuuuuuu)在晶格点和分数坐标处的点积:

dot000 = gx000 * x0 + gy000 * y0 + gz000 * z0
dot100 = gx100 * x1 + gy100 * y0 + gz100 * z0
dot010 = gx010 * x0 + gy010 * y1 + gz010 * z0
dot110 = gx110 * x1 + gy110 * y1 + gz110 * z0
dot001 = gx001 * x0 + gy001 * y0 + gz001 * z1
dot101 = gx101 * x1 + gy101 * y0 + gz101 * z1
dot011 = gx011 * x0 + gy011 * y1 + gz011 * z1
dot111 = gx111 * x1 + gy111 * y1 + gz111 * z1
计算导数 首先,计算
u
v
w

u' = 30 * x0 * x0 * (x0 - 1) * (x0 - 1)
v' = 30 * y0 * y0 * (y0 - 1) * (y0 - 1)
w' = 30 * z0 * z0 * (z0 - 1) * (z0 - 1)
通过使用
Lerp(a,b,t)=a+(b-a)*t展开
n

n = dot000 
  + u(dot100 - dot000)
  + v(dot010 - dot000)
  + w(dot001 - dot000)
  + uv(dot110 - dot010 - dot100 + dot000)
  + uw(dot101 - dot001 - dot100 + dot000)
  + vw(dot011 - dot001 - dot010 + dot000)
  + uvw(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
然后取
n
的偏导数

nx = gx000
   + u'  (dot100 - dot000)
   + u   (gx100 - gx000)
   + v   (gx010 - gx000)
   + w   (gx001 - gx000)
   + u'v (dot110 - dot010 - dot100 + dot000)
   + uv  (gx110 - gx010 - gx100 + gx000)
   + u'w (dot101 - dot001 - dot100 + dot000)
   + uw  (gx101 - gx001 - gx100 - gx000)
   + vw  (gx011 - gx001 - gx010 + gx000)
   + u'vw(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
   + uvw (gx111 - gx011 - gx101 + gx001 - gx110 + gx010 + gx100 - gx000)
,

,

然后
(nx,ny,nz)
是噪声函数的梯度向量(偏导数)

优化 如果编译器无法处理某些常见的子表达式,则可以将其分解。例如:

uv = u * v
vw = v * w
uw = u * w
uvw = uv * w
扩展的
n
中的系数重复使用多次。它们可以通过以下方式计算:

k0 = dot100 - dot000
k1 = dot010 - dot000
k2 = dot001 - dot000
k3 = dot110 - dot010 - k0
k4 = dot101 - dot001 - k0
k5 = dot011 - dot001 - k1
k6 = (dot111 - dot011) - (dot101 - dot001) - k3
导数也有相似的系数

gxk0 = gx100 - gx000
gxk1 = gx010 - gx000
...
n
的计算可以使用扩展形式
k0
<代码>k6

最后的话 用中心差分法对该解进行了验证

虽然这个解决方案看起来很笨拙,但我的实验(仅CPU,SSE)表明,用这个解决方案计算这些导数只需要计算单个3D Perlin噪声样本约50%的额外时间

有限差分法至少需要300%的额外时间(多做3个样本)或600%(为中心差分法多做6个样本)


因此,此解决方案的性能更好,并且在数值上也应该更稳定。

dot111在您的列表中存在两次。它的一个实例应该是dot101Thank@plasmacel。修正了。你们有Wolfram Mathematica的推导笔记本吗?你们也有SimplexNoise的分析导数吗?Inigo Quilez有一个解决方案:
uv = u * v
vw = v * w
uw = u * w
uvw = uv * w
k0 = dot100 - dot000
k1 = dot010 - dot000
k2 = dot001 - dot000
k3 = dot110 - dot010 - k0
k4 = dot101 - dot001 - k0
k5 = dot011 - dot001 - k1
k6 = (dot111 - dot011) - (dot101 - dot001) - k3
gxk0 = gx100 - gx000
gxk1 = gx010 - gx000
...