English version of this page

Å skape et verktøy for generativ kunst

Creative Computing Hub Oslo (C2HO) presenterer en serie verksteder med internasjonale utøvere av kreativ databehandling som vil forklare kodingsprosessen og de estetiske vurderingene bak ett av deres digitale kunstverk.

Image may contain: Font, Slope, Astronomical object, Science, Space.

Introduksjon

Hei, jeg heter Stevan Dedovic. Denne opplæringen, og en tilhørende video, ble laget for Creative Computing Hub Oslo. De ba om at alt skulle publiseres til det offentlige domenet.

Med mindre annet er nevnt, er dette arbeidet lisensiert under CC-BY: https://creativecommons.org/licenses/by/3.0

 

For litt bakgrunn, rundt 2018 besøkte jeg Metropolitan-museet i New York. De hadde en utstilling om abstraksjonisme, og stykkene om geometrisk abstraksjonisme stakk virkelig ut for meg. Kort tid etter holdt jeg en kunstutstilling i Chicago og jeg måtte presentere noen nye arbeider. De følgende stykkene kan finnes på nettsiden min, dedovic.com, og vårt mål i dag er å gjenskape dem.

© 2018 Stevan Dedovic, All Rights Reserved© 2018 Stevan Dedovic, All Rights Reserved

Denne beskrivelsen er et sted mellom en opplæring og forklaring, og den vil følge min kreative prosess. Den er ment for lesere som kjenner litt til Javascript og kanskje p5.js.

P5-oppsett

Først, la oss sette opp et miljø med nyttige funksjoner og standardinnstillinger.

Dette vil sette fargemodusen til HSB (også kjent som HSV), som er litt mer intuitiv enn RGB-fargerommet.

function setup() {
  colorMode(HSB, 360, 100, 100, 1.0);
}
 
function draw() {}

Deretter, la oss lage tegnefunksjonen for å tømme bakgrunnen og tegne en enkel trekant på skjermen. Vi må også lage et lerret.

function setup() {
  colorMode(HSB, 360, 100, 100, 1.0);
 
  // 400px x 400px nearly fills the screen on my laptop
  createCanvas(400, 400); 
}
 
function draw() {
  // 0 saturation and 100 brightness means a white background
  background(0, 0, 100);
 
  // 0 brightness means a black stroke line
  stroke(0, 0, 0);
 
  // small triangle
  triangle(10, 10, 50, 50, 50, 10);
}

Allerede her er det et problem. Størrelsen på lerretet og koordinatene til trekanten er definert i piksler. Dette betyr at skalering av tegningen senere vil være problematisk, for eksempel om vi vil ha en høy DPI-versjon for utskrift eller publisering. Dette kan løses ganske lett, selv om, ved å unngå pikselkoordinater fra starten av, med noen hjelpefunksjoner.

/** Convert normalised value [0, 1] to canvas coordinate [0, WIDTH] */
function w(val) {
  return val * width;
}
 
/** Convert normalised value [0, 1] to canvas coordinate [0, HEIGHT] */
function h(val) {
  return val * height;
}
 
...
 
function draw() {
  background(0, 0, 100);
  stroke(0, 0, 0);
 
  // this is necessary to scale the thickness of the stroke
  //  with the size of the canvas. The value is approximate,
  //  and looks consistent with the 1px line from before.
  strokeWeight(w(0.003));
 
  // draw triangle using coordinates relative to the canvas
  triangle(w(0.1), h(0.1), w(0.9), h(0.9), w(0.9), h(0.1));
}

Nå kan lerrets størrelse endres men tegningen forblir konsistent. Prøv det ut.

Lenke til p5.js-skisse

Merk: Det er verdt å definere datatypene våre på forhånd.

  • i javascript kan number-typer være enten integer eller decimal (floating point), så forskjellen er ikke nødvendig
  • et punkt er en 2-liste med tall, f.eks. [0, 0] eller [3.1, 9.2]
  • en trekant er en 3-liste med punkter, f.eks. [[0, 0], [1, 0], [1, 1]]

