Как использовать функцию шума Перлина? - Java
Формулировка задачи:
Доброго времени суток.
Пытаюсь понять как использовать шум Перлина. После прочтения этой статьи https://habrahabr.ru/post/265775/ я перевел функцию шума Перлина этой статьи на Java:
однако остался ряд следующих вопросов: что передовать в качестве аргумента методу getNoise(float x, float y)? Координаты точки?
Допустим у меня есть некий массив хранящий карту высот, где каждый элемент - тайл или пиксель определенного цвета (от белого, до черного). Я хочу заполнить его используя шум Перлина, чтобы затем использовать для вывода изображения. Я делаю это следующим образом:
и ожидаю получить нечто похожее на туман, облака. Но получаю это:
А вот как выглядит это безобразие, если размер клетки 1 пиксель:
Скажите пожалуйста, что я не так делаю и каковы основные принципы его использования?
import java.util.Random;
public class Perlin2D {
private Random random;
public Perlin2D(long seed) {
random = new Random(seed);
}
public float getNoise(float x, float y) {
int left = (int)x;
int top = (int)y;
float localX = x - left;
float localY = y - top;
// извлекаем градиентные векторы для всех вершин квадрата:
Vector topLeftGradient = getPseudoRandomGradientVector(left, top );
Vector topRightGradient = getPseudoRandomGradientVector(left+1, top );
Vector bottomLeftGradient = getPseudoRandomGradientVector(left, top+1);
Vector bottomRightGradient = getPseudoRandomGradientVector(left+1, top+1);
// вектора от вершин квадрата до точки внутри квадрата:
Vector distanceToTopLeft = new Vector(localX, localY);
Vector distanceToTopRight = new Vector(localX-1, localY);
Vector distanceToBottomLeft = new Vector(localX, localY-1);
Vector distanceToBottomRight = new Vector(localX-1, localY-1);
// считаем скалярные произведения между которыми будем интерполировать
float tx1 = dot(distanceToTopLeft, topLeftGradient);
float tx2 = dot(distanceToTopRight, topRightGradient);
float bx1 = dot(distanceToBottomLeft, bottomLeftGradient);
float bx2 = dot(distanceToBottomRight, bottomRightGradient);
// собственно, интерполяция:
float tx = lerp(tx1, tx2, localX);
float bx = lerp(bx1, bx2, localX);
float tb = lerp(tx, bx, localY);
return tb;
}
private float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
private float dot(Vector a, Vector b) {
return a.x * b.x + a.y * b.y;
}
private Vector getPseudoRandomGradientVector(int x, int y) {
// псевдо-случайное число от 0 до 3 которое всегда неизменно при данных x и y
int v = random.nextInt(4);
switch (v)
{
case 0: return new Vector(1, 0);
case 1: return new Vector(-1, 0);
case 2: return new Vector(0, 1);
default: return new Vector(0,-1);
}
}
private static class Vector {
float x;
float y;
Vector(float x, float y) {
this.x = x;
this.y = y;
}
}
}import java.awt.*;
import java.awt.geom.Rectangle2D;
import javax.swing.*;
public class Map {
private JFrame frame;
public static void main(String[] args) {
new Map();
}
private Map() {
createFrame(500, 500);
}
private void createFrame(int width, int height) {
frame = new JFrame("Perlin noise 2D");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.getContentPane().add(new MyMap(width, height, 1));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class MyMap extends JPanel {
private float[][] map;
private int widthInCell;
private int heightInCell;
private int cellSize;
MyMap(int width, int height, int cellSize) {
setLayout(null);
setPreferredSize(new Dimension(width, height));
widthInCell = width / cellSize;
heightInCell = height / cellSize;
this.cellSize = cellSize;
map = new float[widthInCell][heightInCell];
Perlin2D perlin = new Perlin2D(1000);
for(int x = 0; x < widthInCell; x++) {
for(int y = 0; y < heightInCell; y++) {
float value = perlin.getNoise(x * 0.1f, y * 0.1f);
map[x][y] = (int)(value * 255) & 255;
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for(int x = 0; x < widthInCell; ++x) {
for(int y = 0; y < heightInCell; ++y) {
Rectangle2D cell = new Rectangle2D.Float(x * cellSize, y * cellSize,
cellSize, cellSize);
g2d.setColor(new Color((int)map[x][y], (int)map[x][y], (int)map[x][y]));
g2d.fill(cell);
}
}
}
}
}Решение задачи: «Как использовать функцию шума Перлина?»
textual
Листинг программы
float value = perlin.getNoise(x / 100f, y / 100f, 8, 0.2f) + .5f;