Аналитические нормали к сфере, смещенной с помощью Simplex Noise

Я хочу сделать планету похожей на сферу. Общая идея такова:

  • Создайте группу вершин единичной длины, которые составляют сферу.
  • При рендеринге сферы шейдер оценивает 3D-симплексный шум в точке на единичной сфере.
  • Результат используется как «высота» для смещения текущей вершины вдоль ее направления.

До сих пор все работает как надо.

Теперь я хочу добавить освещение и поэтому мне нужны нормали к поверхности.

При реализации частей, связанных с освещением, я быстро добавил метод для оценки нормалей ландшафта с использованием частных производных во фрагментном шейдере, например:

vec3 X = dFdx(ins.position);
vec3 Y = dFdy(ins.position);
vec3 normal = normalize(cross(X,Y));

где ins.position — интерполированная мировая позиция.

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

Плохое нормальное приближение

Теперь собственно вопросы:

  • Вычисление нормалей для каждой вершины приведет к гладким нормалям, в отличие от рисунка, верно?
  • Одним из преимуществ Simplex Noise по сравнению с Perlin Noise является то, что он имеет «хорошо определенный и непрерывный градиент везде, который можно вычислить довольно дешево» (цитируя превосходный Симплексный шум демистифицирован), а с помощью градиента можно вычислить нормаль, правильно?

Если второй вопрос - "да", у меня две проблемы:

  • Алгоритм симплексного шума был взят из популярного источника, который, к сожалению, не включает вычисление градиента. Я опубликую свою попытку добавить его ниже, но я понятия не имею, правильно ли это.
  • Даже если бы у меня был градиент, я застрял на выводе нормали оттуда.

Любая помощь приветствуется!

Моя попытка реализации градиента (заменены последние несколько строк snoise ):

float snoise(vec3 v, out vec3 grad)
{
    ......

    // Mix final noise value
    vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
    vec4 m2 = m * m;
    vec4 m4 = m2 * m2;

    vec4 pdotx = vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3));

    vec4 temp = m2 * m * pdotx;
    grad = -8.0 * (temp.x * x0 + temp.y * x1 + temp.z * x2 + temp.w * x3);
    grad += m4.x * p0 + m4.y * p1 + m4.z * p2 + m4.w * p3;
    grad *= 42.0;

    return 42.0 * dot(m4, pdotx);
}

ОБНОВЛЕНИЕ:

Ответ на часть о вычислении нормали к поверхности из градиента был дан здесь: Surface нормаль к точке на смещенной сфере.

Остался вопрос, как внедрить вычисление градиента в GLSL-версию 3D Simplex Noise, потому что в моей реализации есть проблемы.

ОБНОВЛЕНИЕ 2:

Вычисление градиента кажется почти правильным, только масштабирование кажется неправильным.
Вместо умножения на 42 довольно хорошие результаты дает деление на 5, но это было выяснено методом проб и ошибок. . Правильный коэффициент масштабирования был бы хорош.


person Gigo    schedule 16.12.2014    source источник
comment
Отвечая на ваш первый вопрос: да и нет. Нормали вершин могут быть гладкими, если у вас есть информация о соседних треугольниках, или они могут быть плоскими, если вы вычисляете их, используя один треугольник. Используя геометрические шейдеры и полосу треугольников со смежностью, вы можете получить до 4 общих треугольников на вершину, что может (учитывая, что на самом деле не количество треугольников, которым принадлежит каждая вершина) достаточно для вычисления приблизительной гладкой нормали к каждой вершине. Для этого вам придется структурировать входные индексы вершин в особом порядке, и для этого требуется GL 3.2+.   -  person Andon M. Coleman    schedule 17.12.2014
comment
Если я правильно понял, результирующие нормали будут гладкими, если нормаль для данной вершины одинакова, независимо от того, какая грань обрабатывается в данный момент. Предполагая, что расчет работает так, как я описал, так оно и будет, потому что нормали будут зависеть только от информации об одной вершине, которой они принадлежат.   -  person Gigo    schedule 17.12.2014
comment
Что ж, (гладкая) нормаль для каждой вершины зависит от двух других вершин в каждом треугольнике, который разделяет эту вершину. Я смотрю на вашу сетку и замечаю некоторые вершины с 10 треугольниками, имеющими одну и ту же вершину. Если вы хотите правильно сгладить эти нормали для каждой вершины, вам придется вычислить нормали граней для всех 10 этих треугольников. Вот почему это не кажется практичным.   -  person Andon M. Coleman    schedule 17.12.2014
comment
Вы можете избежать этой проблемы, настроив сферическую нормаль с помощью вычисляемых графическим процессором градиентов во фрагментном шейдере с помощью dFdx и dYdx.   -  person Justin    schedule 17.12.2014
comment
@ Джастин, ты имеешь в виду, как я описал в вопросе? ;)   -  person Gigo    schedule 17.12.2014
comment
Вы можете вычислить нормаль в вершинном шейдере, оценив симплексный шум еще в двух позициях с небольшими смещениями от вершины.   -  person GuyRT    schedule 17.12.2014
comment
@GuyRT Да, я мог бы, но оценка шума стоит дорого по сравнению с градиентом, и последний даст идеальные нормали, а не приближение.   -  person Gigo    schedule 17.12.2014
comment
Иниго Килез обсуждает получение производных от шума перлина здесь: iquilezles.org/www/articles/morenoise /morenoise.htm — возможно, вы могли бы адаптировать его идеи   -  person GuyRT    schedule 17.12.2014
comment
@Gigo упс. Я не думаю, что вы хотите оценивать функцию шума для каждого фрагмента.   -  person Justin    schedule 17.12.2014


Ответы (1)


Хорошо, оказывается, моя проблема была почти чисто математической.

Если кому-то интересно:
GLSL-реализация вычисления градиента, опубликованная в вопросе, полностью верна.
Вычисление нормалей непосредственно из градиента возможно, как описано здесь.

И результат выглядит именно так, как я хотел, я счастлив;)

Гладкие нормали для каждой вершины

person Gigo    schedule 18.12.2014
comment
Можете ли вы поделиться кодом? я не могу заставить это работать, как вы описали. - person Nils J.; 04.04.2020
comment
Я постараюсь откопать этот проект и выложить на Github. - person Gigo; 07.04.2020
comment
Было бы неплохо :D - person Nils J.; 08.04.2020