Помогите с алгоритмом генератора 4D шума

Программирование на Юнити.

Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 21 янв 2019, 11:00

Прошу помочь найти ошибку. Делаю алгоритм генератора 4D шума, основу взял с разных статей из интернета. При проверке столкнулся с проблемой, если взять двухмерный срез по координатам X, Y с фиксированными значениями Z и W, например так noise.GetNoise(nx, ny, 15, 90), то получается нормальная картина 2D шума. Но если взять срез по другим осям, например noise.GetNoise(15, ny, nz, 90); получается ерунда. Если кто пробовал реализовать подобное прошу подскажите где ошибка или может быть я вообще не так делаю.

код проверки

Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
        public int seed = 0;
        public float scale = 10;
       
        int w = 500;
        int h = 500;
        Texture2D text;
        Color[] pixels;
        Noise noise;
       
        void Start()
        {
                text = new Texture2D(w, h);
                noise = new Noise(seed);
               
                pixels = new Color[w * h];
                for (int x = 0; x < w; x++)
                        for (int y = 0; y < h; y++) {
                                float nx = (float)x / w * scale;
                                float ny = (float)y / h * scale;
                                float nz = 0;
                                float nw = 0;
                               
                                float value = noise.GetNoise(nx, ny, nz, nw);
                               
                                pixels[x + y * w] = new Color(value, value, value);
                        }
               
                text.SetPixels(pixels);
                text.Apply();
        }

        void Update()
        {              
               
        }
   
        void OnGUI()
        {
                GUI.DrawTexture(new Rect(30, 10, w, h), text);
        }
}
 


код генератора шума

Синтаксис:
Используется csharp
using UnityEngine;

public class Noise
{
        Vector4[] randomVectors;
       
        byte[] randomTable;

        public Noise(int seed)
        {
                var random = new System.Random(seed);
                randomTable = new byte[1024];
                random.NextBytes(randomTable);
               
                randomVectors = new Vector4[81];
                float v = 0;
                while (v < randomVectors.Length) {
                        int x = Mathf.FloorToInt(v / 27);
                        int y = Mathf.FloorToInt((v - x * 27) / 9);
                        int z = Mathf.FloorToInt((v - x * 27 - y * 9) / 3);
                        int w = (int)v - x * 27 - y * 9 - z * 3;
                       
                        randomVectors[(int)v] = new Vector4(x - 1, y - 1, z - 1, w - 1);
                        v++;
                }
        }

        //Получаем случайный вектор для X, Y, Z и W
        Vector2 GetRandomVector(int x, int y, int z, int w)
        {
                int v = (int)(((x * 183) ^ (y * 297) ^ (z * 174) ^ (w * 193) + 480) & 1023);
                v = randomTable[v] & 80;
       
                return randomVectors[v];
        }

        //Сглаживание кривой
        float Curve(float t)
        {
                return t * t * t * (t * (t * 6 - 15) + 10);
        }

