2 sided transparency


#1

I want to make dynamic visualization where I need to use cube made of as many as possible smaller cubes with controlled color and opacity.
It will be somekind of voxel cloud.
I’m new to Processing but I have some knowladge about graphics.

As first steps to my goal I trying to make cubes manually.
I use beginShape(QUADS); and fill() to make transparent cube, but I found what opacity works not as I expect.
I see what opacity is like works only in one direction, and not transparent at all in another direction.
How to fix that?
there is my sketch

float a;
int c = 200 + 255*255+ 120*256*256 ;
boolean toggle = true;
void setup()  {
  size(640, 640, P3D);
  textMode(MODEL);
  //noStroke();
//fill(255,000,00,100);
}

void randfill()
{
 int x = int(random(255));
 int y = int(random(255));
 int z = int(random(255));
 fill(x,y,z,40);
}

void draw()  {
  background(190);
  //lights();

  
  translate(width/2, height/2, 0);
  rotateX(-PI/8+a/17); 
  rotateY(PI/6+a/23);
  rotateZ(PI/9);
  stroke(0);  
  fill(0);
  
  text(" 1  1  1", 100, 100, 100);
  text("-1  1  1",-100, 100, 100);
  text("-1 -1  1",-100,-100, 100);
  text("-1 -1 -1",-100,-100,-100);
  text("-1  1 -1",-100, 100,-100);
  text(" 1 -1 -1", 100,-100,-100);
  text(" 1  1 -1", 100, 100,-100);

  if (toggle){
    a += 0.05;
  }
  if(mousePressed){
    toggle = !toggle;
  }
    
  beginShape(QUADS);
  randfill();
  vertex(100,100,100);
  vertex(-100,100,100);
  vertex(-100,100,-100);
  vertex(100,100,-100); 
  
  randfill();
  vertex(100,-100,-100); 
  vertex(100,100,-100);
  vertex(100,100,100);
  vertex(100,-100,100);
  
  randfill();
  vertex(-100,-100,100);
  vertex(-100,100,100);
  vertex(100,100,100);
  vertex(100,-100,100); 
  
  randfill();
  vertex(-100,-100,100);
  vertex(-100,-100,-100);
  vertex(-100,100,-100);
  vertex(-100,100,100);
  
  randfill();
  vertex(-100,-100,100);
  vertex(100,-100,100);
  vertex(100,-100,-100);
  vertex(-100,-100,-100);
 
  randfill();
  vertex(-100,-100,-100);
  vertex(-100,100,-100);
  vertex(100,100,-100);
  vertex(100,-100,-100);  
  
  fill(0,250);
  vertex(100,200,100);
  vertex(-100,200,100);
  vertex(-100,200,-100);
  vertex(100,200,-100);
  
  vertex(200,-100,-100); 
  vertex(200,100,-100);
  vertex(200,100,100);
  vertex(200,-100,100);

  vertex(-100,-100,200);
  vertex(-100,100,200);
  vertex(100,100,200);
  vertex(100,-100,200); 

  vertex(-200,-100,100);
  vertex(-200,-100,-100);
  vertex(-200,100,-100);
  vertex(-200,100,100);  

  vertex(-100,-200,100);
  vertex(100,-200,100);
  vertex(100,-200,-100);
  vertex(-100,-200,-100); 

  vertex(-100,-100,-200);
  vertex(-100,100,-200);
  vertex(100,100,-200);
  vertex(100,-100,-200); 
  
  endShape();
  text(" 1  1  1", 100, 100, 100);
  text("-1  1  1",-100, 100, 100);
  text("-1 -1  1",-100,-100, 100);
  text("-1 -1 -1",-100,-100,-100);
  text("-1  1 -1",-100, 100,-100);
  text(" 1 -1 -1", 100,-100,-100);
  text(" 1  1 -1", 100, 100,-100);
  //box(160); 
}


#2

