# Cull faces in P3D

**URL:** <https://discourse.processing.org/t/cull-faces-in-p3d/43127>\
**Category:** Coding Questions\
**Created:** [November 2, 2023, 9:36pm UTC](https://discourse.processing.org/t/cull-faces-in-p3d/43127 "2023-11-02T21:36:51Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Noodlybanan](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/noodlybanan/32/18897_2.png) [@Noodlybanan](https://discourse.processing.org/u/Noodlybanan)\
**Post date:** [November 2, 2023, 9:36pm UTC](https://discourse.processing.org/t/cull-faces-in-p3d/43127/1 "2023-11-02T21:36:51Z")

</div>

Hi! I was wondering if it is possible to set a face, to only be visible from one angle, as it would increase performance also known as culling faces. Thanks for all your advice 🙂

Im programming a voxel engine (never has been done before, i know) and [this Project called AlpahBlock](https://github.com/Xenthera/AlphaBlock) inspired me, it looks quiet cool and it was made with processing and the performance is also great.

-Libby

---

<div class="post-metadata">

**Author:** ![villares](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/villares/32/3166_2.png) [@villares](https://discourse.processing.org/u/villares)\
**Post date:** [November 8, 2023, 11:27am UTC](https://discourse.processing.org/t/cull-faces-in-p3d/43127/2 "2023-11-08T11:27:52Z")

</div>

That is a great question I wish I had more knowledge about! Managing to cull unseen faces & edges is also very useful for people who want to export vector graphics for pen-plotters…

---

<div class="post-metadata">

**Author:** ![hx2A](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/hx2a/32/14322_2.png) [@hx2A](https://discourse.processing.org/u/hx2A)\
**Post date:** [November 10, 2023, 4:58pm UTC](https://discourse.processing.org/t/cull-faces-in-p3d/43127/3 "2023-11-10T16:58:07Z")

</div>

Face culling can be done by first creating triangles with consistent “winding” of each triangle. This means that the order of the three vertices in each triangle will be consistently counter-clockwise, for example.

Once you have consistent winding, then you can examine the normal vectors of each face. This is usually done by taking a cross product of two vectors. If the triangle vertices are A, B, and C, you take the cross product of AB with AC. You can then look at the sign of the z value to determine if it is front-facing or back-facing.

---

<div class="post-metadata">

**Author:** ![scudly](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/scudly/32/5597_2.png) [@scudly](https://discourse.processing.org/u/scudly)\
**Post date:** [November 10, 2023, 8:01pm UTC](https://discourse.processing.org/t/cull-faces-in-p3d/43127/4 "2023-11-10T20:01:37Z")

</div>

Processing doesn’t seem to use face culling anywhere internally that I can find, but, frustratingly, in its frame cleanup code, it explicitly disables face culling. You can enable it yourself as long as you do it every frame.

Note that the culling here is being done by OpenGL, so it won’t help with exporting to vector formats. And it’s also quite delicate. It’s likely that mixing in other Processing drawing calls might well, disable the culling again.

Press a key to cycle through no culling, back-face culling, and front-face culling.

```auto
PJOGL pgl;
int cullMode = 0;

void setup() {
  size( 800, 800, P3D );
  pgl = (PJOGL) beginPGL();
}

void drawSide() {
  beginShape();
  normal( 0, 0, 1 );
  vertex( -1, -1, 1.5 );
  vertex( 1, -1, 1.5 );
  vertex( 1, 1, 1.5 );
  vertex( -1, 1, 1.5 );
  endShape(CLOSE);
}

void draw() {
  background(64);
  translate( width/2, height/2 );
  scale( height/5.0 );

  lights();

  rotateX(0.01*frameCount);
  rotateY(0.013*frameCount);

  strokeWeight( 0.02 );
// noStroke();
  fill(255);

  if( cullMode == 1 ) {
    pgl.enable( PGL.CULL_FACE );
    pgl.cullFace( PGL.BACK );
  } else if( cullMode == 2 ) {
    pgl.enable( PGL.CULL_FACE );
    pgl.cullFace( PGL.FRONT );
  }

  pushMatrix();
  for( int i=0; i<4; i++ ) {
    drawSide();
    rotateY( TAU/4 );
  }
  rotateX( TAU/4 );
  drawSide();
  rotateX( TAU/2 );
  drawSide();
  popMatrix();
  
  fill( 255, 255, 0 );
  box( 1.4, 1.2, 1.3 );

}

void keyPressed() {
  cullMode = (cullMode+1) % 3;
}

```

---

<div class="post-metadata">

**Author:** ![scudly](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/scudly/32/5597_2.png) [@scudly](https://discourse.processing.org/u/scudly)\
**Post date:** [November 10, 2023, 10:04pm UTC](https://discourse.processing.org/t/cull-faces-in-p3d/43127/5 "2023-11-10T22:04:02Z")

</div>

This one lets you check the performance difference. With N=500, this renders approx. 500\*500 \* 3 quads \* 2 surfaces. Each quad is 2 triangles, so around 3 million triangles total. On my 7 year old machine, I get around 115 fps with no culling and 120 fps with back face culling.

Front face culling prevents the frame rate text from showing which is curious since it worked on my previous example. I would have expected it to not show since it would be text rendered onto a front-facing quad.

So culling takes it from 115 to 120 fps at the risk of other things not working. Definitely put your voxels together into PShapes rather than rendering them as separate `box()` calls. The speedup is huge.

```auto
int N = 500;
PShape s1, s2;

PShape genGrid( int N, float [][] data ) {
  float c = 2.6*N/32;
  PShape s = createShape();
  s.beginShape(QUADS);
  s.noStroke();
  for( int i=0; i<N; i++ ) {
    // -Y side
    float z2 = data[0][i];
    s.fill( data[0][i]/c, 0.5, 1 );
    s.vertex( i+1, 0, 0 );
    s.vertex( i+1, 0, z2 );
    s.vertex( i, 0, z2 );
    s.vertex( i, 0, 0 );
  }
  for( int j=0; j<N; j++ ) {
    float z0, z1, z2;
    // -X side
    z1 = data[j][0];
    s.fill( data[j][0]/c, 0.5, 1 );
    s.vertex( 0, j, 0 );
    s.vertex( 0, j, z1 );
    s.vertex( 0, j+1, z1 );
    s.vertex( 0, j+1, 0 );
    
    for( int i=0; i<N; i++ ) {
      z0 = z1;
      z1 = data[j][i+1];
      z2 = data[j+1][i];

      // +Z top
      s.fill( data[j][i]/c, 0.5, 1 );
      s.vertex( i, j, z0 );
      s.vertex( i+1, j, z0 );
      s.vertex( i+1, j+1, z0 );
      s.vertex( i, j+1, z0 );

      // +X side
      s.fill( data[j][i+(z0>z1?0:1)]/c, 0.5, 1 );
      s.vertex( i+1, j, z0 );
      s.vertex( i+1, j, z1 );
      s.vertex( i+1, j+1, z1 );
      s.vertex( i+1, j+1, z0 );

      // +Y side
      s.fill( data[j+(z0>z2?0:1)][i]/c, 0.5, 1 );
      s.vertex( i+1, j+1, z0 );
      s.vertex( i+1, j+1, z2 );
      s.vertex( i, j+1, z2 );
      s.vertex( i, j+1, z0 );
    }
  }
  s.endShape();
  return s;
}

PJOGL pgl;
int cullMode = 0;

void setup() {
  size( 1600, 1200, P3D );
  frameRate(1000);
  pgl = (PJOGL) beginPGL();
  colorMode( HSB, 1, 1, 1 );
  // leave a strip of 0 on the right and bottom
  float [][] data = new float[N+1][N+1];
  for( int j=0; j<N; j++ )
    for( int i=0; i<N; i++ ) {
      float x = 2.*i/N-1, y = 2.*j/N-1,
        a = atan2( y, x ), r = sqrt(x*x+y*y);
      data[j][i] = (1.1+cos(TAU*2*r+3*a) + random(.5)) * N/32;
    }
  s1 = genGrid( N, data );
  for( int j=0; j<N; j++ )
    for( int i=0; i<N; i++ ) {
      float x = 2.*i/N-0.5, y = 2.*j/N-1.2,
        a = atan2( y, x ), r = sqrt(x*x+y*y);
      data[j][i] = (1.1+cos(TAU*2*(r+0.1*sin(5*a))) + random(.5)) * N/32;
    }
  s2 = genGrid( N, data );
}

void draw() {
  background( 0.6, 1, 0.25 );

  if( cullMode == 1 ) {
    pgl.enable( PGL.CULL_FACE );
    pgl.cullFace( PGL.BACK );
  } else if( cullMode == 2 ) {
    pgl.enable( PGL.CULL_FACE );
    pgl.cullFace( PGL.FRONT );
  }

  lights();
  camera( 0, 0, N*1.2, 0, 0, 0, 0, 1, 0 );
  perspective( PI/3, 1.*width/height, N/4, N*4 );
  push();
  rotateY( TAU/5. );
  translate( 0, 0, -N*0.35 );
  rotateX( TAU/12. );
  rotateZ( frameCount/115. );
  shape( s1, -N/2, -N/2 );
  pop();
  push();
  rotateY( -TAU/5. );
  translate( 0, 0, -N*0.35 );
  rotateX( TAU/12. );
  rotateZ( frameCount/115. );
  shape( s2, -N/2, -N/2 );
  pop();
  fill( 0, 0, 1 );
  camera();
  perspective();
  text( nf( frameRate, 0, 2 ), 10, 20 );
}

void keyPressed() {
  cullMode = (cullMode+1) % 3;
}

```