Trekantinndeling

Dette er den første av en håndfull mønstre i verktøykassen til generative kunstnere jeg vil dekke i dag. Det finnes mange algoritmer og ferdige biblioteker for å utføre trekantinndeling, men vi vil skrive vår egen.

La oss definere "trekantinndeling" som en funksjon som tar en trekant, kutter den i to, og gir ut en liste over de to mindre halvdelene. I tillegg skal inndelingen se tilfredsstillende ut. Dette betyr å skape pent plasserte trekanter ved å dele den lengste siden av trekanten. Til slutt trenger den å være litt tilfeldig og la oss justere hvor tilfeldig. Den overordnede algoritmen er som følger:

  1. Finn lengdene på alle sidene av trekanten
  2. Finn den lengste siden
  3. Velg et punkt m langs den lengste siden, nær midten
  4. Del trekanten i to

Her er det, som kode:

/** Calculates the equality between two floats. */
function equalf(a, b) {
    return Math.abs(- b) < Number.EPSILON;
}
 
/** Subdivides a triangle. Returns a list of triangles. */
function subdivide(tri) {
  // destructure the individual coordinates
  const [[ax, ay], [bx, by], [cx, cy]] = tri;
 
  // Step 1 - find lengths of sides
  const lenAB = dist(ax, ay, bx, by);
  const lenBC = dist(bx, by, cx, cy);
  const lenCA = dist(cx, cy, ax, ay);
 
  // Step 2 - determine longest side
  const longest = Math.max(lenAB, lenBC, lenCA);
 
  // Helper Function
  // Subdivide the traingle ABC along the side AB. 
  // Picks a point random, but close to the midpoint of AB.
  const subdivideAB = (a, b, c) => {
    const [ax, ay] = a;
    const [bx, by] = b;
 
    // Step 3 - select random point close to midpoint
    const n = randomGaussian(0, 0.09);
    const xm = lerp(ax, bx, 0.5 + n);
    const ym = lerp(ay, by, 0.5 + n);
    const p = [xm, ym];
 
    // Step 4 - create two new triangles, splitting side AB
    return [
        [p, a, c],
        [p, b, c]
    ];
  };
 
  // Step 3 - subdivide the longest side
  const [a, b, c] = tri;
  if (equalf(lenAB, longest))
    return subdivideAB(a, b, c);
  if (equalf(lenBC, longest))
    return subdivideAB(b, c, a);
  if (equalf(lenCA, longest))
    return subdivideAB(c, a, b);
}

Jeg vil forklare noen avgjørelser i de neste seksjonene, føl deg fri til å hoppe over denne delen hvis du ikke trenger en grundig forklaring. Men først, for å bruke den ovennevnte funksjonen, må vi endre litt på tegnefunksjonen:

// pull our initial triangle out of the draw function
const defaultTri = [[0.1, 0.1], [0.9, 0.9], [0.9, 0.1]];
 
function draw() {
  background(0, 0, 100);
  stroke(0, 0, 0);
  strokeWeight(w(0.003));
 
  // since subdivide returns a list of triangles, we must iterate over them
  for(tri of subdivide(defaultTri)) {
    const [[x1, y1], [x2, y2], [x3, y3]] = tri;
    triangle(w(x1), h(y1), w(x2), h(y2), w(x3), h(y3));
  }
}

Lenke til p5.js-skisse

Hvis du er interessert i å utforske trekantinndeling videre, anbefaler jeg å sjekke ut denne artikkelen fra 2017 av kunstneren Tyler Hobbs.

flytetall likhet

