Как это называется и как этого добиться! Визуалы в обработке - PullRequest
0 голосов
/ 01 марта 2020

Эй, кто-нибудь знает, как добиться этого эффекта с помощью обработки или как это называется?

Я пытался использовать пример волнового градиента в библиотеке обработки и реализации шума Перлина, но я не могу приблизиться к качеству изображения.

Я знаю, что художник использовал обработку, но не могу понять, как!

waves animated gif

Ссылка на GIF:

https://giphy.com/gifs/processing-jodeus-QInYLzY33wMwM

1 Ответ

4 голосов
/ 10 апреля 2020

Эффект напоминает Op Art (оптическая иллюзия): я рекомендую прочитать / узнать больше об этом увлекательном жанре и таких артистах, как:

Бриджит Райли

Bridget Riley 1979

Bridget Riley, Intake, 1964

(Бриджит Райли, Intake, 1964)

Bridget Riley, Hesistate, 1964

( Бриджит Райли, Hesistate, 1964, Авторское право: (c) Бриджит Райли 2018. Все права защищены. / Фото (c) Тейт )

Виктор Вазарели

Zebra Couple

(Виктор Вазарели, Зебра Пара)

Victor Vasarely, VegaII

(Виктор Вазарели, VegaII)

Фрэнк Стелла Frank Stella, Untitled 1965 (Фрэнк Стелла, без названия 1965, Изображение предоставлено Art Gallery NSW )

и более

Вы замечаете, что эти волны напоминают / во многом вдохновлены работой Бриджит Райли.

I также рекомендуем проверить San Charoenchai ; визуализатор альбома для Beach House - 7

Beach House - 7 album music video thumbnails

Как уже упоминалось в моем комментарии: вы должны опубликовать свою попытку. Волны и шум перлина могут сработать наверняка. Есть много способов добиться подобного внешнего вида.

Вот измененная версия Пример "Шума волны Даниэля Шиффмана" :

int numWaves = 24;

float[] yoff = new float[numWaves];        // 2nd dimension of perlin noise
float[] yoffIncrements = new float[numWaves];

void setup() {
  size(640, 360);
  noStroke();

  for(int i = 0 ; i < numWaves; i++){
    yoffIncrements[i] = map(i, 0, numWaves - 1, 0.01, 0.03);
  }
}

void draw() {
  background(0);



  float waveHeight = height / numWaves;

  for(int i = 0 ; i < numWaves; i++){

    float waveY = i * waveHeight;

    fill(i % 2 == 0 ? color(255) : color(0));
    // We are going to draw a polygon out of the wave points
    beginShape(); 

    float xoff = 0;       // Option #1: 2D Noise
    // float xoff = yoff; // Option #2: 1D Noise

    // Iterate over horizontal pixels
    for (float x = 0; x <= width + 30; x += 20) {
      // Calculate a y value according to noise, map to 
      float y = map(noise(xoff, yoff[i]), 0, 1, waveY , waveY + (waveHeight * 3)); // Option #1: 2D Noise
      // float y = map(noise(xoff), 0, 1, 200,300);    // Option #2: 1D Noise

      // Set the vertex
      vertex(x, y); 
      // Increment x dimension for noise
      xoff += 0.05;
    }
    // increment y dimension for noise
    yoff[i] += yoffIncrements[i];
    vertex(width, height);
    vertex(0, height);
    endShape(CLOSE);
  }
}

Noise Wave modified Processing sketch

Обратите внимание на качество шумовой волны по сравнению с изображением, которое вы пытаетесь эмулировать: в нем есть постоянный ритм. Для меня это намек на то, что он использует циклические синусоидальные волны, меняющие фазу и амплитуду (возможно, даже складывающие волны вместе).

Я написал обширный ответ по анимации синусоидальных волн здесь Reuben Margolin's kinectic sculpture system demo (демоверсия системы скульптур Рувима Марголина c)

По вашему вопросу звучит так, как будто вам будет удобно реализовать анимацию синусоидальной волны. Это помогает, вот пример сложения двух волн:

void setup(){
  size(600,600);
  noStroke();
}

