Вопрос

В настоящее время я в настоящее время реализую 3D Perlin шумопопугивания, используя Shader Model 4 (DirectX 10 HLSL). Генерация самого шума не является большой проблемой (вокруг около того, есть тонны учебников и кодов), но то, что я не нашел, являются аналитическими производными 3D Perlin Shume.

Единственные сайты, принимающие во внимание производные, являются Сайт yńigo quilez и связанный Обсуждение Gamedev.net. Отказ Проблема в том, что в первой ссылке шум является значением на основе значения, а не градиент (который является требованием для меня), во второй ссылке есть только 2D-градиентное шума.

Обратите внимание, что я не ищу численные производные, поскольку те требуют 4 соседних образцов шума для генерирования, и это слишком много накладных.

Кто-нибудь рассчитал эти производные? Есть ли эта эталонная реализация, которая их использует?

Это было полезно?

Решение

Я также не мог найти решение в Интернете сегодня, поэтому я попытался вывести его.

Во-первых, определяется обозначения 3D Perlin шум.

Нотация

Предположим, что 3D Perlin шум вычисляется трилинейной интерполяцией как

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 являются интерполяционными факторами квинтичному многочлену координат фракций (т. Е. Улучшенный уровень шума):

x0 = frac(x)
y0 = frac(y)
z0 = frac(z)
x1 = x0 - 1
y1 = y0 - 1
z1 = z0 - 1

u = x0 * x0 * x0 * (x0 * (6 * x0 - 15) + 10)
v = y0 * y0 * y0 * (y0 * (6 * y0 - 15) + 10)
w = z0 * z0 * z0 * (z0 * (6 * z0 - 15) + 10)

и dot___S - точечные продукты градиентных векторов (gx___, gy___, gz___)S на баллах решетки и координаты фракций:

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)

Расширяясь n с Lerp(a, b, t) = a + (b - a) * t,

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)

,

ny = gy000
   + u   (gy100 - gy000)
   + v'  (dot010 - dot000)
   + v   (gy010 - gy000)
   + w   (gy001 - gy000)
   + uv' (dot110 - dot010 - dot100 + dot000)
   + uv  (gy110 - gy010 - gy100 + gy000)
   + uw  (gy101 - gy001 - gy100 + gy000)
   + v'w (dot011 - dot001 - dot010 + dot000)
   + vw  (gy011 - gy001 - gy010 + gy000)
   + uv'w(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
   + uvw (gy111 - gy011 - gy101 + gy001 - gy110 + gy010 + gy100 - gy000)

,

nz = gz000
   + u   (gz100 - gz000)
   + v   (gz010 - gz000)
   + w'  (dot001 - dot000)
   + w   (gz001 - gz000)
   + uv  (gz110 - gz010 - gz100 + gz000)
   + uw' (dot101 - dot001 - dot100 + dot000)
   + uw  (gz101 - gz001 - gz100 + gz000)
   + vw' (dot011 - dot001 - dot010 + dot000)
   + vw  (gz011 - gz001 - gz010 + gz000)
   + uvw'(dot111 - dot011 - dot101 + dot001 - dot110 + dot010 + dot100 - dot000)
   + uvw (gz111 - gz011 - gz101 + gz001 - gz110 + gz010 + gz100 - gz000)

Затем (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 также.

Окончательные слова

Это решение было проверено против центрального разностного метода.

Хотя это решение выглядит неуклюжему, мой эксперимент (только процессор, SSE) показал, что вычисления этих производных этого решения только вступает только о 50% дополнительное время вычислить один образец шума 3D Perlin.

Конечная разность, по крайней мере, потребуется на 300% дополнительного времени (выполняя дополнительные 3 образца) или 600% (делают 6 образцов для центральной разницы).

Следовательно, это решение лучше в производительности, а также должно быть более численным устойчивым.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top