Flytetall kan være vanskelige når det gjelder rekkefølge og likehet. De har begrenset presisjon, noe som betyr at noen tall, som 1/3, ikke kan representeres uten informasjonstap. Dette emnet er godt dekket på internett. Likhet mellom flytetall må behandles med forsiktighet. Ovenfor har jeg definert en hjelpefunksjon, equalf, som sammenligner to flytetall og returnerer om de er nære nok til å anses som like. Dette er ikke strengt nødvendig, men jeg er mot forsiktighet. Det er vanligvis bedre å skrive litt mer kode enn å håndtere rare presisjonsproblemer med flytetall. Sjekk også ut dokumentasjonen for Javascript Math.EPSILON for mer informasjon.

Merk - Julia Evans' "wizard zines" har en utrolig morsom og informativ zine, med tittelen “How Integers and Floats Work” og jeg kan ikke anbefale den nok!

lerp

Jeg brukte lerp-funksjonen eller lineær interpolasjonsfunksjonen ovenfor, for å velge et midtpunkt mellom to verdier. Hvis du ikke er kjent med den, returnerer den verdien av interpolering fra det første parameteret til det andre parameteret med et beløp fra 0 til 1. Jeg liker å bruke lerp fordi den er svært fleksibel og effektiv.

randomGaussian

Ved å bruke randomGaussian kan vi tilfeldiggjøre plasseringen av midtpunktet langs siden AB. Men, i motsetning til random-funksjonen, er ikke den valgte verdien jevnt valgt; med en Gaussisk fordeling er verdier nær gjennomsnittet mer sannsynlige enn verdier langt fra gjennomsnittet. randomGaussian-funksjonen i P5js tar imot to parametere - gjennomsnittet og standardavviket. Måten den brukes sammen med lerp i koden ovenfor blir effektivt, "velg et punkt tilfeldig, men sannsynligvis nær midtpunktet av linjen".

Interaksjon

Neste på vår reise er å sette opp litt live interaksjon. Vi begynner smått - jeg vil dele den opprinnelige trekanten hver gang jeg trykker på S-tasten på tastaturet. P5.js har en veldig enkel måte å gjøre dette på - funksjonen keyPressed. Dette er liknende setup og draw ved at det er definert av oss, og deretter utfører P5 kjøretiden det.

For å dele trekanten, må vi håndtere tilstanden. For øyeblikket er det draw-funksjonen der subdivide blir kalt, og det betyr at det blir kalt én gang per rendering. I stedet ønsker vi å lagre en liste over alle trekantene som skal tegnes, og:

  • oppdater ved å dele når tasten s blir trykket
  • iterer over trekantene når man renderer i draw-funksjonen

La oss begynne med å eksternalisere trekantene vi planlegger å rendre:

// keep the default as it is useful
const defaultTri = [[0.1, 0.1], [0.9, 0.9], [0.9, 0.1]];
 
// this is our list of triangles to draw. 
// Note the `let` instead of `const` - this is necessary as we will reassign the value when subdividing.
let toDraw = [defaultTri];
 
function draw() {
  background(0, 0, 100);
  stroke(0, 0, 0);
  strokeWeight(w(0.003));
 
  // this will _not_ perform subdivision
  for(tri of toDraw) {
    const [[x1, y1], [x2, y2], [x3, y3]] = tri;
    triangle(w(x1), h(y1), w(x2), h(y2), w(x3), h(y3));
  }
}

Å kjøre koden ovenfor vil bare rendre den opprinnelige trekanten. Ingenting nytt enda, så la oss implementere keyPressed-funksjonen.

// called by P5js whenever a key is pressed.
// See P5 docs for semantics and comparison with other key interaction functions

function keyPressed() {
    if (key === 's') {
        toDraw = toDraw.flatMap(subdivide);
    }
}

Denne funksjonen er ganske enkel - når S-tasten trykkes, tilordnes verdien av toDraw på nytt ved å dele hver trekant i toDraw. flatMap-funksjonen kan sammenlignes med å utføre en map og deretter konkatenerer resultatene.