void draw(){
  background(0);
  // how many waves per sketch height
  int heightDivisions = 30;
  // split the sketch height into equal height sections
  float heightDivisionSize = (float)height / heightDivisions;
  // for each height division
  for(int j = 0 ; j < heightDivisions; j++){
    // use % 2 to alternate between black and white
    // see https://processing.org/reference/modulo.html and
    //     https://processing.org/reference/conditional.html for more
    fill(j % 2 == 0 ? color(255) : color(0));
    // offset drawing on Y axis
    translate(0,(j * heightDivisionSize));
    // start a wave shape
    beginShape();
    // first vertex is at the top left corner
    vertex(0,height);
    // how many horizontal (per wave) divisions ?
    int widthDivisions = 12;
    // equally space the points on the wave horizontally
    float widthDivsionSize = (float)width / widthDivisions;
    // for each point on the wave 
    for(int i = 0; i <= widthDivisions; i++){
      // calculate different phases
      // play with arithmetic operators to make interesting wave additions
      float phase1 = (frameCount * 0.01) + ((i * j) * 0.025);
      float phase2 = (frameCount * 0.05) + ((i + j) * 0.25);
      // calculate vertex x position
      float x = widthDivsionSize * i;
      // multiple sine waves
      // (can use cos() and use other ratios too
      // 150 in this case is the wave amplitude (e.g. from -150 to + 150)
      float y = ((sin(phase1) * sin(phase2) * 150));
      // draw calculated vertex
      vertex(x,y);
    }
    // last vertex is at bottom right corner
    vertex(width,height);
    // finish the shape
    endShape();
  }
}

Результат: Additive Waves shape

Небольшое замечание по производительности: это может быть реализовано больше эффективно использовать PShape , однако я рекомендую поиграть с математикой / геометрией, чтобы найти нужную форму, а в качестве последнего шага подумать об ее оптимизации.

Мое намерение не показывать Вы знаете, как создать точную копию, но показать, что в Op Art есть нечто большее, чем эффект, и, надеюсь, вдохновить вас на изучение других методов достижения чего-то похожего в надежде, что вы откроете для себя свои собственные методы и результаты: что-то новое и свое через веселые счастливые несчастные случаи.

С точки зрения других методов / возможностей для изучения:

  • карты смещения : Displacement maps SVG filter example
  • Использование чередующейся черно-белой текстуры прямых полос на волнистой трехмерной геометрии San Charoenchai Beach House 7 album video
  • с использованием шейдеров: shader toy hermite curve waves surface

Шейдеры огромные т opi c сами по себе, но стоит отметить:

  1. Очень хороший Обучающий курс по шейдерам
  2. Вы можете изучить шейдеры фрагментов на shadertoy , настройте код в браузере, затем внесите небольшие изменения, чтобы вы могли запустить их в обработке.

Вот несколько быстрых примеров:

https://www.shadertoy.com/view/Wts3DB

настроен для черно-белых волн в Обработка как шейдер-Wts3DB.frag

// https://www.shadertoy.com/view/Wts3DB

uniform vec2 iResolution;
uniform float iTime;

#define COUNT 6.
#define COL_BLACK vec3(23,32,38) / 255.0 

#define SF 1./min(iResolution.x,iResolution.y)
#define SS(l,s) smoothstep(SF,-SF,l-s)
#define hue(h) clamp( abs( fract(h + vec4(3,2,1,0)/3.) * 6. - 3.) -1. , 0., 1.)

// Original noise code from https://www.shadertoy.com/view/4sc3z2
#define MOD3 vec3(.1031,.11369,.13787)

vec3 hash33(vec3 p3)
{
    p3 = fract(p3 * MOD3);
    p3 += dot(p3, p3.yxz+19.19);
    return -1.0 + 2.0 * fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
}

float simplex_noise(vec3 p)
{
    const float K1 = 0.333333333;
    const float K2 = 0.166666667;

    vec3 i = floor(p + (p.x + p.y + p.z) * K1);
    vec3 d0 = p - (i - (i.x + i.y + i.z) * K2);

    vec3 e = step(vec3(0.0), d0 - d0.yzx);
    vec3 i1 = e * (1.0 - e.zxy);
    vec3 i2 = 1.0 - e.zxy * (1.0 - e);

    vec3 d1 = d0 - (i1 - 1.0 * K2);
    vec3 d2 = d0 - (i2 - 2.0 * K2);
    vec3 d3 = d0 - (1.0 - 3.0 * K2);

    vec4 h = max(0.6 - vec4(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), 0.0);
    vec4 n = h * h * h * h * vec4(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.0)));

    return dot(vec4(31.316), n);
}

void mainImage( vec4 fragColor, vec2 fragCoord )
{


}

