Попытка Шума Перлина
Я пытаюсь сделать свою попытку с Перлиновым шумом (3-мерным), как описано в этом документе: http://lodev.org/cgtutor/randomnoise.html
Тем не менее, это то, что я получаю. Похоже, что сглаживание не работает. Вы можете видеть блоки размером с параметр 'size'. Может кто-нибудь указать, что я делаю не так?Вот мой код:
%ffp
ctl(1):standard,"Size",range=(1,256), pos=(300,20), size=(120,*),val=64,track, action=preview
onFilterStart:
{
allocArray(9,64,64,64,4); // Array for noise depth
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++) {
fputArray(9,x,y,z,(float)(rand() % 32768) / 32768.0);
}
return false;
}
forEveryTile:
{
double fractX,fractY,fractZ,xx,yy,zz;
int x1,y1,z1,x2,y2,z2,col;
double value = 0.0, value2 = 0.0, size, isize=(float)ctl(1);
// int X=screen Width, int Y=screen Height
for(int y = 0; y < Y; y++) {
for(int x = 0; x < X; x++) {
//for(int z = 0; z < 64; z++) {
value2 = 0.0;
size = isize;
while (size >=1.0) {
xx=(float)x/size;
yy=(float)y/size;
zz=(float)clock()/size;
fractX = xx - (int)(xx);
fractY = yy - (int)(yy);
fractZ = zz - (int)(zz);
x1 = ((int)(xx) + 64) % 64;
y1 = ((int)(yy) + 64) % 64;
z1 = ((int)(zz) + 64) % 64;
x2 = (x1 + 64- 1) % 64;
y2 = (y1 + 64- 1) % 64;
z2 = (z1 + 64- 1) % 64;
value=0.0;
value += fractX * fractY * fractZ * fgetArray(9,z1,y1,x1);
value += fractX * (1 - fractY) * fractZ * fgetArray(9,z1,y2,x1);
value += (1 - fractX) * fractY * fractZ * fgetArray(9,z1,y1,x2);
value += (1 - fractX) * (1 - fractY) * fractZ * fgetArray(9,z1,y2,x2);
value += fractX * fractY * (1 - fractZ) * fgetArray(9,z2,y1,x1);
value += fractX * (1 - fractY) * (1 - fractZ) * fgetArray(9,z2,y2,x1);
value += (1 - fractX) * fractY * (1 - fractZ) * fgetArray(9,z2,y1,x2);
value += (1 - fractX) * (1 - fractY) * (1 - fractZ) * fgetArray(9,z2,y2,x2);
value2 += value*size;
size /= 2.0;
}
col=(int)((float)(128.0 * value2 / isize));
col=max(min(col,255),0);
psetp(x,y,RGB(col,col,col));
//} //z
} //x
} //y
return true;
}
1 ответ:
Ваш код довольно трудно читать в написанном виде.
Для шума Perlin начните с целочисленной функции шума, которая ведет себя как хэш.
float noise(int x, int y, int z) { return hash(x+y*5+z*7); }
Или
Это всего лишь примеры. Важно то, что шум (x, y, z) = шум(x,y,z). Функция шума должна возвращать одно и то же значение для одних и тех же параметров каждый раз.float noise(int x, int y, int z) { return array[x%w+y%h*w+z%d*w*h]; }
Однако есть проблема: функция шума принимает только целочисленные параметры! Но мы хотели бы попробовать его в float ценности.
float noisesample (float x, float y, float z) { ... }
Самый простой способ сделать это-использовать линейную фильтрацию. Любое положительное значение float находится между (int)pos и ((int)pos)+1. В субпозиции pos - (int)pos. Это дает нам:
float Lerp(float a, float b, float f) { return a+(b-a)*f; }
Где f-подпозиция в [0..1] диапазон и a, b-значения слева и справа. Если f равно 0, то Lerp возвращает a, если 1, то b. между ними выполняется линейная интерполяция.
Поэтому используйте это для простой функции 1D noisesample:
float noisesample(float x) { return Lerp(noise((int)x), noise((int)x+1), fract(x) }
С
float fract(float x) { return x-(int)x; }
I я использую(int)x свободно здесь, это то же самое, что пол (x), если x положителен.
Перейти от одного параметра noisesample к x, y легко: выполните Lerp дважды для x при y и y+1, а Lerp между ними:
float noisesample(float x, float y) { float y0 = Lerp(noise((int)x,(int)y), noise((int)x+1,(int)y), fract(x) } float y1 = Lerp(noise((int)x,(int)y+1), noise((int)x+1,(int)y+1), fract(x) } return Lerp ( y0, y1, fract(y) ); }
Сначала интерполируем x, дважды, затем интерполируем между результатами в y. всего мы выберем шум() 4 раза. Я оставляю это как упражнение, как написать noisesample (float x, float y, float z). Он будет пробовать noise() восемь раз и вызывать Lerp 7 раз.
Все, что получил нас заключается в том, что мы можем сэмплировать шум (несколько плавный - есть более плавные способы!) в точке с плавающими координатами. И это то, что нам нужно, чтобы заставить Перлина шуметь!
Ключевая идея состоит в том, чтобы понять выборку. Это просто комбинация выборки простой целочисленной функции шума.float perlin(float x, float y, float z, int oc=4) { // maybe: x = x*2^oc, y, z... float r = 0; float s = 1; for ( int i=0; i<oc; i++ ) { r += noisesample(x,y,z) * s; s/=2.0f; // to taste x/=2.0f; y/=2.0f; z/=2.0f; } return r; }