Every Breath You Take.

The 3D modeling eyeball made with a Gray Scott model.
The 3D modeling eyeball made with a Gray Scott model.
The 3D modeling eyeball made with a Gray Scott model.


Description of this creative coding.

Creative coding works with the reaction-diffusion.

The 3D modeling eyeball is watching you!
Why are you in fear and trembling?
It has been watching over you with a warm heart. ;)

This uneven surface is made with a Gray Scott model.

This code does not display any images on the screen but generates image files in frames directory.
You can make an animation with these files.

Processing example code.

Please feel free to use this example code, if you like it.
To see other works based on my code is my pleasure. And my honor.





// Every Breath You Take.
// @author @deconbatch
// @version 0.1
// Processing 3.2.1
// 2018.10.06

void setup() {
  size(720, 720, P3D);
  colorMode(HSB, 360, 100, 100, 100);
  smooth(8);
  sphereDetail(30);
  noLoop();
  noStroke();
}

void draw() {

  int   frameCntMax = 15 * 6;
  float hueBase     = random(360.0);
  Ecosystem eco     = new Ecosystem(frameCntMax, hueBase);

  translate(0.0, 0.0, 0.0);
  ambientLight(0.0, 0.0, 15.0);
  lightFalloff(1.0, 0.001, 0.0);
  pointLight(0.0, 0.0, 100.0, 100.0, 100.0, 500.0);

  for (int frameCnt = 0; frameCnt < frameCntMax; ++frameCnt) {
    float frameIdx = map(frameCnt, 0, frameCntMax, 6.0, -6.0);
    float sigmoid  = ((1.0 / (1.0 + exp(frameIdx))));

    camera(map(sin(TWO_PI * sigmoid), 0.0, 1.0, 0.0, 200.0),
           map(sin(PI * sigmoid), 0.0, 1.0, 0.0, -300.0),
           1600,
           0, 0, 0,
           1, 0, 0);

    blendMode(BLEND);
    background(0.0, 0.0, 0.0, 100.0);
    backdrop(hueBase);
    eco.setTime(frameCnt);
    eco.initSchale();
    eco.plant();
    eco.culture();
    eco.observe();

    blendMode(SCREEN);
    pupil(hueBase, sigmoid);
    
    saveFrame("frames/" + String.format("%04d", frameCnt) + ".png");
  }

  exit();

}

private void backdrop(float hueBase) {
  pushMatrix();
  rotateZ(radians(15));
  translate(0, 0, -800);
  for (int i = 0; i < 20; ++i) {
    fill(
         hueBase,
         60,
         map(i, 0, 20, 40.0, 0.0),
         100
         );
    rect(
         -2500.0,
         map(i, 0, 20, -1800.0, 2500.0),
         5000.0,
         500.0
         );
  }
  popMatrix();
}

private void pupil(float hueBase, float sigmoid) {
  pushMatrix();
  translate(0.0, 0.0, 500.0);
  for (float i = 0.0; i < 100.0; i += 10.0) {
    translate(-3.0, 0.0, 0.0);
    fill(
         (hueBase + i * 0.6) % 360.0,
         40.0,
         map(i, 0.0, 100.0, 20.0, 0.0),
         100.0
         );
    sphere((10.0 + i * 2.0) * pow((sigmoid - 0.5) * 2.0, 4));
  }
  popMatrix();
}

/*  -------------------- */
public class Ecosystem {

  private Cell[][]    cells;
  private float[][][] initVs;
  private float hueBase;
  private float cellSize;
  private int   canvasW;
  private int   canvasH;
  private int   frameCntMax;
  private int   frameCnt;
  private int   cultureTimeMax;
  
