Abnormal Pixels at the edges of Jump Flood Algorithm voronoi graph.

edited December 2017 in GLSL / Shaders

Glitched Pixels

I'm getting glitched pixels at the edges of the cells that are generated using a JFA shader that I wrote, I don't know what's causing it.

the code is on here github.com/Rupertofly/Vor_Flower

Answers

  • edited December 2017

    Hello can you post a very minimal, strip down version of you problem. Then is more likely someone jump into this, and spend his time debugging. Currently i have to download the project (not that bad) Clasess/classes.pde don't working for any reason, i have to copy past from github. I have to install toxi lib, ani lib. size(1920,1080,OPENGL); OPENGL is deprecated starting with Processing 3.x called P3D etc. all that sum up... i can't even see the output like the picture you posted, so i already debuging you code, and less your shader problem.

  • So, now after playing around with it.
    It looks like you don't blend (erase) the backgrounds properly. Take a look.

    img

  • here is the code running on the CPU instead of GPU and it doesn't have the problem

    //Pgraphics objects for storing images
    PGraphics bMain;
    PGraphics bSource;
    PGraphics bPallete;
    
    void settings() {
      //Settings
      size(500,500,P3D);
      noSmooth();
    }
    
    void setup() {
      //setup Graphics buffers
      bMain = createGraphics(width,height,P3D);
      bMain.noSmooth();
      bSource = createGraphics(width,height,P3D);
      bSource.noSmooth();
      bPallete = createGraphics(width,height,P3D);
      bPallete.noSmooth();
    }
    
    void draw() {
      //draw loop
      background(51);
      image(bMain,0,0);
    }
    
    
    void jfa() {
      //Jfa setup
      bMain.beginDraw();
      bMain.clear();
      bMain.image(bSource,0,0);
      bMain.loadPixels();
      //calculate required steps
      int steps = floor(log2(width));
    
      for (int indexPos = 0; indexPos < steps; ++indexPos) {
        //step setup
        float stepJump = pow(2, steps-indexPos -1);
        for (int i = 0; i < bMain.pixels.length; ++i) {
          //get pixels position and reset distance
          PVector thisPos = new PVector(i%width,floor(i/width));
          float minDist = 1000.0;
    
          for (int xState = -1; xState < 2; xState++) {
            for (int yState = -1; yState < 2; yState++) {
              //for each jump around the pixel get it's positon
              PVector samPos = new PVector(thisPos.x+(xState*stepJump),thisPos.y+(yState*stepJump));
              //check whether it's in the image
              if (samPos.x < 0 || samPos.x > width-1) continue;
              if (samPos.y < 0 || samPos.y > height-1) continue;
              //get sample pixel
              int samIndex = floor((samPos.y*width)+samPos.x);
              color samPxl = bMain.pixels[samIndex];
              if (alpha(samPxl) < 5) continue; // check whether pixel is opaque
              PVector samSeed = myDecode(samPxl >> 16 & 0xFF,samPxl >> 8 & 0xFF,samPxl & 0xFF); //get seed position of pixel
              float thisDist = thisPos.dist(samSeed); //get distance from this pixel to seed
              if (thisDist < minDist) {
                minDist = thisDist;
                bMain.pixels[i] = samPxl; //if seed distance is less then min distance for the seed, set this pixel to that seed
              }
            }
          }
        }
      }
      bMain.updatePixels();
      bMain.endDraw();
    }
    
    float frac(float _i) {
      return _i-floor(_i);
    }
    
    color encodepos(float _x, float _y) {
      float v1 = _x/256;
      float v1a = frac(v1)*256;
      float v1b = floor(v1);
      float v2 = _y/256;
      float v2a = frac(v2)*256;
      float v2b = floor(v2);
      float v3 = (v1b*16)+v2b;
      return color(v1a,v2a,v3,255);
    }
    
    PVector myDecode(int _r, int _g, int _b) {
      float partA = floor(_b/16.0)*256.0;
      float partB = (frac(_b/16.0)*16.0)*256.0;
      return new PVector(partA+_r,partB+_g);
    }
    
    float log2 (int x) {
      return (log(x) / log(2));
    }
    void mousePressed() {
      //add a point to the source buffer, and to the pallete buffer
      bSource.beginDraw();
      bSource.loadPixels();
      bSource.pixels[(mouseY*width)+mouseX] = encodepos(mouseX,mouseY);
      bSource.updatePixels();
      bSource.endDraw();
      bPallete.beginDraw();
      bPallete.loadPixels();
      bPallete.pixels[(mouseY*width)+mouseX] = randColor();
      bPallete.updatePixels();
      bPallete.endDraw();
      jfa();
      processColor();
    }
    int randColor() {
      return color(random(255),random(255),random(255));
    }
    void processColor() {
      //set all pixels in the main image to what they should be from the pallete
      bMain.beginDraw();
      bMain.loadPixels();
      bPallete.loadPixels();
      for (int i = 0; i < bMain.pixels.length; ++i) {
        int samPxl = bMain.pixels[i];
        PVector co = myDecode(samPxl >> 16 & 0xFF,samPxl >> 8 & 0xFF,samPxl & 0xFF);
        bMain.pixels[i] = bPallete.pixels[floor(co.y)*width+floor(co.x)];
      }
      bMain.updatePixels();
      bPallete.updatePixels();
      bMain.endDraw();
    }
    

    however when I use a shader to run the same code in parallel I get the issue, is it something to do with smoothing happening even though I used nosmooth?

  • edited December 2017

    Yes i understand, but your "bug" / problem, happens very early in your code somewhere. Please run a very minimal example of your code, you see then you don't clear the initial rectangle properly (*see below). And already at this stage, you can see some strange lines.

    PGraphics bPallete;
    PGraphics bSource;
    PGraphics bFront;
    PGraphics bBack;
    PGraphics bFinal;
    
    PGraphics bXScan;
    PGraphics bYScan;
    PShader sJFA;
    PShader sFill;
    float cellCount = 0.0;
    ArrayList<FPoint> aPointSet = new ArrayList<FPoint>();
    void setup() {
      size(500, 500, P3D);
      noSmooth();
      // noStroke();
      // noFill() ;
      //aPointSet.add(new FPoint(width/2,height/2,rC(),0));
      sJFA = loadShader("https://"+"raw.githubusercontent.com/Rupertofly/Vor_Flower/master/jfa.glsl");
      sFill = loadShader("https://"+"raw.githubusercontent.com/Rupertofly/Vor_Flower/master/prep.glsl");
      bSource = createGraphics(width, height, P3D);
      // bSource.noSmooth();
      // bSource.beginDraw();
      // bSource.clear();
      // bSource.endDraw();
      bFront = createGraphics(width, height, P3D);
      // bFront.noSmooth();
      bBack = createGraphics(width, height, P3D);
      // bBack.noSmooth();
      bFinal = createGraphics(width, height, P3D);
      // bFinal.noSmooth();
      bPallete = createGraphics(width, height, P3D);
      // bBack.noSmooth();
      frameRate(2);
    }
    
    void draw() {
      redrawsource();
      processFinal();
      // resetShader();
      // noStroke();
      // noFill();
      background(200, 0, 0);
      image(bFinal, 0, 0);
      // image(bPallete,0,0);
      // image(bPallete,0,0);
      // println("frameRate: "+frameRate);
    }
    void processFinal() {
      sFill.set("iResolution", (float)width, (float)height);
      sFill.set("texIn", bFront);
      sFill.set("pIn", bPallete);
      bFinal.beginDraw();
      //bFinal.clear();
      bFinal.shader(sFill);
      //bFinal.noStroke();
      //bFinal.fill(256);
      bFinal.rect(0, 0, width, height);
      // bFinal.resetShader();
      bFinal.endDraw();
    }
    
    
    void mouseClicked() {
      aPointSet.add(new FPoint(mouseX, mouseY, rC(), aPointSet.size()));
      cellCount ++;
      // redrawsource();
      println("cellCount: "+cellCount);
      // bSource.save("source.png");
      // bFinal.save("final.png");
    }
    
    
    void test() {
      bSource.beginDraw();
      // bSource.resetShader();
      // bSource.background(0);
      bSource.loadPixels();
      for (int i = 0; i < bSource.pixels.length; i++) {
        int thisX = i%width;
        int thisY = floor(i/width);
        color thisC = encodepos(thisX, thisY);
        bSource.pixels[i] = thisC;
      }
      bSource.updatePixels();
      bSource.endDraw();
      // bBack.beginDraw();
      bBack.image(bSource, 0, 0);
      // bBack.endDraw();
      sJFA.set("iResolution", (float)width, (float)height);
      sJFA.set("texIn", bBack);
      // bFront.beginDraw();
      // bFront.clear();
      // bFront.noStroke();
      // bFront.fill(255);
      bFront.shader(sJFA);
      bFront.rect(0, 0, width, height);
      // bFront.resetShader();
      // bFront.endDraw();
    }
    void redrawsource() {
      bSource.beginDraw();
      // bSource.resetShader();
      // bSource.background(0,0);
      bSource.loadPixels();
      for (int i = 0; i < aPointSet.size(); ++i) {
        FPoint c = aPointSet.get(i);
        color fill = encodepos(c.x, c.y);
        bSource.pixels[floor((c.y*width)+c.x)] = fill;
      }
      bSource.updatePixels();
      bSource.endDraw();
      bPallete.beginDraw();
      // bPallete.clear();
      for (int i = 0; i < aPointSet.size(); ++i) {
        FPoint l = aPointSet.get(i);
        //   bPallete.noStroke();
        //  bPallete.fill(l.c);
        bPallete.ellipse((int)l.x, (int)l.y, 3, 3);
      }
      bPallete.endDraw();
      jfa();
    }
    public class FPoint {
      public float x, y;
      public int c;
      public int ind;
      FPoint(float _x, float _y, int _c, int _ind) {
        x = _x;
        y = _y;
        c = _c;
        ind = _ind;
      }
    }
    float frac(float _i) {
      return _i-floor(_i);
    }
    color encodepos(float _x, float _y) {
      float v1 = _x/256;
      float v1a = frac(v1)*256;
      float v1b = floor(v1);
      float v2 = _y/256;
      float v2a = frac(v2)*256;
      float v2b = floor(v2);
      float v3 = (v1b*16)+v2b;
      return color(v1a, v2a, v3, 255);
    }
    void jfa() {
      int steps = floor(log2(width));
      bBack.beginDraw();
      // bBack.resetShader();
      bBack.image(bSource, 0, 0);
      bBack.endDraw();
      for (int indexPos = 0; indexPos < steps; ++indexPos) {
        // resetShader();
        float stepJump = pow(2, steps-indexPos -1);
        sJFA.set("iResolution", (float)width, (float)height);
        sJFA.set("texIn", bBack);
        sJFA.set("fIndex", (float)indexPos);
        sJFA.set("fJump", (float)stepJump);
        bFront.beginDraw();
        // bFront.clear();
        // bFront.noStroke();
        // bFront.fill(255);
        bFront.shader(sJFA);
        bFront.rect(0, 0, width, height);
        // bFront.resetShader();
        bFront.endDraw();
        bBack.beginDraw();
        // bBack.resetShader();
        // bBack.clear();
        bBack.image(bFront, 0, 0);
        bBack.endDraw();
      }
    }
    
    float log2 (int x) {
      return (log(x) / log(2));
    }
    int rC() {
      return color(random(255), random(255), random(255));
    }
    

    note on the right side the rect isn't cleared to white, some pixels are left.

    after 2 mouse clicks.
    img

    I recommend you put everything in a single function scope. and rethink your program flow. You have multiple beginDraw() - endDraw(), also resetShader and then redrawsource() if mouseClicked? > redrawsource(); again, but what really happens in the meantime, is the shader really gets the state reset, between all the beginDraws() etc. - also it affects the frameRate, if i would be Processing i would better be totally drunk, then guessing what the programmer, want from me to do next. :)

    void setup() {
    // initial settings
    }
    void draw(){
    // everything else
    }  
    

    Okay you can argument i commented something very important. Try it by your self. Line by line, and i hope you see, what i trying to explain.

  • edited December 2017

    @rupertofly
    made a little playground for you :)

    // slightly modified version found here: 4 cells voronoi - thebookofshaders.com/12/  
    
    PShader shdr;
    
    void setup() {
      // fullScreen(P2D);
      size(640, 360, P2D);
      rectMode(RADIUS);
      shdr=new PShader(this, new String[] {
        "#version 150"
          ,"in vec2 position;"
          +"void main() {"
          +"gl_Position = vec4(position.xy,0.,1.);"
         +"}"
      },new String[] {
        "#version 150"
         ,"uniform vec2 iResolution;"
          +"uniform vec2 iMouse;"
          +"out vec4 fragColor;"
          +"void main() {"
          +"vec2 st = (gl_FragCoord.xy/iResolution.xy);"
          +"st.x *= (iResolution.x/iResolution.y);"
          +"fragColor = vec4(0.);"
          +"vec2[]point=vec2[5](vec2(0.83,0.75),vec2(0.60,0.07),vec2(0.28,0.64),vec2(0.31,0.26),iMouse.xy);"
          +"float m_dist = 1.0;"
          +"for (int i = 0;i< 5;i++) {"
          +"float dist = distance(st, point[i]);"
          +"if ( dist < m_dist ) {"
          +"m_dist = dist;"
          +"point[0] = point[i];"
              +"}"
            +"};"
          +"fragColor =vec4(point[0],0.,1.0)+(1.-step(0.02, m_dist));"
         +"}"
      });
       //
       shdr.set("iResolution", (float)width, (float)height);
       shader(shdr);
    }
    void draw() {
      shdr.set("iMouse", norm(mouseX, 0, width), norm(mouseY, height, 0 ));
      background(0);
      rect(0, 0, width, height);
    }  
    

    optimized version with notes

    PShader shdr;
    
    void setup() {
      // fullScreen(P2D);
      size(640, 360, P2D);
     // rectMode(RADIUS);
      shdr=new PShader(this, new String[] {
        "#version 150"
          ,"in vec2 position;" // comma becourse of need of a \n [newline] after version declaration
          +"out vec2 uv;" // technically noperspective linear interpolation of an vec2 see smooth, flat
          +"void main() {"
          +"uv=position*0.5+0.5;" // per vertex - avoiding per pixel (fragment) coordinates computation 
          +"uv.x*="+(float)width/height+";" // inline aspect
          +"gl_Position = vec4(position,0.,1.);"
         +"}"
      },new String[] {
        "#version 150"
        ,"precision lowp float;" // going lowp just for fun - should be enough precision for 5 dezimal places
         +"in vec2 uv;" // noperspective 
          +"uniform vec2 iMouse;"
          +"out vec4 fragColor;"
          +"void main() {"
          +"fragColor = vec4(0.);"
          // technically vec2[5u](...) array i leave vec2[] empty dynamic size
          +"vec2[] point=vec2[](vec2(0.83,0.75),vec2(0.60,0.07),vec2(0.28,0.64),vec2(0.31,0.26),iMouse);"
          +"float m_dist = 1.0;"
          +"for (int i=0;i<5;i++) {"
          +"float dist = distance(uv, point[i] );"
          +"if ( dist < m_dist ) {"
          +"m_dist = dist;"
          +"point[0] = point[i];"
             +"}"
            +"};"
          +"fragColor =vec4(point[0],0.,1.0)+(1.-step(0.02, m_dist));"
         +"}"
      });
       shader(shdr);
    }
    void draw() {
      shdr.set("iMouse", (float)mouseX/width,1.f-(float)mouseY/height); // avoiding norm function call & we need to fleep the Y java axis 
     // background(0); // no need to erase java background
      rect(0, 0, -1, -1); // -1,-1. avoiding rectMode(RADIUS) - no viewmatix P2D - otherwise width,height get's clipped  
    }
    
Sign In or Register to comment.