Does it look closer to what you want if you throw a

hint(ENABLE_DEPTH_SORT);

at the end of setup?


#3

Yes, its looks like what I want.
I think i got how its works, but its pretty surpricing for me.


#4

You could also try

hint(DISABLE_DEPTH_CHECK);

Somewhat different output - depends what effect you want the opacity to have. Massively different CPU usage though.


#5

i’m not sure if I undertanding it right, but is here possible more speed-up ?
I add pre-sorting of faces adding order to NOT use hint(ENABLE_DEPTH_SORT) because its really slow.
Also instead of adding all 6 faces of cube I add only 3 faces and will limit camera rotation to see cubes only from “good” side - side where this 3 faces will be viewed only from front side.
hint(DISABLE_DEPTH_TEST); do not make much change in FPS, and right now I can not say if rendering result is affected.

This changes made resonable performance for rendering 30^3 - 40^3 cubes

this is my sketch


int ar_size = 30;// number of cubes is ar_size^3
float cube_scale = 600; //hardcoded scaling

int q[][] = new int[ar_size* ar_size*ar_size][3];
void setup()  {
  size(640, 640, P3D);
  textMode(MODEL);
  //hint(ENABLE_DEPTH_SORT);
  //hint(DISABLE_DEPTH_TEST);
  noStroke();
//fill(255,000,00,100);
for(int i=0;i<ar_size* ar_size*ar_size;i++){
   int x = i;
   q[i][0] = x % ar_size;
   x /= ar_size;
   q[i][1] = x % ar_size;
   x /= ar_size;
   q[i][2] = x;
}
//bubble sort of cubes inserting order
for(int i=0; i<ar_size*ar_size*ar_size-1; i++){
  for(int j=i+1; j<ar_size*ar_size*ar_size; j++){
    int s1 = q[i][0]+q[i][1]+q[i][2];
    int s2 = q[j][0]+q[j][1]+q[j][2];
    if(s1<s2){
      int t1 = q[i][0];
      int t2 = q[i][1];
      int t3 = q[i][2];
      q[i][0]=q[j][0];
      q[i][1]=q[j][1];
      q[i][2]=q[j][2];
      q[j][0]=t1;
      q[j][1]=t2;
      q[j][2]=t3;
    }
  }
}
}

void randfill(int p)
{
 int x = int(0);
 int y = int(0);
 int z = int(random(100));
 fill(x,y,z,p); 
}

//place cube
void bx(float x,float y,float z,float size)
{
 // randfill(10);
 /*
  vertex(x+size,y-size,z-size); 
  vertex(x+size,y+size,z-size);
  vertex(x+size,y+size,z+size);
  vertex(x+size,y-size,z+size);

  //randfill(10);
  vertex(x-size,y-size,z+size);
  vertex(x-size,y+size,z+size);
  vertex(x+size,y+size,z+size);
  vertex(x+size,y-size,z+size);
  

    //randfill(10);
  vertex(x+size,y+size,z+size);
  vertex(x-size,y+size,z+size);
  vertex(x-size,y+size,z-size);
  vertex(x+size,y+size,z-size);
  */
  
   // randfill(10);
  vertex(x-size,y-size,z-size);
  vertex(x-size,y+size,z-size);
  vertex(x+size,y+size,z-size);
  vertex(x+size,y-size,z-size); 
   
    //randfill(10);
  vertex(x-size,y-size,z+size);
  vertex(x+size,y-size,z+size);
  vertex(x+size,y-size,z-size);
  vertex(x-size,y-size,z-size);
  
    //randfill(10);
  vertex(x-size,y-size,z+size);
  vertex(x-size,y-size,z-size);
  vertex(x-size,y+size,z-size);
  vertex(x-size,y+size,z+size);
}


