Самые короткие и простые способы генерации различных фракталов или других изображений
1,00
р.
р.
Я интересуюсь компьютерной графикой и хотел бы провести своего рода конкурс. Я хотел бы узнать о новых (для себя) способах генерации различных фракталов или других изображений, полученных по достаточно простой формуле. То есть критерием соревнования является, - использование простой базовой формулы для получения интересных картинок.
Например есть такая реализация цикла по всем пикселям на картинке:
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(' ').map(function (str, i) { return ("" + (1 + i)).padStart(4, "0") + ": " + str }).join(' ')) throw message } } attribute vec2 coords void main(void) { gl_Position = vec4(coords.xy, 0.0, 1.0) } precision highp float uniform vec2 mouse uniform vec2 resolution void main(void) { vec2 m = mouse/resolution vec2 p = gl_FragCoord.xy/resolution - 0.5 gl_FragColor = vec4(p, m) }
Ответ Предлагаю Вашему вниманию Суперформулу Суперформула является обобщением суперэллипса и впервые была выведена Йоханом Гиелисом в 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