Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Самые короткие и простые способы генерации различных фракталов или других изображений


Я интересуюсь компьютерной графикой и хотел бы провести своего рода конкурс.

Я хотел бы узнать о новых (для себя) способах генерации различных фракталов или других изображений, полученных по достаточно простой формуле.

То есть критерием соревнования является, - использование простой базовой формул
для получения интересных  картинок. 



Например есть такая реализация цикла по всем пикселям на картинке: 



let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height  

let formula = (x, y, cx, cy, m) => {
    return [x/w+cx/w, y/h+cy/h, 0]
}

canvas.onmousemove = e => {
    var img = c.getImageData(0, 0, w, h)
    for(var x = 0; x




Необходимо реализовать функцию formula для получения "интересного" изображения, дополнительными аргументами выступают координаты мыши

Язык - любой, но желательно js, из-за возможности онлайн визуализации.

PS: рекурсивные методы мне менее интересны, особенно если рекурсия не хвостовая
так какпортировать это на glsl будет сложно если не невозможно.

P.P.S. для привлечения более широкой аудитории мной был выбран cpu  и код цикла п
картинке я написал для него, однако если Вам угодно, мне больше импонирует webgl, по этому ниже сниппет, где цикл по всем пикселям делает видеокарта, когда я рисую один треугольник, закрывающий весь экран, а функция formula - это фрагментный шейдер :)



let gl = canvas.getContext('webgl');

gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,  3, -1, -1, 3, -1]), gl.STATIC_DRAW);

let pid = gl.createProgram();
shader('vertex', gl.VERTEX_SHADER);
shader('fragment', gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let coords = gl.getAttribLocation(pid, "coords");
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);

let mouse = gl.getUniformLocation(pid, 'mouse');
let resolution = gl.getUniformLocation(pid, 'resolution');
gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight);

let changeCenter = e => {
  e = e.touches ? e.touches[0] : e;
  gl.uniform2f(mouse, e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
  draw();
}

window.addEventListener('mousemove', changeCenter);
window.addEventListener('touchmove', changeCenter);

draw();

function draw() {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, document.querySelector(`script[type="glsl/${src}"]`).textContent);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split('\n').map(function (str, i) {
      return ("" + (1 + i)).padStart(4, "0") + ": " + str
    }).join('\n'));
    throw message;
  }
}








    


Ответы

Ответ 1



Пример с бассейном Ньютона. Области подкрашиваются в соответствии с близостью к корням уравнения z3-1 = 0 class Complex { constructor({algebraic,trigonometric} = {}) { if (algebraic) { this.initAlgebraic(algebraic); } else if (trigonometric) { this.initTrigonometric(trigonometric) } else { throw new Error('Invalid arguments'); } } static fromReal(r) { return Complex.fromAlgebraic(r, 0); } static fromAlgebraic(r, i) { return new Complex({algebraic: {r,i}}); } static fromTrigonometric(modulus, arg) { return new Complex({trigonometric: {modulus,arg}}); } initAlgebraic({r,i}) { this.r = r; this.i = i; this.modulus = Math.sqrt(r * r + i * i); this.arg = Math.atan2(i, r) } initTrigonometric({modulus,arg}) { this.modulus = modulus, this.arg = arg; this.r = this.modulus * Math.cos(arg); this.i = this.modulus * Math.sin(arg); } pow(exp) { return Complex.fromTrigonometric(Math.pow(this.modulus, exp), exp * this.arg); } add(c) { return Complex.fromAlgebraic(this.r + c.r, this.i + c.i); } conjugate(){ return Complex.fromAlgebraic(this.r, -this.i); } sub(c){ return Complex.fromAlgebraic(this.r - c.r, this.i - c.i); } mul(c){ return Complex.fromAlgebraic(this.r*c.r-this.i*c.i, this.r*c.i+this.i*c.r); } div(c){ var cConjugate = c.conjugate(); var mulToConjugate = this.mul(cConjugate); var divider = c.mul(cConjugate).r; return Complex.fromAlgebraic(mulToConjugate.r/divider, mulToConjugate.i/divider); } } let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height function formula(x,y, maxIter, max, min){ var z = Complex.fromAlgebraic(x,y); var d = z; for(var i=0;imin;i++){ var z1 = z.sub(z.pow(3).add(Complex.fromReal(-1)).div(z.pow(2).mul(Complex.fromReal(3)))); //var z1 = z.sub(z.pow(5).add(Complex.fromReal(-1)).div(z.pow(4).mul(Complex.fromReal(5)))); var z2 = z1.sub(z); d = Complex.fromAlgebraic(Math.abs(z2.r),Math.abs(z2.i)); z = z1; } return [i, z]; } var input = document.getElementById('coef'); input.addEventListener('change',function(){ draw(this.value); }); var roots = [ Complex.fromReal(1), Complex.fromAlgebraic(-1, Math.sqrt(3)/2), Complex.fromAlgebraic(-1, -Math.sqrt(3)/2), ] var maxDistance = input.max; function draw(distance){ var xc = w/2; var yc = h/2; var coef = 0.0015 + distance * ((0.015-0.0015)/maxDistance); var img = c.getImageData(0, 0, w, h); var aaa = 0; for(var y = -h/2; y Для урaвнения z5-1 = 0 class Complex { constructor({algebraic,trigonometric} = {}) { if (algebraic) { this.initAlgebraic(algebraic); } else if (trigonometric) { this.initTrigonometric(trigonometric) } else { throw new Error('Invalid arguments'); } } static fromReal(r) { return Complex.fromAlgebraic(r, 0); } static fromAlgebraic(r, i) { return new Complex({algebraic: {r,i}}); } static fromTrigonometric(modulus, arg) { return new Complex({trigonometric: {modulus,arg}}); } initAlgebraic({r,i}) { this.r = r; this.i = i; this.modulus = Math.sqrt(r * r + i * i); this.arg = Math.atan2(i, r) } initTrigonometric({modulus,arg}) { this.modulus = modulus, this.arg = arg; this.r = this.modulus * Math.cos(arg); this.i = this.modulus * Math.sin(arg); } pow(exp) { return Complex.fromTrigonometric(Math.pow(this.modulus, exp), exp * this.arg); } add(c) { return Complex.fromAlgebraic(this.r + c.r, this.i + c.i); } conjugate(){ return Complex.fromAlgebraic(this.r, -this.i); } sub(c){ return Complex.fromAlgebraic(this.r - c.r, this.i - c.i); } mul(c){ return Complex.fromAlgebraic(this.r*c.r-this.i*c.i, this.r*c.i+this.i*c.r); } div(c){ var cConjugate = c.conjugate(); var mulToConjugate = this.mul(cConjugate); var divider = c.mul(cConjugate).r; return Complex.fromAlgebraic(mulToConjugate.r/divider, mulToConjugate.i/divider); } } let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height function formula(x,y, maxIter, max, min){ var z = Complex.fromAlgebraic(x,y); var d = z; for(var i=0;imin;i++){ //var z1 = z.sub(z.pow(3).add(Complex.fromReal(-1)).div(z.pow(2).mul(Complex.fromReal(3)))); var z1 = z.sub(z.pow(5).add(Complex.fromReal(-1)).div(z.pow(4).mul(Complex.fromReal(5)))); var z2 = z1.sub(z); d = Complex.fromAlgebraic(Math.abs(z2.r),Math.abs(z2.i)); z = z1; } return [i, z]; } var input = document.getElementById('coef'); input.addEventListener('change',function(){ draw(this.value); }); var maxDistance = input.max; function draw(distance){ var xc = w/2; var yc = h/2; var coef = 0.0015 + distance * ((0.015-0.0015)/maxDistance); var img = c.getImageData(0, 0, w, h); var aaa = 0; for(var y = -h/2; y

Ответ 2



Предлагаю Вашему вниманию Суперформулу Суперформула является обобщением суперэллипса и впервые была выведена Йоханом Гиелисо в 2003 году. Гиелис предположил использовать формулу для описания сложных форм и кривых, которые встречаются в природе. В полярной системе координат, с радиусом, и углом, суперформула выглядит так: Выбирая различные значения параметров , получаются различные формы. let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height // функция возвращает расстояние в полярной системе координат для угла phi, идущего первым аргументом function superformula(phi, m, n1, a, b, n2, n3) { with (Math) { m = m*phi/4; a = pow(abs(cos(m))/a, n2); b = pow(abs(sin(m))/b, n3); return pow(a + b, -1/n1); } } let formula = (x, y, cx, cy) => { x = (2*x-w)/w*(3-cy/h); y = (2*y-h)/w*(3-cy/h); let a = Math.atan2(y, x) - cx/w*Math.PI; let d = superformula(a, t.m, t.n1, t.a, t.b, t.n2, t.n3); let l = Math.sqrt(x*x + y*y); let c = Math.min(d-l)*10.; return [c, c, c] } let types = { asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1}, bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1}, butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1}, circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1}, clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1}, cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1}, cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1}, diamond: {m: 4, n1: 1, n2: 1, n3: 1, a: 1, b: 1}, drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1}, gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1}, heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18}, heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1}, hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1}, malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1}, pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1}, rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1}, roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1}, square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1}, star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1}, triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1} }; let t = Object.values(types)[0]; let draw = e => { var img = c.getImageData(0, 0, w, h) for(var x = 0; x { draw(e); } canvas.onclick = e => { let vals = Object.values(types); let i = Math.floor(Math.random()*vals.length); t = vals[i]; document.querySelector('span').textContent = 'click to change: ' + Object.keys(types)[i] draw(e) }
click to change: asterisk

Ответ 3



Самое простое что известно мне, помимо фракталов в комплексной плоскости - это фрактал kali, назван в честь опубликовавшего эту формулу на fractalforums формула в glsl для него вообще простая: vec2 q = vec2(x, y); for(var i=0; i<10; i++) q = abs(q)/dot(q,q) - vec2(cx, cy); на js чуть посложнее из-за отсутствия операций над векторами, но все равно очен простая: let formula = (x, y, cx, cy, m) => { x = (2*x-w)/w; y = (2*y-h)/w; for (var i=0; i<10; i++) { x = Math.abs(x) y = Math.abs(y) m = x*x + y*y x = x/m - cx/w y = y/m - cy/h } return [x, y, Math.sqrt(x*x+y*y)/2.] } Вот результат для cpu версии, мышка меняет переменные в формуле для получения другого изображения: let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height let formula = (x, y, cx, cy, m) => { x = (2*x-w)/w; y = (2*y-h)/w; for (var i=0; i<10; i++) { x = Math.abs(x) y = Math.abs(y) m = x*x + y*y x = x/m - cx/w y = y/m - cy/h } return [x, y, Math.sqrt(x*x+y*y)/2.] } canvas.onmousemove = e => { var img = c.getImageData(0, 0, w, h) for(var x = 0; x GPU вариация и другая палитра let gl = canvas.getContext('webgl'); gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 3, -1, -1, 3, -1]), gl.STATIC_DRAW); let pid = gl.createProgram(); shader('vertex', gl.VERTEX_SHADER); shader('fragment', gl.FRAGMENT_SHADER); gl.linkProgram(pid); gl.useProgram(pid); let coords = gl.getAttribLocation(pid, "coords"); gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(coords); let mouse = gl.getUniformLocation(pid, 'mouse'); let resolution = gl.getUniformLocation(pid, 'resolution'); gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight); let changeCenter = e => { e = e.touches ? e.touches[0] : e; gl.uniform2f(mouse, e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop); draw(); } window.addEventListener('mousemove', changeCenter); window.addEventListener('touchmove', changeCenter); draw(); function draw() { gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.clearColor(0, 0, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, 3); } function shader(src, type) { let sid = gl.createShader(type); gl.shaderSource(sid, document.querySelector(`script[type="glsl/${src}"]`).textContent); gl.compileShader(sid); var message = gl.getShaderInfoLog(sid); gl.attachShader(pid, sid); if (message.length > 0) { console.log(src.split('\n').map(function (str, i) { return ("" + (1 + i)).padStart(4, "0") + ": " + str }).join('\n')); throw message; } } Еще одна вариация фрактала let gl = canvas.getContext('webgl'); gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 3, -1, -1, 3, -1]), gl.STATIC_DRAW); let pid = gl.createProgram(); shader('vertex', gl.VERTEX_SHADER); shader('fragment', gl.FRAGMENT_SHADER); gl.linkProgram(pid); gl.useProgram(pid); let coords = gl.getAttribLocation(pid, "coords"); gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(coords); let mouse = gl.getUniformLocation(pid, 'mouse'); let resolution = gl.getUniformLocation(pid, 'resolution'); gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight); let changeCenter = e => { e = e.touches ? e.touches[0] : e; gl.uniform2f(mouse, e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop); draw(); } window.addEventListener('mousemove', changeCenter); window.addEventListener('touchmove', changeCenter); draw(); function draw() { gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.clearColor(0, 0, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, 3); } function shader(src, type) { let sid = gl.createShader(type); gl.shaderSource(sid, document.querySelector(`script[type="glsl/${src}"]`).textContent); gl.compileShader(sid); var message = gl.getShaderInfoLog(sid); gl.attachShader(pid, sid); if (message.length > 0) { console.log(src.split('\n').map(function (str, i) { return ("" + (1 + i)).padStart(4, "0") + ": " + str }).join('\n')); throw message; } }