Så nå, ved å kjøre dette alt sammen, får vi en interaktiv liten app. Prøv å endre strekvekten, det andre parameteret av randomGaussian, eller defaultTri. Legg merke til også hvor lang tid det tar å dele - ytelsen kan være ganske dårlig ved 5+ iterasjoner. Det kan også fullstendig krasje nettleseren din, så vær forsiktig!

Lenke til p5.js-skisse

Offsetting

Neste opp er jeg ønsker å krympe alle trekantene. Dette kalles offsetting, og for trekanter er det enkelt nok. Vi kan flytte en trekant ved å finne sentrum og flytte hvert hjørnepunkt litt innover. Selv om trekanter har flere sentre, vil jeg bruke centroid, på grunn av personlige preferanser og enkelheten av centroid-algoritmen.

// Offsets or insets the triangle by the specified
//  amount. Positive values shrink, negative values grow.   
function offset(tri, amount) {
    const [[x1, y1], [x2, y2], [x3, y3]] = tri;
 
    // calculate centroid coordinates  
    const cx = (x1 + x2 + x3) / 3;
    const cy = (y1 + y2 + y3) / 3;
 
    const moveToward = (px, py, tx, ty) => {
      // atan2 gives us the angle between two points
      const theta = atan2(ty - py, tx - px);
      return [
        px + amount * cos(theta),
        py + amount * sin(theta)
      ];
    };
 
    return [
        moveToward(x1, y1, cx, cy),
        moveToward(x2, y2, cx, cy),
        moveToward(x3, y3, cx, cy),
    ];
}

Og koble offsetting-algoritmen til vår tastaturinndatatering:

function keyPressed() {
    ...
 
    if (key === 'j') {
        toDraw = toDraw.map(tri => offset(tri, 0.01));
    }
    if (key === 'k') {
        toDraw = toDraw.map(tri => offset(tri, -0.01));
    }
}

Nå kan du kjøre appen på nytt og leke med å dele, vokse og krympe alle trekantene. Men det er et par små problemer som kan forbedres.

Holde nede en knapp

Først, i stedet for å bruke keyPressed, kan vi flytte offsettingen til å skje i draw, ved bruk av funksjonen keyIsDown. Dette betyr at vi håndterer holding ikke trykking for å endre formen.

function draw() {
  // setup code
  ...
 
 
  if (keyIsDown(74)) { // "j"
    toDraw = toDraw.map(tri => offset(tri, 0.005));
  }
  if (keyIsDown(75)) { // "k"
    toDraw = toDraw.map(tri => offset(tri, -0.005));
  }
 
  // drawing code
  ...
}

To ting å merke seg:

  • keyIsDown fungerer med tastekoder, ikke en karakter. P5js-dokumentene refererer til dette nettstedet for å slå opp tastekoder. Det er verdt å merke seg at de er følsomme for store og små bokstaver.
  •  siden draw kalles for hver ramme, senket jeg styrken til offsettet til 0,002.

Lenke til p5.js-skisse

Rette opp offsettingen

En ting du kanskje legger merke til er at offsetting-algoritmen forvrenger formen på trekanten fordi den fungerer. Dette er fordi vi må flytte trekantens punkter proporsjonalt i forhold til avstanden fra centroid.

Måten dette kan gjøres på er ved å linjært skalere mengden av offsetting basert på hvor langt hvert punkt er fra centroid, og bruke den lengste avstanden som basisbeløpet. I kode betyr dette å modifisere offsett-funksjonen som følger,

function offset(tri, amount) {
  ...
 
  // longest distance from triangle point to centroid -
  //   this is the baseline
  const lenLongest = Math.max(
    dist(x1, y1, cx, cy),
    dist(x2, y2, cx, cy),
    dist(x3, y3, cx, cy)
  );
 
  const moveToward = (px, py, tx, ty) => {
    const theta = atan2(ty - py, tx - px);
 
    // figure out scaling factor based on length from this
    //  triangle point to centroid and baseline      
    const len = dist(px, py, tx, ty);
    const scaleFactor = len / lenLongest;
 
    // now scale the offsetting amount by the above factor  
    return [
      px + amount * scaleFactor * cos(theta),
      py + amount * scaleFactor * sin(theta)
    ];
  };
 
  ...
}

