Формула белогривых лошадок: perlin noise в картинках



    Наверняка всякий, кто встречался с «перлиновым шумом» (perlin noise), пробовал генерить текстуру облаков.
    Потомучто оно само напрашивается.

    О шуме Перлина на хабре уже была статья, но в ней очень мало картинок.



    Вкратце, суть перлинового шума такова: равномерно распределённые случайные точки соединяются плавным градиентом.
    Выглядит это не особо привлекательно:

    Однако, если взять только верхнюю половину волн, умельчить в 2 раза, в 4, и в 8, и сложить всё это, получатся весьма узнаваемые облака:
    image + image + image + image = image
    Почти тоже самое, но 8 раз:
    image

    По загогулине в центре видно, что такая конструкция фрактальна, и неизбежно обладает точкой схода (fixed point). Соседние загогулины тоже на неё намекают. На практике это особо не мешает, или её можно засунуть куда-нибудь подальше.

    Вот как происходит формирование облаков в текстурном пространстве:
    image

    Возникает резонный вопрос — а зачем уровни масштабируются именно в два раза?
    Вот пример 8ми-уровневого облака с разными коофициантами масштабирования:
    image
    Видно, что до 1.6 облако недостаточно кудряво, а после 2.6 слишком пупырчато.

    Можно попробовать подставить волшебные числа:
    image
    image
    Золотое сечение чуть более размазано, число e чуть более кучнее.
    Никакого чуда не произошло. Хотя иногда с числами оно случается.
    Два.

    Итак, формула клёвого облака такова:
        float acc = 0.0;    
        float amp = 1.0;
        int i;
    
        for(i=0; i<layers; i++){
            float v = noise("perlin", scale*pnt);
            acc += v * amp;
            amp *= .5;        
            scale *= 2.0;
        }
    
        // max(acc) = (2^n - 1) / 2^n
        acc *= (float)(1<<(layers)) / (float)((1<<(layers)-1));
        return acc * 0.5 + 0.5;    
    }
    

    Здесь pnt — точка в пространстве текстуры для которой вычисляется фактор облака. Например, UV.
    Функция noise(method, coords) генерит значении perlin noise для заданной точки, в диапазоне от -1 до +1.
    Есть, наверняка, в любой графической библиотеке, а также в упомянутой выше хабрастатье.

    Вооружившись этой формулой, можно генерить эту текстуру в 3d, и натянуть её на сферу (не заморачиваясь со сферическими проекциями, а просто подставив координаты точки сферы в пространстве):
    image

    Ещё можно сгенерить четырёхмерную текстуру, и, сдвигая по времени, получить живые облака:
    image

    Для пущей убедительности, текстурную 3d-координату можно ещё повернуть вдоль экватора на 30 градусов, пропорционально квадрату косинуса широты:
    image
    Почему 30 градусов и квадрат косинуса — не знаю, но именно так выглядят реальные облака на планете.

    На самом деле, реальные облака на планете Земля, по версии NASA (http://visibleearth.nasa.gov/) выглядят так:
    image
    А полученная текстура имеет с ними мало общего.

    Во-первых, облака надо раскучерявить.
    Для раскучерявливания нужно для каждой точки сместить текстурную координату в каком-нибудь направлении, причём так, чтобы у близких точек смещение различалось незначительно. Очевиднее всего для вычисления смещения использовать снова перлинов шум.
        if(Distortion != 0.0) {
            point sp = scale[0] * pnt.p;
            float st = scale[1] * pnt.t;
            pnt.p[0] +=  Distortion * noise("perlin", sp + vector(1,0,0), st);
            pnt.p[1] +=  Distortion * noise("perlin", sp + vector(0,1,0), st);
            pnt.p[2] +=  Distortion * noise("perlin", sp + vector(0,0,1), st);
            pnt.t    +=  Distortion * noise("perlin", sp, st + 1);
        }
    

    Тут float scale[2] — масштабы в пространстве и времени, pnt — struct { point p; float t; } координаты 4d-точки.
    Если попытаться сэкономить на вызовах функции noise — будут получаться всякие непотребства, типа такого:

    А нужно, чтобы получилась непрерывно искажённая в пространстве и времени текстура:
    image

    Во-вторых, на планете облака распространены весьма рвано, а вблизи выглядят как чистокровный перлонов шум (с масштабами от 8 до 16 от размеров планеты).

    Лучшее, что я придумал за неделю — это умножать облака.
    Крупная облачность, обозначающая мега-воздушные потоки, умноженная на мелкую, барашковую — даёт в результате барашковую облачность, распространённую по мега-потокам.
    image image = image

    Это уже гораздо больше похоже на правду.
    image
    Реальные облака имеют разную степень зернистости, но заморачиваться с этим уже лень.
    Была идея сделать ещё одно очень крупное облако и его значение использовать как параметр масштаба для мелких.
    Этот фокус не прошёл — на границах масштаба барашковые облака неприлично скукоживаются.
    Кроме того, при вращении планеты облака у экватора притормаживают (это и даёт закрученность в 30 градусов), но как это сделать, чтобы текстура не перекручивалась в хлам, а плавненько испарялась — я не придумал.
    Вероятно, надо использовать 5d текстуру, и смещать закрученность в 5ом измерении, но bledner и open shading language на которых это сооужалось, 5d не поддерживают. Такая досада…

    Впрочем, на фоне настоящей планеты такие облака выглядят вполне достойно:
    image
    Хотя и рендерятся в 1.5 раза дольше.

    Анимированное сравнение nasa vs perlin залито на ютюбе.
    Все картинки, включая хайрез «непотребства» лежат в пикасоальбоме.
    Ну и blender-file со всеми конструкциями и скриптами на OSL.
    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 11
    • +6
      Какая изящная облачная технология =)
      • +8
        Если попытаться сэкономить на вызовах функции noise — будут получаться всякие непотребства, типа такого:

        А по-моему как раз круто получилось, выглядит как инопланетная трёхмерная поверхность какая-то.
        • +1
          Наслаждайтесь
          www.youtube.com/watch?v=NplqgBZOjk8
          :)
        • +1
          если синий заменить на черный, а белый — на желтый с красным отливом — получится шикарное огненное полотно, ИМХО. Особенно правая часть текстуры ну очень похожа
          • +4
            на скорую руку:


            рельефно получилось)
            • +1
              Внутренняя поверхность кровеносного сосуда…

              Первая ассоциация.
          • +1
            Ещё кое что похожее:

            (Облака над Сибирью)
        • 0
          На C++ есть, судя по отзывам, хорошая библиотека libnoise.
          Собственно, сам скачал, хотел поразбираться, а тут и статья сразу. Спасибо!
          • 0
            Если оно умеет, то стоит попробовать simplex noise.
            Это алгоритм от тогоже Перлина, но более эффективный для много-размерностей: O(n^2) против O(2^n).
            Выглядит оно, разумеется, немного не так, и какие получатся из него облака — непонятно.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое