Smooth edges on a surface

Hi! i’m using the marching cubes algorithm for create surfaces. My goal is to create surfaces with a smooth looking. The algorithm creates a facet approximation of the surface

following the explanation of Paul Bourke ( http://paulbourke.net/geometry/polygonise/ ) i was able to create a smooth looking surface using the average of the vertex normals.

The thing is that this process is too slow, first, i create an array with unique vertices. Then, i compare all the vertices with the unique vertices in order to asing the average normal to the unique vertices.

I want to create this smooth looking surfaces, but with big datasets (like an MRI scan).

I’m looking for a better approach, i guess using shaders will be a really good option because of the GPU usage, but i’m new in that topic and don’t know where to start.

In the image you can see the comparison between the marching cubes algorithm with and without averaging the normals.

¡Gracias!

1 Like

@hamoid Hi!This is what i want to ask you :slight_smile:

Hi @bryan,

A shader implementation would probably be more suitable to deal with large datasets but you can still give the LaplacianSmooth class from the toxiclibs library a try.

2 Likes

If you’re rendering with Toxiclibs, just turn smoothing on:

gfx.mesh(mesh, true); // <-- true = smooth on

:slight_smile:

2 Likes

You mention MRI scan, are you aware of toxiclibs example, here modified to processing3 syntax and using smooth as per hamoid suggestion:-

/**
 * MRISurface demo showing how to utilize the IsoSurface class to efficiently
 * visualise volumetric data, in this case using MRI scan data obtained from the
 * volvis.org repository of free datasets.
 * The demo also shows how to save the generated mesh as binary STL file (or
 * alternatively in OBJ format) for later use in other 3D tools/digital fabrication.
 * 
 * I've planned further classes for the toxi.geom.volume package to easier draw
 * and manipulate volumetric data.
 * 
 * Important note: This demo is fairly memory hungry, so if you receive OutOfMemory
 * errors, please make sure to give Processing as much RAM as possible (see preferences)
 * and if this still fails, reduce the surface resolution from DIM=128 to 64 or 32...
 *
 * Controls:
 * Click mouse button to toggle rendering style between shaded/wireframe.
 */

/* 
 * Copyright (c) 2009 Karsten Schmidt
 * 
 * This demo & library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * http://creativecommons.org/licenses/LGPL/2.1/
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.volume.*;
import toxi.math.noise.*;
import toxi.processing.*;

import processing.opengl.*;

int DIM=128;
float ISO_THRESHOLD = 0.1;
Vec3D SCALE=new Vec3D(DIM,DIM,DIM).scaleSelf(8);

IsoSurface surface;
Mesh3D mesh;
ToxiclibsSupport gfx;

boolean isWireframe=false;

void setup() {
  size(1024,768,P3D);
  // hint(ENABLE_OPENGL_4X_SMOOTH);
  gfx=new ToxiclibsSupport(this);
  strokeWeight(0.5);
  // convert MRI scan data into floats
  // MRI data is 256 x 256 x 256 voxels @ 8bit/voxel
  byte[] mriData=loadBytes("aneurism.raw.gz");
  // scale factor to normalize 8bit to the 0.0 - 1.0 interval
  float mriNormalize=1/255.0;
  // setup lower resolution grid for IsoSurface
  VolumetricSpaceArray volume=new VolumetricSpaceArray(SCALE,DIM,DIM,DIM);
  float[] cloud=volume.getData();
  int stride=256/DIM;
  for(int z=0,idx=0; z<256; z+=stride) {
    for(int y=0; y<256; y+=stride) {
      int sliceIdx=y*256+z*65536;
      for(int x=0; x<256; x+=stride) {
        byte b=mriData[x+sliceIdx];
        cloud[idx++]=(int)(b<0 ? 256+b : b)*mriNormalize;
      }
    }
  }
  long t0=System.nanoTime();
  // create IsoSurface and compute surface mesh for the given iso threshold value
  surface=new HashIsoSurface(volume,0.15);
  mesh=surface.computeSurfaceMesh(null,ISO_THRESHOLD);
  float timeTaken=(System.nanoTime()-t0)*1e-6;
  println(timeTaken+"ms to compute "+mesh.getNumFaces()+" faces");
}

void draw() {
  background(128);
  translate(width/2,height/2,0);
  rotateX(mouseY*0.01);
  rotateY(mouseX*0.01);
  ambientLight(48,48,48);
  lightSpecular(230,230,230);
  directionalLight(255,255,255,0,-0.5,-1);
  specular(255,255,255);
  shininess(16.0);
  if (isWireframe) {
    stroke(255);
    noFill();
  } 
  else {
    noStroke();
    fill(255);
  }
  gfx.mesh(mesh, true);
}

2 Likes

Many thanks! this is exactly what i was looking for!