        //Функция шума
        public float GetNoise(float fx, float fy, float fz, float fw)
        {
                int pointX = Mathf.FloorToInt(fx);
                int pointY = Mathf.FloorToInt(fy);
                int pointZ = Mathf.FloorToInt(fz);
                int pointW = Mathf.FloorToInt(fw);

                float pointInsideX = fx - pointX;
                float pointInsideY = fy - pointY;
                float pointInsideZ = fz - pointZ;
                float pointInsideW = fw - pointW;
               
                float[] points = new float[16];
                int[] offsetX = new int[]{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };
                int[] offsetY = new int[]{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 };
                int[] offsetZ = new int[]{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 };
                int[] offsetW = new int[]{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };           
               
                for (int p = 0; p < points.Length; p++) {
                        Vector4 gradient = GetRandomVector(pointX + offsetX[p],
                                                   pointY + offsetY[p],
                                                   pointZ + offsetZ[p],
                                                   pointW + offsetW[p]);
                        Vector4 distance = new Vector4(pointInsideX - offsetX[p],
                                                   pointInsideY - offsetY[p],
                                                   pointInsideZ - offsetZ[p],
                                                   pointInsideW - offsetW[p]);
                       
                        points[p] = Vector4.Dot(gradient, distance);
                }
               
                pointInsideX = Curve(pointInsideX);
                pointInsideY = Curve(pointInsideY);
                pointInsideZ = Curve(pointInsideZ);
                pointInsideW = Curve(pointInsideW);
               
                float x1 = Mathf.Lerp(points[0], points[1], pointInsideX);
                float x2 = Mathf.Lerp(points[2], points[3], pointInsideX);
                float x3 = Mathf.Lerp(points[4], points[5], pointInsideX);
                float x4 = Mathf.Lerp(points[6], points[7], pointInsideX);
                float x5 = Mathf.Lerp(points[8], points[9], pointInsideX);
                float x6 = Mathf.Lerp(points[10], points[11], pointInsideX);
                float x7 = Mathf.Lerp(points[12], points[13], pointInsideX);
                float x8 = Mathf.Lerp(points[14], points[15], pointInsideX);
               
                float y1 = Mathf.Lerp(x1, x2, pointInsideY);
                float y2 = Mathf.Lerp(x3, x4, pointInsideY);
                float y3 = Mathf.Lerp(x5, x6, pointInsideY);
                float y4 = Mathf.Lerp(x7, x8, pointInsideY);
               
                float z1 = Mathf.Lerp(y1, y2, pointInsideZ);
                float z2 = Mathf.Lerp(y3, y4, pointInsideZ);
               
                float w1 = Mathf.Lerp(z1, z2, pointInsideW);

                return (w1 + 1) / 2;
        }
}
 
KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

Re: Помогите с алгоритмом генератора 4D шума

Сообщение Cr0c 21 янв 2019, 13:10

Формат "4д шума" какой?
Аватара пользователя
Cr0c
Адепт
 
Сообщения: 3035
Зарегистрирован: 19 июн 2015, 13:50
Skype: cr0c81

Re: Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 21 янв 2019, 16:40

Cr0c писал(а):Формат "4д шума" какой?


Подобие шума Перлина, в качестве случайных величин в сетке вектора со случайным направлением.
KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

Re: Помогите с алгоритмом генератора 4D шума

Сообщение Cr0c 22 янв 2019, 03:09

Как самый простой вариант:
Синтаксис:
Используется csharp
float Perlin4D(Vector4 seed) { return Mathf.PerlinNoise(seed.x + seed.z * 100f, seed.y + seed.w*100f); }
 
Аватара пользователя
Cr0c
Адепт
 
Сообщения: 3035
Зарегистрирован: 19 июн 2015, 13:50
Skype: cr0c81

Re: Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 22 янв 2019, 06:21

Cr0c писал(а):Как самый простой вариант:
Синтаксис:
Используется csharp
float Perlin4D(Vector4 seed) { return Mathf.PerlinNoise(seed.x + seed.z * 100f, seed.y + seed.w*100f); }
 


Это точно не то, значения seed.z * 100f и seed.w*100f задают сдвиг двухмерного шума, ничего общего с четырёхмерным шумом это не имеет.
KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

Re: Помогите с алгоритмом генератора 4D шума

Сообщение jetyb 22 янв 2019, 09:26

Синтаксис:
Используется csharp
        Vector2 GetRandomVector(int x, int y, int z, int w)
        {
                int v = (int)(((x * 183) ^ (y * 297) ^ (z * 174) ^ (w * 193) + 480) & 1023);
                v = randomTable[v] & 80;
       
                return randomVectors[v];
        }
 

v тут - случайная "одинаково" зависящая от 4 переменных величина с значениями от 0 - 1023 . Не факт, что она принимает все эти значения и равномерно, но предположим.
v = randomTable[v] & 80;
Первая ошибка. 80 = 2 ^ 6 + 2 ^ 4, v&80 будет здесь принимать не 80, а всего 4 разных значения. Да и распределение будет не равномерным.
Почему не взять положить длину randomVectors 256, по всем значениям байта.
jetyb
Адепт
 
