Как вы создаете чередующийся шум Perlin?

по теме:

Я бы хотел создать чередующийся шум Perlin. Я работаю из функций Пола Боурка PerlinNoise*(), которые например:

// alpha is the "division factor" (how much to damp subsequent octaves with (usually 2))
// beta is the factor that multiplies your "jump" into the noise (usually 2)
// n is the number of "octaves" to add in
double PerlinNoise2D(double x,double y,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p[2],scale = 1;

   p[0] = x;
   p[1] = y;
   for (i=0;i<n;i++) {
      val = noise2(p);
      sum += val / scale;
      scale *= alpha;
      p[0] *= beta;
      p[1] *= beta;
   }
   return(sum);
}

Использование кода:

real val = PerlinNoise2D( x,y, 2, 2, 12 ) ; // test

return val*val*skyColor + 2*val*(1-val)*gray + (1-val)*(1-val)*cloudColor ;

Дает небо, как

nontileable

Что не является тлируемым.

Значения пикселей равны 0-> 256 (ширина и высота), а пиксель (0,0) использует (x, y) = (0,0), а пиксель (256, 256) использует (x, y) = ( 1,1)

Как я могу сделать его черепичным?

118 голосов | спросил bobobobo 10 FebruaryEurope/MoscowbFri, 10 Feb 2012 06:58:49 +0400000000amFri, 10 Feb 2012 06:58:49 +040012 2012, 06:58:49

9 ответов


72

Есть две части, чтобы сделать бесшовно плетеный шум fBm, как это. Во-первых, вам нужно сделать функцию шума Perlin самой плиткой. Вот несколько Python-кода для простой функции шума Perlin, которая работает с любым периодом до 256 (вы можете тривиально расширять ее так, как вам нравится, изменяя первый раздел):

import random
import math
from PIL import Image

perm = range(256)
random.shuffle(perm)
perm += perm
dirs = [(math.cos(a * 2.0 * math.pi / 256),
         math.sin(a * 2.0 * math.pi / 256))
         for a in range(256)]

def noise(x, y, per):
    def surflet(gridX, gridY):
        distX, distY = abs(x-gridX), abs(y-gridY)
        polyX = 1 - 6*distX**5 + 15*distX**4 - 10*distX**3
        polyY = 1 - 6*distY**5 + 15*distY**4 - 10*distY**3
        hashed = perm[perm[int(gridX)%per] + int(gridY)%per]
        grad = (x-gridX)*dirs[hashed][0] + (y-gridY)*dirs[hashed][1]
        return polyX * polyY * grad
    intX, intY = int(x), int(y)
    return (surflet(intX+0, intY+0) + surflet(intX+1, intY+0) +
            surflet(intX+0, intY+1) + surflet(intX+1, intY+1))

Перлин-шум генерируется из суммирования небольших «серпухов», которые являются произведением произвольно ориентированного градиента и разделимой функции полиномиального спада. Это дает положительную область (желтый) и отрицательную область (синий)

Ядро

Поверхности имеют протяженность 2x2 и сосредоточены на целых точках решетки, поэтому значение шума Перлина в каждой точке пространства создается путем суммирования серпухов в углах ячейки, которые он занимает.

Сумма

Если вы сделаете перенос направлений градиента с некоторым периодом, сам шум будет автоматически завершен с тем же периодом. Вот почему приведенный выше код берет координату решетки по модулю периода до того, как он хэширует ее через таблицу перестановок.

Другим шагом является то, что при суммировании октав вам нужно масштабировать период с частотой октавы. По сути, вы захотите, чтобы каждая октава разбивала всего одно изображение раз, а не несколько раз:

def fBm(x, y, per, octs):
    val = 0
    for o in range(octs):
        val += 0.5**o * noise(x*2**o, y*2**o, per*2**o)
    return val

Поместите это вместе, и вы получите что-то вроде этого:

size, freq, octs, data = 128, 1/32.0, 5, []
for y in range(size):
    for x in range(size):
        data.append(fBm(x*freq, y*freq, int(size*freq), octs))
im = Image.new("L", (size, size))
im.putdata(data, 128, 128)
im.save("noise.png")

Плиточный шум fBm

Как вы можете видеть, это действительно гладко:

fBm Noise, Tiled

С небольшим изменением и цветовым отображением, вот изображение облака, разбитое 2x2:

Облака!

Надеюсь, это поможет!

ответил Boojum 11 FebruaryEurope/MoscowbSat, 11 Feb 2012 11:57:48 +0400000000amSat, 11 Feb 2012 11:57:48 +040012 2012, 11:57:48
83

Вот один довольно умный способ, который использует 4D Perlin noise.

В основном, сопоставьте координату X вашего пикселя с двумерным кругом и координатой Y вашего пикселя со вторым двумерным кругом и поместите эти два круга, ортогональные друг другу в пространстве 4D. Полученная текстура является плетёной, не имеет явных искажений и не повторяется так, как зеркальная текстура будет.

Скопируйте код из статьи:

for x=0,bufferwidth-1,1 do
    for y=0,bufferheight-1,1 do
        local s=x/bufferwidth
        local t=y/bufferheight
        local dx=x2-x1
        local dy=y2-y1

        local nx=x1+cos(s*2*pi)*dx/(2*pi)
        local ny=y1+cos(t*2*pi)*dy/(2*pi)
        local nz=x1+sin(s*2*pi)*dx/(2*pi)
        local nw=y1+sin(t*2*pi)*dy/(2*pi)

        buffer:set(x,y,Noise4D(nx,ny,nz,nw))
    end
end
ответил John Calsbeek 10 FebruaryEurope/MoscowbFri, 10 Feb 2012 12:02:09 +0400000000pmFri, 10 Feb 2012 12:02:09 +040012 2012, 12:02:09
21

Хорошо, я понял. Ответ заключается в том, чтобы пройти в torus в 3D-шуме, создав из него 2D-текстуру.

torus wraps 2 dirs

код:

Color Sky( double x, double y, double z )
{
  // Calling PerlinNoise3( x,y,z ),
  // x, y, z _Must be_ between 0 and 1
  // for this to tile correctly
  double c=4, a=1; // torus parameters (controlling size)
  double xt = (c+a*cos(2*PI*y))*cos(2*PI*x);
  double yt = (c+a*cos(2*PI*y))*sin(2*PI*x);
  double zt = a*sin(2*PI*y);
  double val = PerlinNoise3D( xt,yt,zt, 1.5, 2, 12 ) ; // torus

  return val*val*cloudWhite + 2*val*(1-val)*gray + (1-val)*(1-val)*skyBlue ;
}

Результаты:

Когда:

tilable sky

И плиточный:

показывая его плитки

ответил bobobobo 11 FebruaryEurope/MoscowbSat, 11 Feb 2012 01:10:21 +0400000000amSat, 11 Feb 2012 01:10:21 +040012 2012, 01:10:21
16

Один простой способ, я могу думать о том, чтобы взять вывод функции шума и зеркало /перевернуть его в изображение, которое в два раза больше. Трудно объяснить, так вот изображение: введите описание изображения здесь>> </p>

<p> Теперь, в этом случае, довольно очевидно, что вы делали, когда смотрели на это. Я могу думать о двух способах (возможно :-)) решить следующее: </p>

<ol>
<li> <p> Вы можете взять это более крупное изображение, а затем создать больше шума поверх него, но (и я не уверен, что это возможно), ориентированного на середину (поэтому края остаются неизменными). Это может добавить дополнительный бит разницы, который заставит ваш мозг думать, что это не просто зеркальные образы. </p> </li>
<li> <p> (Я также не уверен, что это возможно). Вы могли бы попытаться объединиться с входами в функцию шума, чтобы генерировать исходное изображение по-разному. Вы должны сделать это с помощью проб и ошибок, но ищите функции, которые привлекают ваше внимание, когда вы пливите /зеркалируете его, а затем попробуйте и не создавайте их. </p> </li>
</ol>
<p> Надеюсь, что это поможет. </p></body></html>

