xxxxxxxxxx
334
const W = 256;
const H = 256;
let RNG;
const ROOT_2 = Math.sqrt(2);
function setup()
{
createCanvas(W, H);
background(0, 0, 0);
RNG = new LinearCongruentialGenerator(41556);
const img = createImage(W, H);
img.loadPixels();
const cols = 16, rows = 16;
const perlinData =
{
GradientVectors: GenerateGradientVectorGrid(cols, rows),
Cols: cols,
Rows: rows,
Exponent: 2,
InMin: Infinity,
InMax: -Infinity,
OutMin: 0,
OutMax: 255,
RGB: [0, 255, 128],
};
Render(img, PerlinNoise, perlinData, PerlinPostProcess);
img.updatePixels();
image(img, 0, 0);
const bayer = new BayerMatrix(4);
print("" + bayer);
}
function GetPixel(img, p)
{
return [Array(4)].map((_, i) => img.pixels[p + i]);
}
function SetPixel(img, p, inputRgba)
{
inputRgba.forEach((v, i) => img.pixels[p + i] = v);
}
function Render(img, renderFunction, args)
{
for (let y = 0; y < H; ++y)
{
for (let x = 0; x < W; ++x)
{
const nx = 2 * x / W - 1;
const ny = 2 * y / H - 1;
const outputRgba = renderFunction(x, y, nx, ny, args);
const p = 4 * (x + W * y);
SetPixel(img, p, outputRgba);
}
}
const postProcessFunctions = args.filter(
arg => typeof arg === "function" && "PostProcess" in arg);
for (const postProcessFunction of postProcessFunctions)
{
for (let y = 0; y < H; ++y)
{
for (let x = 0; x < W; ++x)
{
const p = 4 * (x + W * y);
let inOutRgba = GetPixel(img, p);
const nx = 2 * x / W - 1;
const ny = 2 * y / H - 1;
inOutRgba = postProcessFunction(x, y, nx, ny, inOutRgba, args);
SetPixel(img, p, inOutRgba);
}
}
}
}
function SoftCircle(x, y, nx, ny, reversed)
{
let rSq = nx * nx + ny * ny;
let a = max(1 - rSq, 0) * 255;
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function HGradient(x, y, nx, ny, reversed)
{
let a = 255 * x / W;
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function VGradient(x, y, nx, ny, reversed)
{
let a = 255 * y / H;
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function SlashGradient(x, y, nx, ny, reversed)
{
let a = 255 * ((x / W) + (1 - y / H)) / 2;
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function BackslashGradient(x, y, nx, ny, reversed)
{
let a = 255 * ((x / W) + (y / H)) / 2;
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function PowerSpot(x, y, nx, ny, exponent = 1, reversed = false)
{
let r = Math.sqrt(nx * nx + ny * ny);
let a = 255 * (Math.max(1 - r, 0) ** (1 / exponent));
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function SmoothFalloffSpot(x, y, nx, ny, r, R, exponent = 1, reversed = false)
{
let dSq = nx * nx + ny * ny;
if (dSq < r * r)
return reversed ? [0, 0, 0, 0] : [255, 255, 255, 255];
if (dSq > R * R)
return reversed ? [255, 255, 255, 255] : [0, 0, 0, 0];
let d = Math.sqrt(dSq);
let f = (d - r) / (R - r);
let a;
if (f < 0.5)
a = ((2 * f) ** exponent) / 2;
else
a = 1 - ((2 - 2 * f) ** exponent) / 2;
if (!reversed)
a = 1 - a;
a *= 255;
return [255, 255, 255, a];
}
function SharpFalloffSpot(x, y, nx, ny, r, R, exponent = 1, reversed = false)
{
let dSq = nx * nx + ny * ny;
if (dSq < r * r)
return reversed ? [0, 0, 0, 0] : [255, 255, 255, 255];
if (dSq > R * R)
return reversed ? [255, 255, 255, 255] : [0, 0, 0, 0];
let d = Math.sqrt(dSq);
let f = (d - r) / (R - r);
let a = Math.pow(1 - f ** exponent, 1 / exponent);
if (reversed)
a = 1 - a;
a *= 255;
return [255, 255, 255, a];
}
function HCylinderVGradient(x, y, nx, ny, exponent = 1, reversed = false)
{
let a = (1 - Math.abs(ny) ** exponent) * 255;
if (reversed)
a = 255 - a;
return [255, 255, 255, a];
}
function Noise(x, y, nx, ny, a = 0.5)
{
if (Math.random() > a)
return [0, 0, 0, 0];
return [255, 255, 255, 255];
}
function GrayNoise(x, y, nx, ny, depth = 4)
{
let r = Math.trunc(Math.random() * depth);
if (r === 0)
return [0, 0, 0, 0];
return [255, 255, 255, 255 * r / (depth - 1)];
}
function PerlinNoise(x, y, nx, ny, data)
{
const gradientVectors = data.GradientVectors;
const cols = data.Cols;
const rows = data.Rows;
const exponent = data.Exponent;
const cellX = x * cols / width;
const cellY = y * rows / height;
const x0 = Math.floor(cellX);
const x1 = x0 + 1;
const y0 = Math.floor(cellY);
const y1 = y0 + 1;
const toX0 = cellX - x0;
const toY0 = cellY - y0;
const toX1 = cellX - x1;
const toY1 = cellY - y1;
const iX0 = TrueMod(x0, cols);
const iX1 = TrueMod(x1, cols);
const iY0 = TrueMod(y0, rows);
const iY1 = TrueMod(y1, rows);
const gTL = gradientVectors[iX0][iY0];
const gTR = gradientVectors[iX1][iY0];
const gBL = gradientVectors[iX0][iY1];
const gBR = gradientVectors[iX1][iY1];
const dTL = ArrayDot(gTL, [toX0, toY0]);
const dTR = ArrayDot(gTR, [toX1, toY0]);
const dBL = ArrayDot(gBL, [toX0, toY1]);
const dBR = ArrayDot(gBR, [toX1, toY1]);
const u = Fade(toX0, exponent);
const v = Fade(toY0, exponent);
const top = Lerp(dTL, dTR, u);
const bottom = Lerp(dBL, dBR, u);
const noiseValue = (Lerp(top, bottom, v) + (ROOT_2 / 2)) / ROOT_2;
const opacity = Math.floor(255 * noiseValue);
data.InMin = Math.min(data.InMin, opacity);
data.InMax = Math.max(data.InMax, opacity);
return [data.RGB, opacity];
}
function PerlinPostProcess(x, y, nx, ny, r, g, b, a, data)
{
const inMin = data.InMin;
const inMax = data.InMax;
const outMin = data.OutMin;
const outMax = data.OutMax;
a = (outMax - outMin) * (a - inMin) / (inMax - inMin) + outMin;
return [r, g, b, a];
}
PerlinPostProcess.PostProcess = true;
function GenerateGradientVectorGrid(cols, rows)
{
const vectors = [];
for (let x = 0; x < cols; ++x)
{
const column = [];
for (let y = 0; y < rows; ++y)
column.push(GenerateRandomGradientVector());
vectors.push(column);
}
return vectors;
}
function GenerateRandomGradientVector()
{
const theta = RNG.Random() * TAU;
// const theta = Math.random() * TAU;
return [Math.cos(theta), Math.sin(theta)];
}
function ArrayDot(a, b)
{
return a.reduce((acc, c, i) => acc + c * b[i], 0);
}
const Lerp = (a, b, t) => a + t * (b - a);
const TrueMod = (a, b) => ((a % b) + b) % b;
function Fade(n, exponent)
{
const a = x => Math.pow(x, exponent);
const b = x => 1 - Math.pow(1 - x, exponent);
return n < 0.5 ?
a(2 * n) / 2 :
b(2 * n - 1) / 2 + 0.5;
}