Det er nå mye bedre! Det finnes sannsynligvis bedre algoritmer for å gjøre dette, men jeg liker denne tilnærmingen fordi det lar meg justere noen av disse verdiene senere slik at jeg kan endre oppførselen til offsettingen på en unik måte.

Lenke til p5.js-skisse

Deaktivere bakgrunnsoppdatering

Et av mine favoritttriks er å deaktivere oppdatering av bakgrunnen i et interaktivt kunstverk. Å fjerne fyll og senke strekens opasitet er også en god idé.

function draw() {
  // commented out now
  // background(0, 0, 100);     
 
  noFill();
  stroke(0, 0, 0, 0.05);
 
  ...
}

Interaksjoner, del 2

Jeg skal ta en kjapp avstikker og forbedre den overordnede opplevelsen av skissen. Det vil gjøre den til et generelt bedre verktøy.

Veksle tegning

Først opp er veksling av tegningen av trekantene og skru den av som standard.

// optionally set this to true so that is draws by default 
let shouldDraw = false;
 
function keyPressed() {
  ...
 
 
  if (key === 'd') {
    shouldDraw = !shouldDraw;
  }
}
 
function draw() {
  ...
 
  if (shouldDraw) {
    for(tri of toDraw) {
      const [[x1, y1], [x2, y2], [x3, y3]] = tri;
      triangle(w(x1), h(y1), w(x2), h(y2), w(x3), h(y3));
    }
  }
 
  ...
}

Merk: siden bare tegnedelen av draw-funksjonen er betinget, vil vi alltid dele, vokse, krympe trekantene. Ingen av interaksjonene påvirkes.

Tilbakestille

Denne er ganske enkel. Når jeg trykker på r, vil jeg at hele skissen skal tilbakestilles. Det betyr å tilbakestille til den opprinnelige trekanten, deaktivere tegning og tømme lerretet. Her er den:

function keyPressed() {
  ...
 
 
  // reset key
  if (key === 'r') {
    shouldDraw = false;
    toDraw = [defaultTri];
    background(0, 0, 100); 
  }
}

Lenke til p5.js-skisse

Sandmaleri

Jeg vil sitere Anders Hoff / Inconvergent-artikkelen, Grains of Sand, for å forklare konseptet:

I prinsippet virker det ved å tegne flere nesten gjennomsiktige prikker tilfeldig langs en linje. Dette ligner på å spre et fast antall sandkorn langs en linje—derav navnet. Hvis du alltid bruker det samme antall korn, vil dette få linjen til å se tett ut når punktene er nær hverandre. Og omvendt, mindre tett når de er lengre fra hverandre.

Jeg elsker å bruke dette som en "tegneteknikk", ser på det som et emulert digitalt medium.

Grunnleggende algoritme

Algoritmen er ganske enkel: gitt et antall korn, n, plasser dem tilfeldig langs linjen AB. For å starte, vil vi plassere kornene jevnt tilfeldig, og langs kantene av trekantene vi allerede tegner. Trikset er å holde linjene til en konsekvent tetthet, det vil si å legge til flere korn hvis linjen er lengre.

function paintLine(a, b) {
  const [x1, y1] = a;
  const [x2, y2] = b;
  const len = dist(x1, y1, x2, y2);
 
  // density might need to be tweaked to look pleasant   
  const density = 500;
  for (let i = 0; i < len * density; i++) {
 
    // random number [0, 1]
    const r = random();
 
    const xr = lerp(x1, x2, r);
    const yr = lerp(y1, y2, r);
    point(w(xr), h(yr))
  }
}