void main(void) {
    //vec2 uv = vec2(gl_FragColor.x / iResolution.y, gl_FragColor.y / iResolution.y);
    vec2 uv = gl_FragCoord.xy / iResolution.y;
    float m = 0.;
    float t = iTime *.5;
    vec3 col;
    for(float i=COUNT; i>=0.; i-=1.){
        float edge = simplex_noise(vec3(uv * vec2(2., 0.) + vec2(0, t + i*.15), 3.))*.2 + (.95/COUNT)*i;
        float mi = SS(edge, uv.y) - SS(edge + .095, uv.y);        
        m += mi;        

        if(mi > 0.){
            col = vec3(1.0);
        }
    }           

    col = mix(COL_BLACK, col, m);

    gl_FragColor = vec4(col,1.0);

  // mainImage(gl_FragColor,gl_FragCoord);
}

загружен в Обработка как:

PShader shader;

void setup(){
  size(300,300,P2D);
  noStroke();
  shader = loadShader("shader-Wts3DB.frag");
  shader.set("iResolution",(float)width, float(height));
}

void draw(){
  background(0);
  shader.set("iTime",frameCount * 0.05);
  shader(shader);
  rect(0,0,width,height);
}

shadertoy Processing shader example 1

https://www.shadertoy.com/view/MtsXzl

настроены как шейдер-MtsXzl.frag

//https://www.shadertoy.com/view/MtsXzl
#define SHOW_GRID 1

const float c_scale = 0.5;
const float c_rate = 2.0;

#define FLT_MAX 3.402823466e+38

uniform vec3 iMouse;
uniform vec2 iResolution;
uniform float iTime;

//=======================================================================================
float CubicHermite (float A, float B, float C, float D, float t)
{
    float t2 = t*t;
    float t3 = t*t*t;
    float a = -A/2.0 + (3.0*B)/2.0 - (3.0*C)/2.0 + D/2.0;
    float b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0;
    float c = -A/2.0 + C/2.0;
    float d = B;

    return a*t3 + b*t2 + c*t + d;
}

//=======================================================================================
float hash(float n) {
    return fract(sin(n) * 43758.5453123);
}

//=======================================================================================
float GetHeightAtTile(vec2 T)
{
    float rate = hash(hash(T.x) * hash(T.y))*0.5+0.5;

    return (sin(iTime*rate*c_rate) * 0.5 + 0.5) * c_scale;
}

//=======================================================================================
float HeightAtPos(vec2 P)
{
    vec2 tile = floor(P);

    P = fract(P);

    float CP0X = CubicHermite(
        GetHeightAtTile(tile + vec2(-1.0,-1.0)),
        GetHeightAtTile(tile + vec2(-1.0, 0.0)),
        GetHeightAtTile(tile + vec2(-1.0, 1.0)),
        GetHeightAtTile(tile + vec2(-1.0, 2.0)),
        P.y
    );

    float CP1X = CubicHermite(
        GetHeightAtTile(tile + vec2( 0.0,-1.0)),
        GetHeightAtTile(tile + vec2( 0.0, 0.0)),
        GetHeightAtTile(tile + vec2( 0.0, 1.0)),
        GetHeightAtTile(tile + vec2( 0.0, 2.0)),
        P.y
    );    

    float CP2X = CubicHermite(
        GetHeightAtTile(tile + vec2( 1.0,-1.0)),
        GetHeightAtTile(tile + vec2( 1.0, 0.0)),
        GetHeightAtTile(tile + vec2( 1.0, 1.0)),
        GetHeightAtTile(tile + vec2( 1.0, 2.0)),
        P.y
    );        

    float CP3X = CubicHermite(
        GetHeightAtTile(tile + vec2( 2.0,-1.0)),
        GetHeightAtTile(tile + vec2( 2.0, 0.0)),
        GetHeightAtTile(tile + vec2( 2.0, 1.0)),
        GetHeightAtTile(tile + vec2( 2.0, 2.0)),
        P.y
    );

    return CubicHermite(CP0X, CP1X, CP2X, CP3X, P.x);
}

//=======================================================================================
vec3 NormalAtPos( vec2 p )
{
    float eps = 0.01;
    vec3 n = vec3( HeightAtPos(vec2(p.x-eps,p.y)) - HeightAtPos(vec2(p.x+eps,p.y)),
                         2.0*eps,
                         HeightAtPos(vec2(p.x,p.y-eps)) - HeightAtPos(vec2(p.x,p.y+eps)));
    return normalize( n );
}