  Ecosystem(int frameCntMax, float hueBase) {

    this.frameCntMax = frameCntMax;
    this.hueBase     = hueBase;

    frameCnt       = 0;
    cultureTimeMax = 0;
    cellSize       = 8.0;

    canvasW  = floor(width * 2.0 / cellSize);
    canvasH  = floor(height * 2.0 / cellSize);
    
    cells    = new Cell[canvasW][canvasH];
    initVs   = new float[2][canvasW][canvasH];

    // constract cells
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        cells[x][y] = new Cell();
        initVs[0][x][y] = random(0.5, 1.0);
        initVs[1][x][y] = random(0.0, 0.5);
        cells[x][y].setU(initVs[0][x][y]);
        cells[x][y].setV(initVs[1][x][y]);
      }
    }
    
    // set neighbor cells
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        cells[x][y].setNeighbor(new Cell[] {
            // 8 directions
            cells[max(x-1,0)][max(y-1,0)],
            cells[min(x+1,canvasW-1)][max(y-1,0)],
            cells[max(x-1,0)][min(y+1,canvasH-1)],
            cells[min(x+1,canvasW-1)][min(y+1,canvasH-1)],
            cells[x][max(y-1,0)],
            cells[x][min(y+1,canvasH-1)],
            cells[max(x-1,0)][y],
            cells[min(x+1,canvasW-1)][y]
          });
      }
    }
  }

  public void setTime(int frameCnt) {
    this.frameCnt = frameCnt;
  }

  public void initSchale() {
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        cells[x][y].setU(initVs[0][x][y]);
        cells[x][y].setV(initVs[1][x][y]);
      }
    }
  }
  
  public void plant() {
    float frameIdx = map(frameCnt, 0, frameCntMax, 0.0, -6.0);
    float sigmoid = ((1.0 / (1.0 + exp(frameIdx))));
    float easeRate = sin(PI * sigmoid);
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        cells[x][y].setFeed(map(noise(x * 0.1 + easeRate * 0.08, y * 0.2 - easeRate * 0.08),
                                0.0,
                                1.0,
                                0.05,
                                0.10
                                ));
        cells[x][y].setKill(map(noise(x * 0.2 - easeRate * 0.08, y * 0.1 + easeRate * 0.08),
                                0.0,
                                1.0,
                                0.056,
                                0.0665
                                ));
      }
    }
    cultureTimeMax = floor(
                           40
                           + 1500 * pow(
                                        1.0 - abs(cos(PI * map(frameCnt, 0, frameCntMax, 0.0, 1.0))),
                                        2)
                           );
  }

  public void culture() {
    for (int cultureTime = 0; cultureTime < cultureTimeMax; cultureTime++) {
      for (int x = 0; x < canvasW; x++) {
        for (int y = 0; y < canvasH; y++) {
          cells[x][y].laplacian();
        }
      }
      for (int x = 0; x < canvasW; x++) {
        for (int y = 0; y < canvasH; y++) {
          cells[x][y].react();
        }
      }
    }
  }
  
  public void observe() {
    DrawCell drawCellU = new DrawCellU();
    DrawCell drawCellV = new DrawCellV();
    
    float radiusEllipse = 700.0;

    float frameIdx = sin(PI * map(frameCnt, 0, frameCntMax, 0.0, 1.0));
    float idoStart = max(0.25, frameIdx);
    float divIdo = 0.8;

    // outer skin
    for (float ido = 32 - idoStart * 8.0; ido <= 140; ido += divIdo) { // Y
      biotissue(
                radiusEllipse - frameIdx * cellSize,
                ido,
                divIdo,
                drawCellU
                );
    }
    // inner skin
    for (float ido = 10 + idoStart * 18.0; ido <= 120; ido += divIdo) { // Y
      biotissue(
                radiusEllipse + frameIdx * cellSize,
                ido,
                divIdo,
                drawCellV
                );
    }
  }

  private void biotissue(float radiusEllipse, float ido, float divIdo, DrawCell drawCell) {
    float radianIdo = radians(ido);
    float divKdo = 180 / max(160 / divIdo * sin(radianIdo), 1.0);
    for (float kdo = 0; kdo < 360; kdo += divKdo) { // Z
      float radianKdo = radians(kdo);
      float locateX   = radiusEllipse * cos(radianKdo) * sin(radianIdo);
      float locateY   = radiusEllipse * sin(radianKdo) * sin(radianIdo);
      float locateZ   = radiusEllipse * cos(radianIdo);

      pushMatrix();
      translate(locateX, locateY, locateZ);
      rotateZ(radianKdo); // must be this order Z -> Y
      rotateY(radianIdo);
      // not real 3D but 3D sphere -> 2D flat square mapping
      int cellX = floor((radiusEllipse + locateX) / cellSize);
      int cellY = floor((radiusEllipse + locateY) / cellSize);
      drawCell.withSphere(hueBase, cells[cellX][cellY]);
      popMatrix();
    }
  }

  // to use polymorphism in drawing cells
  private abstract class DrawCell {
    abstract void withSphere(float hueBase, Cell cells);
  }
  
  private class DrawCellU extends DrawCell {
    DrawCellU() {
      super();
    }
    public void withSphere(float hueBase, Cell cells) {
      float sizeU = (cells.getStandardU() * 10.0) * cellSize;
      float hueApply = (30.0 + hueBase + cells.getStandardU() * 60.0) % 360.0;
      fill(
           hueApply,
           20.0,
           100.0,
           100.0
           );
      sphere(sizeU);
    }
  }
  
  private class DrawCellV extends DrawCell {
    DrawCellV() {
      super();
    }
    public void withSphere(float hueBase, Cell cells) {
      float sizeV = (cells.getStandardV() * 6.0) * cellSize;
      float hueApply = (hueBase + cells.getStandardV() * 60.0) % 360.0;
      fill(
           hueApply,
           30.0,
           70.0,
           100.0
           );
      sphere(sizeV);
    }
  }

}