Lenke til p5.js-skisse

Domenevridning

For å gi den litt mer karakter, vil vi bruke en teknikk kalt domenevridning. Denne teknikken går i bunn og grunn ut på å kartlegge inngangene av kornposisjoneringen gjennom en støyfunksjon. Det er en flott artikkel av Inigo Quilez, domenevridning, som dekker konseptet godt.

function paintLine(a, b) {
  ...  
 
  const rez = 0.01
  for (let i = 0; i < len * density; i++) {
 
    // because `noise` is nonuniform and continuous,
    //  we get a really visually pleasing effect   
    const r = random();
    const n = noise(x1 * rez, y1 * rez, r);
 
    const xr = lerp(x1, x2, n);
    const yr = lerp(y1, y2, n);
 
    point(w(xr), h(yr))
  }
} 

Jeg bruker støyfunksjonen i P5js, som jeg ikke kommer til å dekke i dybden her. Det er mange andre artikler som dekker gradientstøy andre steder.

Ett lite problem med støyfunksjonen er utgangsområdet - den returnerer verdier fra [-1, 1] mens lerp tar i mot verdier fra [0, 1]. Dette kan løses på flere måter, men for nå skal jeg ganske enkelt skalere den opp litt og få den til å "slå inn". Dessverre har Javascript ingen innebygd funksjon for å gjøre dette, så vi må reimplementere fmod-funksjonen fra C.

function fmod(n, d) {
    return n - (Math.floor(/ d) * d);
}
 
function paintLine(a, b) {
  ...  
 
  const rez = 0.01
  for (let i = 0; i < len * density; i++) {
 
    const r = random();
    let n = noise(x1 * rez, y1 * rez, r, frameCount);
 
    // scale by 10 and only keep the fractional part
    n = fmod(* 10, 1.0);
 
    const xr = lerp(x1, x2, n);
    const yr = lerp(y1, y2, n);
 
    point(w(xr), h(yr))
  }
}

Interaksjoner og endring

Noen ytterligere idéer til leseren.

  • Trekk rez-variabelen ut til en global og bruk noen nye taster (f.eks. h, l) for å redusere og øke verdien, henholdsvis
  • Juster n = fmod(...) kallet. Kanskje ved å skalere inngangene litt.
  • legg til en tredje parameter til støyfunksjonens kall, kanskje basert på frameCount?

Lenke til p5.js-skisse

Avslutning

Skissen kommer virkelig godt sammen. Det er bare noen få små ting jeg trenger å legge til før det er en tro kopi av de opprinnelige verkene.

Farge

Jeg liker interaktiviteten til resten av skissen, så fargevalget vil også være interaktivt. Farger kan defineres som en liste av HSV-verdier og velges ved å trykke på nummertastene. En smart måte å håndtere tastetrykk på er å bruke keyCode igjen, og vite at tallene 0-9 på tastaturet har økende keyCode-verdier.

const colorPalette = [
    [47, 35, 92],
    [33, 100, 100],
    [276, 16, 80],
];
 
// default color is black
let currentColor = [0, 0, 0];         
 
function setup() {
  ... 
 
  // this was moved out of draw
  stroke(0, 0, 0, 0.05);
}
 
function keyPressed() {
  ...
 
  // "0"
  if (keyCode == 48) {
    stroke(0, 0, 0, 0.05)
  }
 
  // "1"-"9"
  if (keyCode >= 49 && keyCode <= 57) {
    const idx = min(keyCode - 49, colorPalette.length - 1);
    const currentColor = [...colorPalette[idx], 0.05];
    stroke.apply(null, currentColor);
  }
}

Det er verdt å merke seg - de faktiske fargeverdiene er ikke mine. Jeg stjal dem fra en bok jeg kjøpte for en stund siden, A Dictionary of Color Combinations.