//=======================================================================================
float RayIntersectSphere (vec4 sphere, in vec3 rayPos, in vec3 rayDir)
{
    //get the vector from the center of this circle to where the ray begins.
    vec3 m = rayPos - sphere.xyz;

    //get the dot product of the above vector and the ray's vector
    float b = dot(m, rayDir);

    float c = dot(m, m) - sphere.w * sphere.w;

    //exit if r's origin outside s (c > 0) and r pointing away from s (b > 0)
    if(c > 0.0 && b > 0.0)
        return -1.0;

    //calculate discriminant
    float discr = b * b - c;

    //a negative discriminant corresponds to ray missing sphere
    if(discr < 0.0)
        return -1.0;

    //ray now found to intersect sphere, compute smallest t value of intersection
    float collisionTime = -b - sqrt(discr);

    //if t is negative, ray started inside sphere so clamp t to zero and remember that we hit from the inside
    if(collisionTime < 0.0)
        collisionTime = -b + sqrt(discr);

    return collisionTime;
}

//=======================================================================================
vec3 DiffuseColor (in vec3 pos)
{
    #if SHOW_GRID
    pos = mod(floor(pos),2.0);
    return vec3(mod(pos.x, 2.0) < 1.0 ? 1.0 : 0.0);
    #else
    return vec3(0.1, 0.8, 0.9);
    #endif
}

//=======================================================================================
vec3 ShadePoint (in vec3 pos, in vec3 rayDir, float time, bool fromUnderneath)
{
    vec3 diffuseColor = DiffuseColor(pos);
    vec3 reverseLightDir = normalize(vec3(1.0,1.0,-1.0));
    vec3 lightColor = vec3(1.0);    
    vec3 ambientColor = vec3(0.05);

    vec3 normal = NormalAtPos(pos.xz);
    normal *= fromUnderneath ? -1.0 : 1.0;

    // diffuse
    vec3 color = diffuseColor;
    float dp = dot(normal, reverseLightDir);
    if(dp > 0.0)
        color += (diffuseColor * lightColor);

    return color;
}

//=======================================================================================
vec3 HandleRay (in vec3 rayPos, in vec3 rayDir, in vec3 pixelColor, out float hitTime)
{
    float time = 0.0;
    float lastHeight = 0.0;
    float lastY = 0.0;
    float height;
    bool hitFound = false;
    hitTime = FLT_MAX;
    bool fromUnderneath = false;

    vec2 timeMinMax = vec2(0.0, 20.0);

    time = timeMinMax.x;

    const int c_numIters = 100;
    float deltaT = (timeMinMax.y - timeMinMax.x) / float(c_numIters);

    vec3 pos = rayPos + rayDir * time;
    float firstSign = sign(pos.y - HeightAtPos(pos.xz));

    for (int index = 0; index < c_numIters; ++index)
    {       
        pos = rayPos + rayDir * time;

        height = HeightAtPos(pos.xz);

        if (sign(pos.y - height) * firstSign < 0.0)
        {
            fromUnderneath = firstSign < 0.0; 
            hitFound = true;
            break;
        }

        time += deltaT;     
        lastHeight = height;
        lastY = pos.y;
    }


    if (hitFound) {
        time = time - deltaT + deltaT*(lastHeight-lastY)/(pos.y-lastY-height+lastHeight);
        pos = rayPos + rayDir * time;
        pixelColor = ShadePoint(pos, rayDir, time, fromUnderneath);
        hitTime = time;
    }

    return pixelColor;
}