Ответ 4



Множество Жюлиа (Julia set) Формула для него выглядит вот так: где z это комплексное число: В этом примере значение c зависит от координат мышки, что позволяет одновременн наблюдать великое множество различных изображений множества Жюлиа Все эти изображения получены при помощи нижеприведенного сниппета с минимальным изменениями let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height let formula = (x, y, cx, cy, m) => { let z = [(2*y-h)/w*1.5,(2*x-w)/w*1.5]; for (var i = 0; i < 32; ++i) { z = [ z[0] * z[0] - z[1] * z[1] + cy/h, 2. * z[0] * z[1] + cx/w]; if (z[0]*z[0] + z[1]*z[1] > 4.) return i; } return 0 } canvas.onmousemove = e => { var img = c.getImageData(0, 0, w, h) for(var x = 0; x По очень похожей формуле получается обобщение всех множеств Жюлиа - Множество Мандельброта (Mandelbrot set)

Ответ 5



Сетка Аполлона. let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height; var x = 0.2, y = 0.3, a = 0, b = 0, r = Math.sqrt(3), points = Array(1000), counter = 0; (function starter() { draw(); requestAnimationFrame(starter); }()) function sqr(x) { return x * x; } function putPixels(x, y) { var img = c.getImageData(0, 0, w, h) let offset = (y * w + x) * 4; img.data[offset] = 255; img.data[offset + 1] = 0; img.data[offset + 2] = 0; img.data[offset + 3] = 255; c.putImageData(img, 0, 0) } function draw() { a = Math.random(); var div = sqr(1 + r - x) + sqr(y); var a0 = 3 * (1 + r - x) / (div) - (1 + r) / (2 + r); var b0 = 3 * y / (div); if (a <= 1 / 3 && a >= 0) { x1 = a0; y1 = b0; } else { var a1 = -1 / 2; var b1 = r / 2; var a2 = -1 / 2; var b2 = -r / 2; var d =sqr(a0) + sqr(b0); var f1x = a0 / (d); var f1y = -b0 / (d); if (a <= 2 / 3 && a > 1 / 3) { x1 = f1x * a1 - f1y * b1; y1 = f1x * b1 + f1y * a1; } if (a <= 3 / 3 && a > 2 / 3) { x1 = f1x * a2 - f1y * b2; y1 = f1x * b2 + f1y * a2; } } x = x1; y = y1; putPixels(320 + x * 50 | 0, 240 + y * 50 | 0); }

Ответ 6



При возведении комплексного аргумента в 4 степень можно получить фрактал по форме напоминающий живой организм, такие фракталы называют биоморфами. class Complex { constructor({algebraic,trigonometric} = {}) { if (algebraic) { this.initAlgebraic(algebraic); } else if (trigonometric) { this.initTrigonometric(trigonometric) } else { throw new Error('Invalid arguments'); } } static fromReal(r) { return Complex.fromAlgebraic(r, 0); } static fromAlgebraic(r, i) { return new Complex({algebraic: {r,i}}); } static fromTrigonometric(modulus, arg) { return new Complex({trigonometric: {modulus,arg}}); } initAlgebraic({r,i}) { this.r = r; this.i = i; this.modulus = Math.sqrt(r * r + i * i); this.arg = Math.atan2(i, r) } initTrigonometric({modulus,arg}) { this.modulus = modulus, this.arg = arg; this.r = this.modulus * Math.cos(arg); this.i = this.modulus * Math.sin(arg); } pow(exp) { return Complex.fromTrigonometric(Math.pow(this.modulus, exp), exp * this.arg); } add(c) { return Complex.fromAlgebraic(this.r + c.r, this.i + c.i); } } let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height function formula(x,y,cx,cy, maxIter){ var z = Complex.fromAlgebraic(x,y); var c = Complex.fromAlgebraic(cx,cy); for(var i=0;i 10 || Math.abs(z.i) > 1000) { return i; } } return 0; } canvas.onmousemove = e => { var xc = w/2; var yc = h/2; var img = c.getImageData(0, 0, w, h) for(var y = -h/2; y

Ответ 7



Фрактал Ляпунова Фракталы Ляпунова строятся отображением областей стабильного и хаотического поведения измеряемых экспонентой Ляпунова, в плоскости a-b для данной периодической последовательности a и b. Форма зависит от введенной строки включающей символы A и B, а так же от выбранного квадрата в диапазоне [0x4, 0x4] Приведенный пример с конкретными параметрами также известен как Zircon City. let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height var i = 0; function formula(a, b, N, S) { var xn = 0.5; var sum = 0; for (var n = 1; n < N; n++) { var rn = S[n % S.length] == 'A' ? a : b; xn = rn * xn * (1 - xn); sum += Math.log2(Math.abs(rn * (1 - 2 * xn))); if (sum > 10000) return 50; if (sum < -10000) return -50; } return sum / N; } function getRect() { var xmin = +document.getElementById('xmin').value; var xmax = +document.getElementById('xmax').value; var ymin = +document.getElementById('ymin').value; var ymax = +document.getElementById('ymax').value; return [{ start: Math.min(xmin, xmax), range: Math.abs(xmax - xmin) / w }, { start: Math.min(ymin, ymax), range: Math.abs(ymax - ymin) / h }]; } function draw() { var img = c.getImageData(0, 0, w, h) var S = document.getElementById('S').value; var [p1, p2] = getRect(); for (var x = 0; x < w; x++) { for (var y = 0; y < h; y++) { let La = formula(p1.start + x * p1.range, p2.start + y * p2.range, 1000, S); let offset = ((h - y) * w + x) * 4 if (La > 0) { img.data[offset] = (La * 25 + 10) % 256; img.data[offset + 1] = (La * 25 + 10) % 216; } else { img.data[offset] = 255 - (-La * 20 + 50) % 256 img.data[offset + 1] = 215 - (-La * 20 + 50) % 216 } img.data[offset + 2] = 0 img.data[offset + 3] = 255 } } c.putImageData(img, 0, 0) } draw(); #c { display: flex; } #settings{ margin-left:10px; }




Ответ 8



В продолжение темы фракталов в комплексной плоскости - вариация множества Мандельброта но в каждой итерации компоненты комплексного z берутся по модулю - имеет название Burning Ship, за визуальную похожесть деталей фрактала на корабль. Интересен очень высокой вариативностью геометрических узоров при большом увеличении. ... z[0] = Math.abs(z[0]); z[1] = Math.abs(z[1]); ... Если приближаться в район мачт кораблей можно найти интересные участки let c = canvas.getContext('2d'), w=canvas.width, h=canvas.height, cx=0, cy=0, z=0.25; let formula = (x, y, cx, cy, it) => { let z = [0,0]; for (var i = 0; i < it; ++i) { z[0] = Math.abs(z[0]); z[1] = Math.abs(z[1]); z = [ z[0] * z[0] - z[1] * z[1] - x+cx, 2. * z[0] * z[1] + y-cy]; if (z[0]*z[0] + z[1]*z[1] > 4.1) return i; } return 0 } let p, ct; canvas.addEventListener('mousedown',function(e){ p = {x:e.pageX, y:e.pageY}; ct = {x:cx, y:cy}; }) canvas.addEventListener('mousemove',function(e){ if (!p) return; cx = ct.x + (e.pageX - p.x)/w/z; cy = ct.y + (e.pageY - p.y)/h/z; render() }) canvas.addEventListener('mouseup',function(e){ p = null }) canvas.addEventListener('wheel',function(e){ z *= 1 + Math.sign(e.wheelDeltaY)*0.1 e.preventDefault(); render() }) render() function render() { let red = r.value; let green = g.value; let blue = b.value; let it = iter.value; var img = c.getImageData(0, 0, w, h) for(var x = 0; x
PS: В сниппете работает колесо мышки и драг, при помощи слайдеров можно поменят кол-во итерация и палитру

Комментариев нет:

Отправить комментарий