How can I optimize Box drawing ?

edited June 2017 in Library Questions

Hi, I've tried to make a program that shows a 2D image in 3D, using the brightness of each pixel. So I draw a box at each x and y, the z is the birghtness. Unfortunately, the only way I've found to see the image is to draw boxes in draw(), but it gets really slow for images bigger than 400*400. I think that there are ways to make that better, so how can I do it ?

Code :

import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;
PeasyCam camera;
PImage img;

void setup() {
  size(800, 800, P3D);
  ambientLight(255, 255, 255, 0, 0, 0);
  img = loadImage("download.jpg");
  camera = new PeasyCam(this, img.width/2, img.height/2, 0, 1000);
  img.loadPixels();
}
void draw() {
  background(0);
  for (int i=0; i<img.pixels.length; i++) {
    color c = img.pixels[i];
    float b = map(brightness(c), 0, 255, -100, 100);
    fill(c);
    noStroke();
    int x = i%(img.width);
    int y = (i-x)/img.width;
    translate(x, y, b);
    box(1);
    translate(-x, -y, -b);
  }
}

Answers

  • 640,000 boxes...

  • The geometry is static so try a PShape. Also, boxes that small probable won't look like boxes so try points instead - no need to draw 6 sides per pixel that way.

    But the main problem is the sheer number of boxes you're trying to draw

  • I can draw less boxes, but I'll lose resolution. And I can't get the PShape method to work :

    import peasy.*;
    import peasy.org.apache.commons.math.*;
    import peasy.org.apache.commons.math.geometry.*;
    PeasyCam camera;
    PImage img;
    PShape particles;
    
    void setup() {
      size(800, 800, P3D);
      ambientLight(255, 255, 255, 0, 0, 0);
      img = loadImage("download.jpg");
      camera = new PeasyCam(this, img.width/2, img.height/2, 0, 1000);
      particles = createShape(PShape.GROUP);
      img.loadPixels();
      for (int i=0; i<img.pixels.length; i++){
        PShape part = createShape(BOX,10,10,10);
        part.setFill(img.pixels[i]);
        part.beginShape();
        part.noStroke();
        part.endShape();
        particles.addChild(part);
        int x = i%(img.width);
        int y = (i-x)/img.width;
        translate(x, y, map(brightness(img.pixels[i]), 0, 255, -100, 100));
      }
    }
    void draw() {
      background(0);
      shape(particles);
      println(frameRate);
    }
    
  • can't test the code at the moment but that translate at line 24 looks like it needs applying to the part. otherwise where are you specifying the position of the cube within the group?

    there is a limit to the amount of things you can put into a shape, depends on your graphics cards. i also find that adding things as children incurs some overhead, as if something iterates through the children one at a time. but you have no choice here, i don't think (unless you want to add your own quads to a single shape, 6 for each cube)

  • edited June 2017

    In your first solution : you could fill an ArrayList of boxes in setup and display the ArrayList

    But your 2nd solution without line 30 And some corrections looks great


    example for solution 1 with ArrayList holding boxes of type class Box (another topic but same class and similar filling of the class in setup / generate):

        import peasy.*;
    
        ArrayList<Box> boxes = new ArrayList(); 
        PeasyCam cam; 
    
        // ------------------------------------------------
    
        void setup() {
          size(1200, 1000, P3D);
          cam = new PeasyCam(this, 0, 0, 0, 200);
          println("working"); 
          //boxes=
          generate(0, 0, 0, 
            2, 
            167);
          println("done: "+boxes.size());
        }
    
        void draw() {
          background(0); 
          avoidClipping();
          lights();
          for (int i = 0; i<boxes.size(); i++) {
            //for (int i = 0; i<1; i++) {
            boxes.get(i).show();
          }
        }
    
        // ------------------------------------------------------
    
        void generate(float x2, float y2, float z2, int depth, float r) {
    
          PVector pos=new PVector(x2, y2, z2); 
          int sum ;
    
          for (int x = -1; x < 2; x++) {
            for (int y = -1; y < 2; y++) {
              for (int z = -1; z < 2; z++) {
    
                sum = abs(x) + abs(y) + abs(z);
    
                if (false) {
                  if (sum <= 1) {
                    break;
                  }
                }
    
                float newR = r/3;
    
                if (sum > 1) {
                  if (depth==0) {
                    Box b = new Box(pos.x + x*newR, 
                      pos.y + y*newR, 
                      pos.z + z*newR, 
                      newR, 
                      boxes.size());
    
                    // if (x!=-1&&y!=0)
                    // if (random(100)>9)
                    boxes.add(b);
                  } else
                  {
                    generate(pos.x + x*newR, 
                      pos.y + y*newR, 
                      pos.z + z*newR, 
                      depth-1, 
                      newR);
                  }
                }
              }
            }
          }
          // return boxes;
        }//func
    
        void avoidClipping() {
          // avoid clipping (at camera): 
          // https : // 
          // forum.processing.org/two/discussion/4128/quick-q-how-close-is-too-close-why-when-do-3d-objects-disappear
          perspective(PI/3.0, (float) width/height, 1, 1000000);
        }//func 
    
    
        // ========================================================
    
        class Box {
    
          PVector pos;
          float radius;
          int index; 
    
          Box(float x, float y, float z, 
            float radius_, 
            int index_) {
            pos = new PVector(x, y, z);
            radius = radius_;
            index = index_;     //index = boxes.size();
          }
    
          void show() {
            pushMatrix();
            translate(pos.x, pos.y, pos.z);
    
            // text 
            if (false) {
              pushMatrix(); // store Matrix
              fill(255, 2, 2); //red
              textAlign(CENTER, CENTER); // alignment  
              textMode(SHAPE);           // looks much better 
              text(index, 0, 0, 0); // this is new
              popMatrix(); // restore Matrix
            }
    
            // how to draw the box
            if (!keyPressed) {
              //  noStroke();
              stroke(111); 
              fill(255);//white
              fill(255, 0, 0);//white
            } else {
              noFill(); 
              stroke(255);
            }
    
            //box
            box(radius);
    
            popMatrix();
          }//method
          //
        }//class 
        //
    
  • Thank you but with that, I still get low framerate. I've tried with triangles strip and it works great :Sans titre

    Tell me if there is some adjustments/optimizations to be made :

    import peasy.*;
    import peasy.org.apache.commons.math.*;
    import peasy.org.apache.commons.math.geometry.*;
    PeasyCam camera;
    PImage img;
    float[][] z;
    int step = 1; // Higher = Less precision but more speed
    
    void setup() {
      size(800, 800, P3D);
      ambientLight(255, 255, 255, 0, 0, 0);
      img = loadImage("lsbgal.jpg");
      camera = new PeasyCam(this, img.width/2, img.height/2, 0, 1000);
      img.loadPixels();
      //z[][] stores values of z for each pixel of the image
      z = new float[img.width][img.height];
      for (int i=0; i<img.width-1; i++) {
        for (int j=0; j<img.height; j++) {
          z[i][j] = map(brightness(img.pixels[j*img.width +i]), 0, 255, -25, 25);
        }
      }
      //Smooth the z values
      for (int i=1; i<img.width-1; i++) {
        for (int j=1; j<img.height-1; j++) {
          z[i][j] = (z[i-1][j-1]+z[i-1][j]+z[i-1][j+1]+z[i][j-1]+z[i][j+1]+z[i+1][j-1]+z[i+1][j]+z[i+1][j+1])/8;
        }
      }
      noStroke();
    }
    void draw() {
      background(0);
      for (int i=0; i<(img.width-step); i+=step) {
        beginShape(TRIANGLE_STRIP);
        for (int j=0; j<(img.height-step); j+=step) {
          fill(img.pixels[j*img.width +i]);
          vertex(i, j, z[i][j]);
          vertex(i+step, j, z[i+step][j]);
        }
        endShape();
      } 
    }
    
  • Not possible to store what draw does in a PShape and display that?

Sign In or Register to comment.