Lenke til p5.js-skisse

Maskering

Maskering er et enkelt konsept. Jeg ønsker å isolere seksjoner av bildet fra tegning. En mulighet er å bruke flere p5.Graphics-renderere, som kan tenkes på som lag i GIMP/Photoshop/lignende bilderedigeringsprogramvare. Et annet alternativ er å utføre maskeringsarbeidet selv i tegnefunksjonen. Jeg valgte opprinnelig å gjøre det selv fordi i et høyoppløst trykk kan det lille "kornet" som plasseres under sandmaleri være synlig, og muligens strekke seg over mer enn en piksel. Jeg ønsker ikke å beskjære grafikken min, jeg ønsker å enkelt utelate å tegne et korn. Dessverre er dette ikke særlig effektivt.

For en enkel sirkel, beregne om et punkt er innenfor eller utenfor, gjør man ved å sørge for at avstanden mellom vårt punkt og sentrum av sirkelen er mindre enn radiusen til sirkelen. Her er koden:

function testInCircle(testPoint, circleCenter, circleRadius) {
    const [px, py] = testPoint;
    const [cx, cy] = circleCenter;
 
    return dist(px, py, cx, cy) < circleRadius;
}  

Nå ved å legge det til tegnefunksjonen lar det oss klippe den tegnede trekanten.

function paintLine(a, b) {
  ...
 
  // point in center with a diameter of 0.8
  if (testInCircle([xr, yr], [0.5, 0.5], 0.4)) {
    point(w(xr), h(yr)) 
  }
 
  ...
}

Det er måter å gjøre mer komplekse former på, og jeg vil henvise leseren til SDF-er, eller signerte avstandsfunksjoner. Her er en annen artikkel av Inigo Quilez som demonstrerer noen 2D SDF-eksempler med kode. Dessverre er en generell løsning på problemet med punkt-i-polygon ikke så enkelt, og utenfor omfanget av denne opplæringen.

Lenke til p5.js-skisse

Fin

Selv om det er mye mer som kan gjøres, må jeg avslutte. En ting jeg bevisst har utelatt er en liten hjelpefunksjon for å randomisere den startende trekanten. Jeg overlater dette til leseren, men jeg inkluderer det i min endelige skisse. Hvis du trenger et hint - jeg gjør det ved å plassere trekantkoordinatene langs en stor sirkel.

Lenke til slutt p5.js-skisse

Jeg vil etterlate et par ideer og lenker til ytterligere materiale av noen av mine favorittkunstnere og koder.

Å ta det videre

  • Liknende som det forrige punktet, prøv forskjellige former enn trekanter. Generell polygontriangulering, også kjent som tessellering, er et stort felt og et virkelig morsomt område å utforske.
  • Legg til flere fargepaletter. Bruk kontroltasten + et tall for å veksle mellom paletter.
  • Prøv andre SDF-er og kombiner dem ved bruk av boolske operasjoner
  • Prøv annen maskeringskode. For eksempel ved å sample et bitmap eller bilde
  • Ta domenevridning et skritt videre ved å bruke den gjeldende verdien av noise som inngang i en annen noise oppringning
  • Endre kornets sandform til en firkant eller trekant

Referanser

Andre kunstnere

Dette ... var en lang skrivelse. Hvis du finner unøyaktigheter eller har spørsmål, er du velkommen til å ta kontakt via e-post, Instagram eller Matrix. Du kan kontakte meg på stevanproductions@gmail.com

Igjen tusen takk til Creative Computing Hub Oslo for denne muligheten!

© Stevan Dedovic 2024, Noen rettigheter forbeholdt

Med mindre annet er angitt, er dette verket lisensiert under https://creativecommons.org/licenses/by/4.0/ 

Emneord: C2HO, Creative Computing, Creative Coding Av Stevan Dedovic
Publisert 13. mai 2024 10:07 - Sist endret 13. mai 2024 19:26