/* Gray-Scott model -------------------- */
public class Cell {
  private float difU  = 0.001;
  private float difV  = 0.000238;
  private float dt    = 1.0;
  private float dxPow = pow(0.085, 2);
  private float feed  = 0.0;
  private float kill  = 0.0;
  
  private float valU;
  private float valV;
  private float lapU;
  private float lapV;

  private Cell[] neighbor;

  Cell() {
    valU = 0.0;
    valV = 0.0;
    resetLaplacian();
  }

  private void resetLaplacian() {
    lapU = 0.0;
    lapV = 0.0;
  }

  public void setFeed(float init) {
    feed = init;
  }

  public void setKill(float init) {
    kill = init;
  }

  public void setV(float init) {
    valV = init;
  }

  public void setU(float init) {
    valU = init;
  }

  public void setNeighbor(Cell[] pNeighbor) {
    neighbor = new Cell[pNeighbor.length];
    for (int i = 0; i < pNeighbor.length; ++i) {
      neighbor[i] = pNeighbor[i];
    }
  }

  public float getRawU() {
    return valU;
  }

  public float getRawV() {
    return valV;
  }

  public float getStandardU() {
    return constrain(valU, 0.0, 1.0);
  }

  public float getStandardV() {
    return constrain(valV, 0.0, 1.0);
  }

  public void laplacian() {
    float sumU = 0.0;
    float sumV = 0.0;
    for (int i = 0; i < neighbor.length; ++i) {
      sumU += neighbor[i].getRawU();
      sumV += neighbor[i].getRawV();
    }
    lapU = (sumU - valU * neighbor.length) / dxPow;
    lapV = (sumV - valV * neighbor.length) / dxPow;
  }

  public void react() {
    float reaction = valU * valV * valV;
    float inflow   = feed * (1.0 - valU);
    float outflow  = (feed + kill) * valV;
    valU += dt * (difU * lapU - reaction + inflow);
    valV += dt * (difV * lapV + reaction - outflow);
    resetLaplacian();
  }
}

/*
Copyright (C) 2018- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see 
*/




No comments :

Post a Comment