Сообщения: 1486
Зарегистрирован: 31 окт 2011, 17:21

Re: Помогите с алгоритмом генератора 4D шума

Сообщение Cr0c 22 янв 2019, 09:39

KrolikEdward писал(а):
Cr0c писал(а):Как самый простой вариант:
Синтаксис:
Используется csharp
float Perlin4D(Vector4 seed) { return Mathf.PerlinNoise(seed.x + seed.z * 100f, seed.y + seed.w*100f); }
 


Это точно не то, значения seed.z * 100f и seed.w*100f задают сдвиг двухмерного шума, ничего общего с четырёхмерным шумом это не имеет.

Так сам же про перлин говорил. Чем это не перлин? У него вектор - это сид значения.
Аватара пользователя
Cr0c
Адепт
 
Сообщения: 3035
Зарегистрирован: 19 июн 2015, 13:50
Skype: cr0c81

Re: Помогите с алгоритмом генератора 4D шума

Сообщение jetyb 22 янв 2019, 10:24

Так сам же про перлин говорил. Чем это не перлин? У него вектор - это сид значения.

Нет, это будет вырожденный Перлин, ну все равно что написать вот так
Синтаксис:
Используется csharp
public float MyPerline(float x, float y, float z, float w) => Mathf.Perlin(x,y);
 

Это в четырехмерном пространстве будет просто двумерный цилиндр, зависящий от 2 измерений.
Синтаксис:
Используется csharp
Mathf.PerlinNoise(seed.x + seed.z * 100f, seed.y + seed.w*100f)

Будет таким же цилиндром, только по 2 другим осям.
Афинное преобразование координат не поменяет вырожденность фигуры.
jetyb
Адепт
 
Сообщения: 1486
Зарегистрирован: 31 окт 2011, 17:21


Re: Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 22 янв 2019, 12:57

jetyb писал(а):
Синтаксис:
Используется csharp
        Vector2 GetRandomVector(int x, int y, int z, int w)
        {
                int v = (int)(((x * 183) ^ (y * 297) ^ (z * 174) ^ (w * 193) + 480) & 1023);
                v = randomTable[v] & 80;
       
                return randomVectors[v];
        }
 

v тут - случайная "одинаково" зависящая от 4 переменных величина с значениями от 0 - 1023 . Не факт, что она принимает все эти значения и равномерно, но предположим.
v = randomTable[v] & 80;
Первая ошибка. 80 = 2 ^ 6 + 2 ^ 4, v&80 будет здесь принимать не 80, а всего 4 разных значения. Да и распределение будет не равномерным.
Почему не взять положить длину randomVectors 256, по всем значениям байта.


Вы правы, в построении шума участвуют всего 4 вектора, исправил v = randomTable[v] & 80; на v = randomTable[v]; v = v - Mathf.FloorToInt((float)v / 81) * 81; теперь используются все вектора. Строка int v = (int)(((x * 183) ^ (y * 297) ^ (z * 174) ^ (w * 193) + 480) & 1023); работает.
Проблема всё равно не решилась, при проверке отдельно генерацию по каждому измерению, по осям Z и W, например так float value = noise.GetNoise(0, 0, nz, 0);, шума нет.
Возможно у меня проблема в интерполяции, схему нарисовал, но проблем в ней я не могу найти, на вид всё правильно
Изображение
KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

Re: Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 22 янв 2019, 12:58

KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

Re: Помогите с алгоритмом генератора 4D шума

Сообщение jetyb 22 янв 2019, 14:05

Синтаксис:
Используется csharp
Vector4 gradient = GetRandomVector(pointX + offsetX[p],
                                                   pointY + offsetY[p],
                                                   pointZ + offsetZ[p],
                                                   pointW + offsetW[p]);
                        Vector4 distance = new Vector4(pointInsideX - offsetX[p],
                                                   pointInsideY - offsetY[p],
                                                   pointInsideZ - offsetZ[p],
                                                   pointInsideW - offsetW[p]);
                       
                        points[p] = Vector4.Dot(gradient, distance);
 