ответил Richard Marskell - Drackir 10 FebruaryEurope/MoscowbFri, 10 Feb 2012 07:37:13 +0400000000amFri, 10 Feb 2012 07:37:13 +040012 2012, 07:37:13
10

Первая версия этого ответа была фактически неправильной, я обновил ее

Метод, который я использовал успешно, делает шум доменом черепичным. Другими словами, сделайте свою базовую функцию noise2() периодической. Если noise2() является периодическим, а beta - целочисленным, в результате шум будет иметь тот же период, что и noise2().

Как сделать noise2() периодическим? В большинстве реализаций эта функция использует некоторый шум решетки. То есть, он получает случайные числа в целых координатах и ​​интерполирует их. Например:

function InterpolatedNoise_1D(float x)

  integer_X    = int(x)
  fractional_X = x - integer_X

  v1 = SmoothedNoise1(integer_X)
  v2 = SmoothedNoise1(integer_X + 1)

  return Interpolate(v1 , v2 , fractional_X)

end function

Эта функция может быть тривиально модифицирована, чтобы стать периодической с целым периодом. Просто добавьте одну строку:

integer_X = integer_X % Period

перед вычислением v1 и v2. Таким образом, значения в целых координатах будут повторяться каждый период единиц, а интерполяция гарантирует, что результирующая функция будет гладкой.

Обратите внимание, однако, что это работает только тогда, когда Period больше 1. Итак, чтобы использовать это при создании бесшовных текстур, вам нужно будет выбрать квадрат Period x Period, а не 1x1.

ответил Nevermind 10 FebruaryEurope/MoscowbFri, 10 Feb 2012 10:29:36 +0400000000amFri, 10 Feb 2012 10:29:36 +040012 2012, 10:29:36
6

Другой альтернативой является создание шума с использованием библиотек libnoise. Вы можете легко создавать шум над теоретическим бесконечным пространством.

Посмотрите на следующее: http://libnoise.sourceforge.net/tutorials/tutorial3.html# плитка

Существует также порт XNA выше: http://bigblackblock.com/tools/libnoisexna

Если вы закончите использовать порт XNA, вы можете сделать что-то вроде этого:

Perlin perlin = new Perlin();
perlin.Frequency = 0.5f;                //height
perlin.Lacunarity = 2f;                 //frequency increase between octaves
perlin.OctaveCount = 5;                 //Number of passes
perlin.Persistence = 0.45f;             //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;

//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);

//Get a section
_map.GeneratePlanar(left, right, top, down);

GeneratePlanar - это функция вызова, чтобы получить разделы в каждом направлении, которые будут легко соединяться с остальными текстурами.

Конечно, этот метод является более дорогостоящим, чем просто наличие единой текстуры, которая может использоваться на нескольких поверхностях. Если вы хотите создать некоторые случайные плиткие текстуры, это может быть чем-то, что вас интересует.

ответил jgallant 10 FebruaryEurope/MoscowbFri, 10 Feb 2012 15:43:42 +0400000000pmFri, 10 Feb 2012 15:43:42 +040012 2012, 15:43:42
6

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

Все, что вам действительно нужно сделать, это использовать функцию генерации шума периодическая . Вот и все!

Отличная реализация общедоступного домена, основанная на первичном алгоритме «продвинутого» шума, может быть найдена здесь . Вам нужна функция pnoise2. Код был написан Стефаном Густавсоном, который сделал острый комментарий здесь о том, как именно эта проблема, и как другие ошибались. Слушайте Густавсона, он знает, о чем говорит.

В отношении различных сферических проекций, которые некоторые из них предложили: ну, они по существу работают (медленно), но они также создают двумерную текстуру, которая является сплющенной сферой, так что края будут более уплотненными, что, вероятно, приведет к нежелательному эффекту. Конечно, если вы намереваетесь для того, чтобы ваша 2D-текстура была проецирована на сферу, это путь, но это не то, о чем просили.