void draw()  {
  println("fps: ",frameRate);
  background(0);
  //lights();
  colorMode(HSB,100,100,100,1024);

  translate(width/2, height/2, 0);
  rotateX(0.05*210*PI/8 + (mouseY-height/2)*0.003);  
  rotateY(0.05*285*PI/8 + (mouseX-width/2)*0.003);
  
  beginShape(QUADS);
  noStroke();
  
  float size = cube_scale/(2*ar_size);
  
  for(int i=0;i<ar_size*ar_size*ar_size;i++){
    int ind = q[i][0]+q[i][1]+q[i][2];
    int op = 10*(ar_size/2 - ind/3); 
    float r = abs(q[i][0]-ar_size/2)+
              abs(q[i][1]-ar_size/2)+
              abs(q[i][2]-ar_size/2);
    r = (ar_size*1.4-r)/ar_size;
    op = int(r*40);
    randfill(op);
    bx(q[i][0]*size-size*ar_size/2,
       q[i][1]*size-size*ar_size/2,
       q[i][2]*size-size*ar_size/2,
       size/2);
  } 
  endShape();  
}

On my hardware I have ~6 FPS for 40^3 cubes.
Any tips to make sketch even faster?


#6

27000 is a lot of cubes! :slight_smile:

Calling vertex 12*27000 times is not ideal. I think that sends a lot of data to the GPU item by item on each frame. Faster is probably having one PShape that contains all the quads to batch the GPU operations, then draw it in one shape(myShape) operation. Try to update the PShape (moving it’s vertices) instead of deleting and creating new PShapes. I think that should help, but getting to 60 fps may be a challenge and require lower level stuff like direct openGL or C++.

I wonder if avoiding transparency and using blendMode(ADD) instead helps, although it would look different.


#7

Try to update the PShape (moving it’s vertices) instead of deleting and creating new PShapes.

I do not undertand because I do not use PShape class and just use pure beginShape()

BTW your reply made me try.
this is about 8.2 FPS for 40^3 cubes

  for( iterating cubes ){  
 beginShape(QUADS);
    //add 3 faces  of cube
 endShape();
  } 

And this is 5.5 FPS

 beginShape(QUADS);
  for( iterating cubes ){ 
    
  }
 endShape();

meanwhile choosing right bulk size also can be a little faster as 8.5 FPS

  int cnt = -1;
  for( iterating cubes ){
    cnt += 1;
    if(cnt==0) {beginShape(QUADS);}
    //  add 3 faces  of cube 
    if(cnt==999){
      cnt=-1;
      endShape();      
    }  
  }

I’m not sure if I undertand you right, but by my thoughts using examples from
https://www.processing.org/reference/createShape_.html
must not do big difference in perfomance between using PShape class or just beginShape() / endShape() in draw(), but I can try to check it.

P.S. I do not need actually 60 FPS, but I want to make it as fast as possible to be able to have more cubes with not that low FPS, and also I will need to add array processing at each frame, what also will drop FPS.


#8

There’s a massive difference. In the examples under Demo/Performance compare CubicGridImmediate with CubicGridRetained.

Unfortunately, updating the geometry seems to have a bottleneck - at least the results with the particles examples are the opposite for me. Not looked enough to work out why yet.


#9

I was getting ahead of myself. My point was that if you switch to using a PShape, recreating it on every frame may be worse than modifying an existing instance.

The question was about 2 sided transparency and I think now you have some ideas on how to do that. To make it easier to find by others, maybe open a new question about increasing frame rate when generating 3D graphics?

Spoiler: frame rate can be limited by different reasons: high CPU usage, high GPU usage (shader complexity or high number of vertices or pixels), amount of data sent from CPU to GPU, computer temperature, etc. Drawing 27.000 shapes is actually a low number, and calculating 27.000 operations on the CPU is also a very low number. But sending data elements one by one from the CPU to the GPU is slow. Sending less, bigger chunks of data to the GPU, or keeping data in the GPU you can achieve better results. But let’s continue in another thread :slight_smile: