В этой статье мы покажем простой порт реализации шума Перлина в D. Реализация очень простая и ее легко повторить вообще без сторонних библиотек и иных сторонних компонентов. Если вам интересно, то на правах рецепта, мы прилагаем код ниже.
За основу взята неплохая статья с Habr — «Пишем настоящий шум Перлина», в которой неплохо все изложено и объяснен сам принцип работы предлагаемого кода. Именно из-за простоты, а также из-за схожести языков C# (который был использован в статье, если не ошибаемся) и D была выбрана реализация для портирования. По этим же причинам мы не приводим объяснения механики кода и если вас интересуют подробности, то советуем обратится к оригинальной статье.
Вот полный код класса для двумерного шума Перлина:
import std.math : floor; import std.random; class Perlin2D { private byte[] permutationTable; this(int seed = unpredictableSeed) { permutationTable = new byte[1024]; auto rng = Random(seed); foreach (i; 0..1024) { permutationTable[i] = cast(ubyte) uniform(0, 255, rng); } } private float[] getPseudoRandomGradientVector(int x, int y) { int v = cast(ulong) (((x * 1836311903) ^ (y * 2971215073) + 4807526976) & 1023); v = permutationTable[v] & 3; switch (v) { case 0: return cast(float[]) [1.0, 0.0 ]; case 1: return cast(float[]) [-1.0, 0.0]; case 2: return cast(float[]) [0.0, 1.0 ]; default: return cast(float[]) [0.0, -1.0]; } } static float quinticCurve(float t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } static float lerp(float a, float b, float t) { return a * (t - 1.0) + b * t; } static float lerp2(float a, float b, float t) { return lerp(a, b, quinticCurve(t)); } static float dot(float[] a, float[] b) { return a[0] * b[0] + a[1] * b[1]; } public float generateNoise(float fx, float fy) { int left = cast(int) floor(fx); int top = cast(int) floor(fy); float pointInQuadX = fx - cast(float) left; float pointInQuadY = fy - cast(float) top; float[] topLeftGradient = getPseudoRandomGradientVector(left, top ); float[] topRightGradient = getPseudoRandomGradientVector(left + 1, top ); float[] bottomLeftGradient = getPseudoRandomGradientVector(left, top + 1); float[] bottomRightGradient = getPseudoRandomGradientVector(left + 1, top + 1); debug { writeln(topLeftGradient, " ", topRightGradient, " ", bottomLeftGradient, " ", bottomRightGradient); } float[] distanceToTopLeft = [ pointInQuadX, pointInQuadY ]; float[] distanceToTopRight = [ pointInQuadX - 1, pointInQuadY ]; float[] distanceToBottomLeft = [ pointInQuadX, pointInQuadY - 1 ]; float[] distanceToBottomRight = [ pointInQuadX - 1, pointInQuadY - 1 ]; debug { writeln(distanceToTopLeft, " ", distanceToTopRight, " ", distanceToBottomLeft, " ", distanceToBottomRight); } float tx1 = dot(distanceToTopLeft, topLeftGradient); float tx2 = dot(distanceToTopRight, topRightGradient); float bx1 = dot(distanceToBottomLeft, bottomLeftGradient); float bx2 = dot(distanceToBottomRight, bottomRightGradient); debug { writeln(tx1, " ", tx2, " ", bx1, " ", bx2); } pointInQuadX = quinticCurve(pointInQuadX); pointInQuadY = quinticCurve(pointInQuadY); float tx = lerp(tx1, tx2, pointInQuadX); float bx = lerp(bx1, bx2, pointInQuadX); float tb = lerp(bx, tx, pointInQuadY); debug { writeln(tx, " ", bx, " ", tb); } return tb; } public float generateNoise(float fx, float fy, int octaves, float persistence = 0.5f) { float amplitude = 1; float max = 0; float result = 0; while (octaves --> 0) { max += amplitude; result += generateNoise(fx, fy) * amplitude; amplitude *= persistence; fx *= 2; fy *= 2; } return result / max; } }
Испытать код можно простым выводом в формате P3 или P6, который можно реализовать даже без нашей любимой ppmformats. К примеру можно сделать так:
import std.stdio; void main() { auto pn = new Perlin2D; writeln("P3\n512 512\n255\n"); foreach (i; 0..512) { foreach (j; 0..512) { auto v = cast(int) (127 + 127 * pn.generateNoise(i, j, 10, 0.4)); writeln(v, " ", v, " ", v); } } }
Результат (очень мелко) выглядит примерно так:

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