Непонятно, откуда взята эта функция Vector4.Dot(gradient, distance);
Она принимает значения от -4 до 4. Как-то странно, обычно все пронормированно от -1 до 1 или от 0 до 1.
Если надо проинтерполировать злы сетки, то функция будет другой.
jetyb
Адепт
 
Сообщения: 1486
Зарегистрирован: 31 окт 2011, 17:21

Re: Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 22 янв 2019, 14:21

jetyb писал(а):
Синтаксис:
Используется csharp
Vector4 gradient = GetRandomVector(pointX + offsetX[p],
                                                   pointY + offsetY[p],
                                                   pointZ + offsetZ[p],
                                                   pointW + offsetW[p]);
                        Vector4 distance = new Vector4(pointInsideX - offsetX[p],
                                                   pointInsideY - offsetY[p],
                                                   pointInsideZ - offsetZ[p],
                                                   pointInsideW - offsetW[p]);
                       
                        points[p] = Vector4.Dot(gradient, distance);
 

Непонятно, откуда взята эта функция Vector4.Dot(gradient, distance);
Она принимает значения от -4 до 4. Как-то странно, обычно все пронормированно от -1 до 1 или от 0 до 1.
Если надо проинтерполировать злы сетки, то функция будет другой.


gradient это случайные вектора в узлах сетки он задаёт направление градиента, distance это вектор к исследуемой точки внутри сетки, от каждого узла вокруг этой точки. points[p] = Vector4.Dot(gradient, distance); это скалярное произведение полученных векторов для каждой из 16 узлов, далее интерполируется по схеме выше, сначала по оси X потом по Y потом по Z и по W. Vector4.Dot(gradient, distance); принимает значение от -4 до 4, потому что это сумма 4-мерных векторов a · b = a1 · b1 + a2 · b2 + ... + an · bn т.е. для n-мерного вектора, значения будут от -n до n.

Попробовал пронормировать points[p] = Vector4.Dot(gradient, distance); от -1 до 1, результат тот же но стал тусклым, нет совсем тёмных и совсем светлых пятен на тестовой текстуре
KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

Re: Помогите с алгоритмом генератора 4D шума

Сообщение jetyb 23 янв 2019, 08:15

Понял где лажа.
На не целых числах разное распределение наблюдается.
На целых же числах
Синтаксис:
Используется csharp
                int pointX = Mathf.FloorToInt(fx);
                int pointY = Mathf.FloorToInt(fy);
                int pointZ = Mathf.FloorToInt(fz);
                int pointW = Mathf.FloorToInt(fw);

                float pointInsideX = fx - pointX;
                float pointInsideY = fy - pointY;
                float pointInsideZ = fz - pointZ;
                float pointInsideW = fw - pointW;
 

pointX = fx, pointInsideX = 0 и т.п
Поэтому везде всегда points[0] = Vector3.Dot(Случайный вектор в узле, (0,0,0,0)) = 0.
С другой стороны, т.к pointInsideX(YZW) = 0 , то при "линейной" интерполяции результатов в сетке получается именно результат points[0].
Возвращается всегда (0 + 1)/2 = 0.5 на целых числах.

То есть при целых числах надо сразу возвращать значение в узлах, а не баловаться с интерполяцией.
jetyb
Адепт
 
Сообщения: 1486
Зарегистрирован: 31 окт 2011, 17:21

Re: Помогите с алгоритмом генератора 4D шума

Сообщение KrolikEdward 23 янв 2019, 16:34

Для векторного шума это норма, в узлах сетки значение всегда 0, если пронормировать от 0 до 1, то будет 0.5 Для шума Перлина это нормально.
KrolikEdward
UNец
 
Сообщения: 8
Зарегистрирован: 21 янв 2019, 10:44

След.

Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 17