Browse Source

ambient occlusion demo

Former-commit-id: 870a80e705bb4127411b415614f7d2051dfb5026
Alec Jacobson (jalec 11 years ago
parent
commit
c3d5b2b5d9

+ 1 - 1
VERSION.txt

@@ -3,4 +3,4 @@
 # Anyone may increment Minor to indicate a small change.
 # Major indicates a large change or large number of changes (upload to website)
 # World indicates a substantial change or release
-0.3.2
+0.3.3

+ 33 - 0
examples/ambient-occlusion/Makefile

@@ -0,0 +1,33 @@
+
+.PHONY: all
+
+# Shared flags etc.
+include ../../Makefile.conf
+
+all: example
+
+.PHONY: example
+
+LIBIGL=../../
+LIBIGL_INC=-I$(LIBIGL)/include
+LIBIGL_LIB=-L$(LIBIGL)/lib -ligl -liglembree
+
+EIGEN3_INC=-I/opt/local/include/eigen3 -I/opt/local/include/eigen3/unsupported
+
+EMBREE=$(LIBIGL)/external/embree
+EMBREE_INC=-I$(EMBREE)/rtcore -I$(EMBREE)/common
+EMBREE_LIB=-L$(EMBREE)/bin -lrtcore -lsys
+
+ANTTWEAKBAR_INC=-I$(LIBIGL)/external/AntTweakBar/include
+ANTTWEAKBAR_LIB=-L$(LIBIGL)/external/AntTweakBar/lib -lAntTweakBar -framework AppKit
+INC=$(LIBIGL_INC) $(ANTTWEAKBAR_INC) $(EIGEN3_INC) $(EMBREE_INC)
+LIB=$(OPENGL_LIB) $(GLUT_LIB) $(ANTTWEAKBAR_LIB) $(LIBIGL_LIB) $(EMBREE_LIB)
+
+example: example.o
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -o example example.o $(LIB)
+
+example.o: example.cpp
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -c example.cpp -o example.o $(INC)
+clean:
+	rm -f example.o
+	rm -f example

+ 253 - 0
examples/ambient-occlusion/example.cpp

@@ -0,0 +1,253 @@
+#include <igl/OpenGL_convenience.h>
+#include <igl/per_face_normals.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/normalize_row_lengths.h>
+#include <igl/draw_mesh.h>
+#include <igl/draw_floor.h>
+#include <igl/quat_to_mat.h>
+#include <igl/report_gl_error.h>
+#include <igl/read.h>
+#include <igl/trackball.h>
+#include <igl/material_colors.h>
+#include <igl/barycenter.h>
+#include <igl/matlab_format.h>
+#include <igl/embree/EmbreeIntersector.h>
+#include <igl/embree/ambient_occlusion.h>
+
+#ifdef __APPLE__
+#  include <GLUT/glut.h>
+#else
+#  include <GL/glut.h>
+#endif
+
+#include <Eigen/Core>
+
+#include <vector>
+#include <iostream>
+
+// Width and height of window
+int width,height;
+// Rotation of scene
+float scene_rot[4] = {0,0,0,1};
+// information at mouse down
+float down_scene_rot[4] = {0,0,0,1};
+bool trackball_on = false;
+int down_mouse_x,down_mouse_y;
+// Position of light
+float light_pos[4] = {0.1,0.1,-0.9,0};
+// Vertex positions, normals, colors and centroid
+Eigen::MatrixXd V,N,C,mid;
+// Faces
+Eigen::MatrixXi F;
+// Bounding box diagonal length
+double bbd;
+igl::EmbreeIntersector<Eigen::MatrixXd,Eigen::MatrixXi,Eigen::Vector3d> ei;
+// Running ambient occlusion
+Eigen::VectorXd S;
+int tot_num_samples = 0;
+
+void reshape(int width,int height)
+{
+  using namespace std;
+  // Save width and height
+  ::width = width;
+  ::height = height;
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glViewport(0,0,width,height);
+}
+
+// Set up projection and model view of scene
+void push_scene()
+{
+  using namespace igl;
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  gluPerspective(45,(double)width/(double)height,1e-2,100);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  gluLookAt(0,0,3,0,0,0,0,1,0);
+  glPushMatrix();
+  float mat[4*4];
+  quat_to_mat(scene_rot,mat);
+  glMultMatrixf(mat);
+}
+
+void pop_scene()
+{
+  glPopMatrix();
+}
+
+// Scale and shift for object
+void push_object()
+{
+  glPushMatrix();
+  glScaled(2./bbd,2./bbd,2./bbd);
+  glTranslated(-mid(0,0),-mid(0,1),-mid(0,2));
+}
+
+void pop_object()
+{
+  glPopMatrix();
+}
+
+const float back[4] = {30.0/255.0,30.0/255.0,50.0/255.0,0};
+void display()
+{
+  using namespace Eigen;
+  using namespace igl;
+  using namespace std;
+
+  if(!trackball_on && tot_num_samples < 10000)
+  {
+    if(S.size() == 0)
+    {
+      S.resize(V.rows());
+      S.setZero();
+    }
+    VectorXd Si;
+    const int num_samples = 20;
+    ambient_occlusion(ei,V,N,num_samples,Si);
+    S *= (double)tot_num_samples;
+    S += Si*(double)num_samples;
+    tot_num_samples += num_samples;
+    S /= (double)tot_num_samples;
+    // Convert to 1-intensity
+    C.resize(S.rows(),3);
+    C<<S,S,S;
+    C.array() = (1.0-C.array());
+  }
+
+  glClearColor(back[0],back[1],back[2],0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  // All smooth points
+  glEnable( GL_POINT_SMOOTH );
+
+  glDisable(GL_LIGHTING);
+  push_scene();
+  glEnable(GL_DEPTH_TEST);
+  glDepthFunc(GL_LEQUAL);
+  glEnable(GL_NORMALIZE);
+  glEnable(GL_COLOR_MATERIAL);
+  glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
+  push_object();
+
+  // Draw the model
+  // Set material properties
+  glEnable(GL_COLOR_MATERIAL);
+  draw_mesh(V,F,N,C);
+
+  pop_object();
+
+  // Draw a nice floor
+  glPushMatrix();
+  const double floor_scale = 2./bbd;
+  const double floor_offset =
+    -2./bbd*(V.col(1).minCoeff()+mid(1));
+  glTranslated(0,floor_offset,0);
+  const float GREY[4] = {0.5,0.5,0.6,1.0};
+  const float DARK_GREY[4] = {0.2,0.2,0.3,1.0};
+  draw_floor(GREY,DARK_GREY);
+  glPopMatrix();
+
+  pop_scene();
+
+  report_gl_error();
+
+  glutSwapBuffers();
+  glutPostRedisplay();
+}
+
+void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  using namespace Eigen;
+  using namespace igl;
+  switch(glutState)
+  {
+    case 1:
+      // up
+      glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
+      trackball_on = false;
+      break;
+    case 0:
+      // down
+      glutSetCursor(GLUT_CURSOR_CYCLE);
+      // collect information for trackball
+      trackball_on = true;
+      copy(scene_rot,scene_rot+4,down_scene_rot);
+      down_mouse_x = mouse_x;
+      down_mouse_y = mouse_y;
+    break;
+  }
+}
+
+void mouse_drag(int mouse_x, int mouse_y)
+{
+  using namespace igl;
+
+  if(trackball_on)
+  {
+    // Rotate according to trackball
+    trackball<float>(
+      width,
+      height,
+      2,
+      down_scene_rot,
+      down_mouse_x,
+      down_mouse_y,
+      mouse_x,
+      mouse_y,
+      scene_rot);
+  }
+}
+
+
+void key(unsigned char key, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  switch(key)
+  {
+    // Ctrl-c and esc exit
+    case char(3):
+    case char(27):
+      exit(0);
+    default:
+      cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
+  }
+  
+}
+
+
+int main(int argc, char * argv[])
+{
+  using namespace Eigen;
+  using namespace igl;
+  using namespace std;
+
+  // init mesh
+  if(!read("../shared/beast.obj",V,F))
+  {
+    return 1;
+  }
+  // Compute normals, centroid, colors, bounding box diagonal
+  per_vertex_normals(V,F,N);
+  mid = 0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff());
+  bbd = (V.colwise().maxCoeff() - V.colwise().minCoeff()).maxCoeff();
+
+  // Init embree
+  ei = EmbreeIntersector<MatrixXd,MatrixXi,Vector3d>(V,F);
+
+  // Init glut
+  glutInit(&argc,argv);
+  glutInitDisplayString( "rgba depth double samples>=8 ");
+  glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT));
+  glutCreateWindow("ambient-occlusion");
+  glutDisplayFunc(display);
+  glutReshapeFunc(reshape);
+  glutKeyboardFunc(key);
+  glutMouseFunc(mouse);
+  glutMotionFunc(mouse_drag);
+  glutMainLoop();
+  return 0;
+}

+ 1 - 0
examples/shared/beast.obj.REMOVED.git-id

@@ -0,0 +1 @@
+f74ba92908329690def57b05ddc547293716c1e5

+ 1 - 1
include/igl/embree/Makefile

@@ -37,7 +37,7 @@ obj:
 	ar cqs $@ $(OBJ_FILES)
 
 obj/%.o: %.cpp %.h
-	g++ $(AFLAGS) $(CFLAGS) -c -o $@ $< $(INC)
+	g++ $(AFLAGS) $(OPENMP) $(CFLAGS) -c -o $@ $< $(INC)
 
 clean:
 	rm -f obj/*.o

+ 55 - 0
include/igl/embree/ambient_occlusion.cpp

@@ -0,0 +1,55 @@
+#include "ambient_occlusion.h"
+#include "EmbreeIntersector.h"
+#include <igl/random_dir.h>
+
+template <
+  typename PointMatrixType,
+  typename FaceMatrixType,
+  typename RowVector3,
+  typename DerivedP,
+  typename DerivedN,
+  typename DerivedS >
+void igl::ambient_occlusion(
+  const igl::EmbreeIntersector<PointMatrixType,FaceMatrixType,RowVector3> & ei,
+  const Eigen::PlainObjectBase<DerivedP> & P,
+  const Eigen::PlainObjectBase<DerivedN> & N,
+  const int num_samples,
+  Eigen::PlainObjectBase<DerivedS> & S)
+{
+  using namespace Eigen;
+  using namespace igl;
+  const int n = P.rows();
+  // Resize output
+  S.resize(n,1);
+  // Embree seems to be parallel when constructing but not when tracing rays
+#pragma omp parallel for
+  // loop over mesh vertices
+  for(int p = 0;p<n;p++)
+  {
+    const Vector3d origin = P.row(p);
+    const Vector3d normal = N.row(p);
+    int num_hits = 0;
+    MatrixXd D = random_dir_stratified(num_samples);
+    for(int s = 0;s<num_samples;s++)
+    {
+      //Vector3d d = random_dir();
+      Vector3d d = D.row(s);
+      if(d.dot(normal) < 0)
+      {
+        // reverse ray
+        d *= -1;
+      }
+      embree::Hit hit;
+      if(ei.intersectRay(origin,d,hit))
+      {
+        num_hits++;
+      }
+    }
+    S(p) = (double)num_hits/(double)num_samples;
+  }
+}
+
+#ifndef IGL_HEADER_ONLY
+// Explicit template instanciation
+template void igl::ambient_occlusion<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 3, 1, 0, 3, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(igl::EmbreeIntersector<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, const int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
+#endif

+ 39 - 0
include/igl/embree/ambient_occlusion.h

@@ -0,0 +1,39 @@
+#ifndef IGL_AMBIENT_OCCLUSION_H
+#define IGL_AMBIENT_OCCLUSION_H
+#include <igl/igl_inline.h>
+#include <Eigen/Core>
+namespace igl
+{
+  // Forward define
+  template <
+    typename PointMatrixType,
+    typename FaceMatrixType,
+    typename RowVector3>
+  class EmbreeIntersector;
+  // Compute ambient occlusion per given point
+  //
+  // Inputs:
+  //    ei  EmbreeIntersector containing (V,F)
+  //    P  #P by 3 list of origin points
+  //    N  #P by 3 list of origin normals
+  // Outputs:
+  //    S  #P list of ambient occlusion values between 1 (fully occluded) and 0
+  //      (not occluded)
+  //
+  template <
+    typename PointMatrixType,
+    typename FaceMatrixType,
+    typename RowVector3,
+    typename DerivedP,
+    typename DerivedN,
+    typename DerivedS >
+  void ambient_occlusion(
+    const igl::EmbreeIntersector<PointMatrixType,FaceMatrixType,RowVector3> & ei,
+    const Eigen::PlainObjectBase<DerivedP> & P,
+    const Eigen::PlainObjectBase<DerivedN> & N,
+    const int num_samples,
+    Eigen::PlainObjectBase<DerivedS> & S);
+
+
+};
+#endif

+ 47 - 0
include/igl/random_dir.cpp

@@ -0,0 +1,47 @@
+#include "random_dir.h"
+#include <igl/PI.h>
+#include <cmath>
+
+Eigen::Vector3d igl::random_dir()
+{
+  using namespace Eigen;
+  using namespace igl;
+  double z =  (double)rand() / (double)RAND_MAX*2.0 - 1.0;
+  double t =  (double)rand() / (double)RAND_MAX*2.0*PI;
+  // http://www.altdevblogaday.com/2012/05/03/generating-uniformly-distributed-points-on-sphere/
+  double r = sqrt(1.0-z*z);
+  double x = r * cos(t);
+  double y = r * sin(t);
+  return Vector3d(x,y,z); 
+}
+
+Eigen::MatrixXd igl::random_dir_stratified(const int n)
+{
+  using namespace Eigen;
+  using namespace igl;
+  using namespace std;
+  const double m = floor(sqrt(double(n)));
+  MatrixXd N(n,3);
+  int row = 0;
+  for(int i = 0;i<m;i++)
+  {
+    const double x = double(i)*1./m;
+    for(int j = 0;j<m;j++)
+    {
+      const double y = double(j)*1./m;
+      double z = (x+(1./m)*(double)rand() / (double)RAND_MAX)*2.0 - 1.0;
+      double t = (y+(1./m)*(double)rand() / (double)RAND_MAX)*2.0*PI;
+      double r = sqrt(1.0-z*z);
+      N(row,0) = r * cos(t);
+      N(row,1) = r * sin(t);
+      N(row,2) = z;
+      row++;
+    }
+  }
+  // Finish off with uniform random directions
+  for(;row<n;row++)
+  {
+    N.row(row) = random_dir();
+  }
+  return N;
+}

+ 20 - 0
include/igl/random_dir.h

@@ -0,0 +1,20 @@
+#ifndef IGL_RANDOM_DIR_H
+#define IGL_RANDOM_DIR_H
+#include "igl_inline.h"
+
+#include <Eigen/Core>
+
+namespace igl
+{
+  // Generate a uniformly random unit direction in 3D, return as vector
+  Eigen::Vector3d random_dir();
+  // Generate n stratified uniformly random unit directions in 3d, return as rows
+  // of an n by 3 matrix
+  //
+  // Inputs:
+  //   n  number of directions
+  // Return n by 3 matrix of random directions
+  Eigen::MatrixXd random_dir_stratified(const int n);
+}
+
+#endif