ответил Tal Liron 5 MaramTue, 05 Mar 2013 09:01:08 +04002013-03-05T09:01:08+04:0009 2013, 09:01:08
4

Вот гораздо более простой способ сделать черепичный шум: 2D Плиточный шум http://imageshack.us/a/img41/6294/nvkb.jpg

Вы используете модульную оболочку для каждой шкалы шума. Они соответствуют краям области независимо от того, какую частоту вы используете. Таким образом, вам нужно использовать обычный 2D-шум, который намного быстрее. Вот живой код WebGL, который можно найти в ShaderToy: https://www.shadertoy.com/view/4dlGW2

Три функции выполняют всю работу, а fBM передается вектор x /y в диапазоне от 0.0 до 1.0.

// Tileable noise, for creating useful textures. By David Hoskins, Sept. 2013.
// It can be extrapolated to other types of randomised texture.

#define SHOW_TILING
#define TILES 2.0

//----------------------------------------------------------------------------------------
float Hash(in vec2 p, in float scale)
{
    // This is tiling part, adjusts with the scale...
    p = mod(p, scale);
    return fract(sin(dot(p, vec2(35.6898, 24.3563))) * 353753.373453);
}

//----------------------------------------------------------------------------------------
float Noise(in vec2 x, in float scale )
{
    x *= scale;

    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    //f = (1.0-cos(f*3.1415927)) * .5;
    float res = mix(mix(Hash(p,                  scale),
        Hash(p + vec2(1.0, 0.0), scale), f.x),
        mix(Hash(p + vec2(0.0, 1.0), scale),
        Hash(p + vec2(1.0, 1.0), scale), f.x), f.y);
    return res;
}

//----------------------------------------------------------------------------------------
float fBm(in vec2 p)
{
    float f = 0.4;
    // Change starting scale to any integer value...
    float scale = 14.0;
    float amp = 0.55;
    for (int i = 0; i < 8; i++)
    {
        f += Noise(p, scale) * amp;
        amp *= -.65;
        // Scale must be multiplied by an integer value...
        scale *= 2.0;
    }
    return f;
}

//----------------------------------------------------------------------------------------
void main(void)
{
    vec2 uv = gl_FragCoord.xy / iResolution.xy;

#ifdef SHOW_TILING
    uv *= TILES;
#endif

    // Do the noise cloud (fractal Brownian motion)
    float bri = fBm(uv);

    bri = min(bri * bri, 1.0); // ...cranked up the contrast for no reason.
    vec3 col = vec3(bri);

#ifdef SHOW_TILING
    vec2 pixel = (TILES / iResolution.xy);
    // Flash borders...
    if (uv.x > pixel.x && uv.y > pixel.y                                        // Not first pixel
    && (fract(uv.x) < pixel.x || fract(uv.y) < pixel.y) // Is it on a border?
    && mod(iGlobalTime-2.0, 4.0) < 2.0)                 // Flash every 2 seconds
    {
        col = vec3(1.0, 1.0, 0.0);
    }
#endif
    gl_FragColor = vec4(col,1.0);
}
ответил Krondike 21 stEurope/Moscowp30Europe/Moscow09bEurope/MoscowSat, 21 Sep 2013 18:55:03 +0400 2013, 18:55:03
3

У меня были некоторые не плохие результаты, интерполирующие вблизи краев плитки (обернутые края), но это зависит от того, какой эффект вы пытаетесь достичь, и точных параметров шума. Отлично подходит для слегка расплывчатого шума, не очень хорошего со спикеем /мелкозернистыми.

ответил kaoD 10 FebruaryEurope/MoscowbFri, 10 Feb 2012 08:21:44 +0400000000amFri, 10 Feb 2012 08:21:44 +040012 2012, 08:21:44

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132