//=======================================================================================
void main()
{      
    // scrolling camera
    vec3 cameraOffset = vec3(iTime, 0.5, iTime);

    //----- camera
    vec2 mouse = iMouse.xy / iResolution.xy;

    vec3 cameraAt   = vec3(0.5,0.5,0.5) + cameraOffset;

    float angleX = iMouse.z > 0.0 ? 6.28 * mouse.x : 3.14 + iTime * 0.25;
    float angleY = iMouse.z > 0.0 ? (mouse.y * 6.28) - 0.4 : 0.5;
    vec3 cameraPos  = (vec3(sin(angleX)*cos(angleY), sin(angleY), cos(angleX)*cos(angleY))) * 5.0;

 //    float angleX = 0.8;
 //    float angleY = 0.8;
 //    vec3 cameraPos = vec3(0.0,0.0,0.0);
    cameraPos += vec3(0.5,0.5,0.5) + cameraOffset;

    vec3 cameraFwd  = normalize(cameraAt - cameraPos);
    vec3 cameraLeft  = normalize(cross(normalize(cameraAt - cameraPos), vec3(0.0,sign(cos(angleY)),0.0)));
    vec3 cameraUp   = normalize(cross(cameraLeft, cameraFwd));

    float cameraViewWidth   = 6.0;
    float cameraViewHeight  = cameraViewWidth * iResolution.y / iResolution.x;
    float cameraDistance    = 6.0;  // intuitively backwards!


    // Objects
    vec2 rawPercent = (gl_FragCoord.xy / iResolution.xy);
    vec2 percent = rawPercent - vec2(0.5,0.5);

    vec3 rayTarget = (cameraFwd * vec3(cameraDistance,cameraDistance,cameraDistance))
                   - (cameraLeft * percent.x * cameraViewWidth)
                   + (cameraUp * percent.y * cameraViewHeight);
    vec3 rayDir = normalize(rayTarget);


    float hitTime = FLT_MAX;
    vec3 pixelColor = vec3(1.0, 1.0, 1.0);
    pixelColor = HandleRay(cameraPos, rayDir, pixelColor, hitTime);


    gl_FragColor = vec4(clamp(pixelColor,0.0,1.0), 1.0);
}

и мышь, интерактивная обработка Эскиз:

PShader shader;

void setup(){
  size(300,300,P2D);
  noStroke();
  shader = loadShader("shader-MtsXzl.frag");
  shader.set("iResolution",(float)width, float(height));
}

void draw(){
  background(0);
  shader.set("iTime",frameCount * 0.05);
  shader.set("iMouse",(float)mouseX , (float)mouseY, mousePressed ? 1.0 : 0.0);
  shader(shader);
  rect(0,0,width,height);
}

shadertoy Processing shader example 1

Shadertoy - отличный способ играть / учиться: веселиться!

Обновление Вот быстрая настройка теста Пример 3D поколения Terrain Generation Даниэля Шиффмана для добавления раздетой текстуры и базовых c синусоид вместо шумов перлина:

waves motion surface with stripped texture * 1 185 *

// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/IKB1hWWedMk

int cols, rows;
int scl = 20;
int w = 2000;
int h = 1600;

float flying = 0;

float[][] terrain;

PImage texture;

void setup() {
  size(600, 600, P3D);
  textureMode(NORMAL);
  noStroke();

  cols = w / scl;
  rows = h/ scl;
  terrain = new float[cols][rows];

  texture = getBarsTexture(512,512,96);
}


void draw() {

  flying -= 0.1;

  float yoff = flying;
  for (int y = 0; y < rows; y++) {
    float xoff = 0;
    for (int x = 0; x < cols; x++) {
      //terrain[x][y] = map(noise(xoff, yoff), 0, 1, -100, 100);
      terrain[x][y] = map(sin(xoff) * sin(yoff), 0, 1, -60, 60);
      xoff += 0.2;
    }
    yoff += 0.2;
  }



  background(0);

  translate(width/2, height/2+50);
  rotateX(PI/9);
  translate(-w/2, -h/2);
  for (int y = 0; y < rows-1; y++) {
    beginShape(TRIANGLE_STRIP);
    texture(texture);
    for (int x = 0; x < cols; x++) {

      float u0 = map(x,0,cols-1,0.0,1.0);
      float u1 = map(x+1,0,cols-1,0.0,1.0);
      float v0 = map(y,0,rows-1,0.0,1.0);
      float v1 = map(y+1,0,rows-1,0.0,1.0);

      vertex(x*scl, y*scl, terrain[x][y], u0, v0);
      vertex(x*scl, (y+1)*scl, terrain[x][y+1], u1, v1);
    }
    endShape();
  }

}

PGraphics getBarsTexture(int textureWidth, int textureHeight, int numBars){
  PGraphics texture = createGraphics(textureWidth, textureHeight);
  int moduleSide = textureWidth / numBars;

  texture.beginDraw();
  texture.background(0);
  texture.noStroke();
  for(int i = 0; i < numBars; i+= 2){
    texture.rect(0, i * moduleSide, textureWidth, moduleSide);
  }
  texture.endDraw();

  return texture;
}
...