Manta Interactive Ray Tracer Development Mailing List

Text archives Help


[Manta] r1942 - in trunk: Engine/Factory Engine/Renderers Interface scenes


Chronological Thread 
  • From: roni@sci.utah.edu
  • To: manta@sci.utah.edu
  • Subject: [Manta] r1942 - in trunk: Engine/Factory Engine/Renderers Interface scenes
  • Date: Fri, 21 Dec 2007 17:20:34 -0700 (MST)

Author: roni
Date: Fri Dec 21 17:20:30 2007
New Revision: 1942

Added:
   trunk/Engine/Renderers/NPREdges.cc
   trunk/Engine/Renderers/NPREdges.h
   trunk/scenes/tylenol.cc
Modified:
   trunk/Engine/Factory/RegisterKnownComponents.cc
   trunk/Engine/Renderers/CMakeLists.txt
   trunk/Engine/Renderers/Raytracer.cc
   trunk/Interface/RayPacket.h
   trunk/scenes/CMakeLists.txt
Log:
Adding an NPR edge renderer.  It works by casting a stencil of rays
about each sample ray and checking whether:

1. Any ray in the stencil strikes a primitive different from the one
struck by the sample ray

2. If all rays in the stencil strike the same object as the sample
ray, whether the gradient of the normal field is large.

If condition 1 holds, then the sample is shaded black because it is
either a silhouette line or an intersection line between two
primitives.  If condition 2 holds, then the sample is shaded black
because it is a crease line.

Currently, silhouettes are defined as an intersection between a
primitive and the background and are therefore treated the same as
intersections between two primitives.  This may change if people find
it useful to distinguish silhouettes from intersections.

Also, the way the crease lines are handled wastes computation if it is
known that a primitive has no creases (e.g. a sphere).  I don't know
what the performance hit is (in fact, disabling crease handling at
compile time makes no appreciable difference on this G4 laptop I'm
using to do all the coding and testing at the moment) but a more
flexible way to handle creases may be useful for other reasons as
swell.

scenes/tylenol.cc:
scenes/CMakeLists.txt:

  An acetaminophen molecule (or paracetamol to you commonwealthers).
I added this because it nicely demonstrates the primitive-primitive
intersection lines.  Sometimes this molecule is just what you need
after hours of debugging a new Manta renderer ;)

Interface/RayPacket.h:

  The wasHit() method should be const.

Engine/Factory/RegisterKnownComponents.cc:
Engine/Renderers/NPREdges.cc:
Engine/Renderers/NPREdges.h:
Engine/Renderers/CMakeLists.txt:

  Added the NPR edge renderer and plugged it.

Engine/Renderers/Raytracer.cc:

  Added some extra debug ray output.

Modified: trunk/Engine/Factory/RegisterKnownComponents.cc
==============================================================================
--- trunk/Engine/Factory/RegisterKnownComponents.cc     (original)
+++ trunk/Engine/Factory/RegisterKnownComponents.cc     Fri Dec 21 17:20:30 
2007
@@ -51,6 +51,7 @@
 #include <Engine/Renderers/KajiyaPathtracer.h>
 #include <Engine/Renderers/Moire.h>
 #include <Engine/Renderers/Noise.h>
+#include <Engine/Renderers/NPREdges.h>
 #include <Engine/Renderers/NullRenderer.h>
 #include <Engine/Renderers/RayGen.h>
 #include <Engine/Renderers/Raytracer.h>
@@ -124,6 +125,7 @@
     engine->registerComponent("moire", &Moire::create);
     engine->registerComponent("noise", &Noise::create);
     engine->registerComponent("raytracer", &Raytracer::create);
+    engine->registerComponent("edges", &NPREdges::create);
 
     // Register cameras
     //engine->registerComponent("environment", &EnvironmentCamera::create);

Modified: trunk/Engine/Renderers/CMakeLists.txt
==============================================================================
--- trunk/Engine/Renderers/CMakeLists.txt       (original)
+++ trunk/Engine/Renderers/CMakeLists.txt       Fri Dec 21 17:20:30 2007
@@ -6,6 +6,8 @@
      Renderers/Moire.cc
      Renderers/Noise.h
      Renderers/Noise.cc
+     Renderers/NPREdges.h
+     Renderers/NPREdges.cc
      Renderers/NullRenderer.h
      Renderers/NullRenderer.cc
      Renderers/RayGen.h

Added: trunk/Engine/Renderers/NPREdges.cc
==============================================================================
--- (empty file)
+++ trunk/Engine/Renderers/NPREdges.cc  Fri Dec 21 17:20:30 2007
@@ -0,0 +1,291 @@
+
+#include <Core/Exceptions/IllegalArgument.h>
+#include <Core/Util/Args.h>
+#include <Engine/Renderers/NPREdges.h>
+#include <Engine/Renderers/Raytracer.h>
+#include <Interface/Background.h>
+#include <Interface/Camera.h>
+#include <Interface/Context.h>
+#include <Interface/Material.h>
+#include <Interface/RayPacket.h>
+#include <Interface/Scene.h>
+
+#include <iostream>
+using namespace std;
+
+using namespace Manta;
+
+Renderer* NPREdges::create(const vector<string>& args)
+{
+  return new NPREdges(args);
+}
+
+NPREdges::NPREdges(const vector<string>& args)
+  : raytracer(new Raytracer),
+    imageX(0.0), imageY(0.0),
+    lineWidth(1.0), normalThreshold(0.0),
+    computeCreases(true)
+{
+  bool threshold_set = false;
+
+  for(size_t i=0; i<args.size(); i++){
+    const string& arg = args[i];
+    if(arg == "-width"){
+      if(!getArg(i, args, lineWidth))
+       throw IllegalArgument("NPREdges -width", i, args);
+    }
+    else if(arg == "-normal-threshold"){
+      Real val;
+      if(!getArg(i, args, val))
+       throw IllegalArgument("NPREdges -normal-threshold", i, args);
+
+      this->setNormalThreshold(val);
+      threshold_set = true;
+    }
+    else if(arg == "-nocrease"){
+      computeCreases = false;
+    }
+    else {
+      throw IllegalArgument("NPREdges", i, args);
+    }
+  }
+
+  if(!threshold_set)
+    this->setNormalThreshold(0.1);
+}
+
+NPREdges::~NPREdges()
+{
+  delete raytracer;
+}
+
+void NPREdges::setupBegin(const SetupContext& context, int)
+{
+}
+
+void NPREdges::setupDisplayChannel(SetupContext& context)
+{
+  bool stereo;
+  int x, y;
+  context.getResolution(stereo, x, y);
+
+  // image{X|Y} are the distnaces in screen coordinates corresponding
+  // to half of lineWidth pixel dimensions.  This is used as the
+  // one-sided stencil width.  The screen coordinates map to [-1,1],
+  // so we want lineWidth/2 times 2/res.
+  imageX = lineWidth/x;
+  imageY = lineWidth/y;
+}
+
+void NPREdges::setupFrame(const RenderContext&)
+{
+}
+
+void NPREdges::traceEyeRays(const RenderContext& context, RayPacket& rays)
+{
+  ASSERT(rays.getFlag(RayPacket::HaveImageCoordinates));
+  context.camera->makeRays(context, rays);
+  rays.initializeImportance();
+  traceRays(context, rays);
+}
+
+void NPREdges::traceRays(const RenderContext& context, RayPacket& rays)
+{
+  const int debugFlag = rays.getAllFlags() & RayPacket::DebugPacket;
+  if(debugFlag)
+    cerr << "debug ray" << endl;
+
+  rays.resetHits();
+  context.scene->getObject()->intersect(context, rays);
+
+  // Create parallel ray packets, each containing one member of a
+  // "surround" for each ray in the rays parameter.
+  RayPacketData surroundData[8];
+
+  RayPacket surrRight(surroundData[0], RayPacket::UnknownShape,
+                     rays.begin(), rays.end(), rays.getDepth(), 
RayPacket::HaveImageCoordinates);
+  RayPacket surrLeft(surroundData[1], RayPacket::UnknownShape,
+                    rays.begin(), rays.end(), rays.getDepth(), 
RayPacket::HaveImageCoordinates);
+  RayPacket surrUp(surroundData[2], RayPacket::UnknownShape,
+                  rays.begin(), rays.end(), rays.getDepth(), 
RayPacket::HaveImageCoordinates);
+  RayPacket surrDown(surroundData[3], RayPacket::UnknownShape,
+                    rays.begin(), rays.end(), rays.getDepth(), 
RayPacket::HaveImageCoordinates);
+
+  RayPacket *const surround[] = { &surrRight,
+                                 &surrLeft,
+                                 &surrUp,
+                                 &surrDown };
+
+  const unsigned surroundSize = sizeof(surround)/sizeof(surround[0]);
+
+  // If we need to do actual shading, we'll need to use a Raytracer,
+  // so create a temporary RenderContext.
+  RenderContext subContext(context.rtrt_int,
+                          context.channelIndex, context.proc, 
context.numProcs,
+                          context.frameState,
+                          context.loadBalancer, context.pixelSampler,
+                          raytracer, context.shadowAlgorithm,
+                          context.camera, context.scene,
+                          context.storage_allocator,
+                          context.rng,
+                          context.sample_generator);
+
+  // Populate the surround packets' image coordinates.
+  for(int i=rays.begin(); i<rays.end(); i++){
+    int eye = rays.getWhichEye(i);
+    Real xcoord = rays.getImageCoordinates(i, 0);
+    Real ycoord = rays.getImageCoordinates(i, 1);
+
+    surrLeft.setPixel(i, eye, xcoord-imageX, ycoord);
+    surrRight.setPixel(i, eye, xcoord+imageX, ycoord);
+    surrUp.setPixel(i, eye, xcoord, ycoord+imageY);
+    surrDown.setPixel(i, eye, xcoord, ycoord-imageY);
+  }
+
+  // Generate camera rays for the surround and trace them.
+  for(unsigned i=0; i<surroundSize; i++){
+    context.camera->makeRays(context, *surround[i]);
+    surround[i]->resetHits();
+    context.scene->getObject()->intersect(context, *surround[i]);
+  }
+
+  for(int i=rays.begin(); i<rays.end(); ){
+    switch(type(rays, surround, i)){
+    case Background:
+      {
+       int j=i+1;
+       while(j < rays.end() && type(rays, surround, j) == Background)
+         ++j;
+       RayPacket subPacket(rays, i, j);
+       context.scene->getBackground()->shade(subContext, subPacket);
+       i = j;
+      }
+      break;
+
+    case Shade:
+      {
+       const Primitive *hit = rays.getHitPrimitive(i);
+       const Material *matl = rays.getHitMaterial(i);
+       int j=i+1;
+       while(j < rays.end() && type(rays, surround, j) == Shade && 
rays.getHitPrimitive(j) == hit)
+         ++j;
+
+       if(!computeCreases){
+         RayPacket subPacket(rays, i, j);
+         matl->shade(subContext, subPacket);
+       }
+       else{
+         // Handle crease edges here.
+         RayPacket normRight(surrRight, i, j);
+         RayPacket normLeft(surrLeft, i, j);
+         RayPacket normUp(surrUp, i, j);
+         RayPacket normDown(surrDown, i, j);
+
+         normRight.computeFFNormals(context);
+         normLeft.computeFFNormals(context);
+         normUp.computeFFNormals(context);
+         normDown.computeFFNormals(context);
+
+         // Process the section of the ray packet between i and j -
+         // this section contains samples that all struck the same
+         // primitive.  The task is to find any crease line samples
+         // and mark them black, while shading the rest with the
+         // normal ray tracer.
+         int m=i, n=i+1;
+         while(m<j){
+           // This loop looks for a sequence of crease samples.
+           while(m<j){
+             Vector v1 = normRight.getNormal(m) - normLeft.getNormal(m);
+             Vector v2 = normUp.getNormal(m) - normDown.getNormal(m);
+       
+             if(v1.length2() + v2.length2() > normalThreshold){
+               rays.setColor(m, Color(RGBColor(0,0,0)));
+               ++m;
+             }
+             else{
+               break;
+             }
+           }
+
+           // If the stretch of crease samples reaches to the end of
+           // the sub packet, we are done.
+           if(m == j)
+             break;
+
+           // Start looking for a stretch of non-crease samples right
+           // after the crease samples.
+           n = m+1;
+           while(n<j){
+             Vector v1 = normRight.getNormal(n) - normLeft.getNormal(n);
+             Vector v2 = normUp.getNormal(n) - normDown.getNormal(n);
+
+             if(v1.length2() + v2.length2() > normalThreshold){
+               // When a crease sample is found, shade the non crease
+               // samples and break out of the loop.
+               rays.setColor(n, Color(RGBColor(0,0,0)));
+               RayPacket subPacket(rays, m, n);
+               matl->shade(subContext, subPacket);
+               break;
+             }
+             else{
+               ++n;
+             }
+           }
+
+           // If we reached the end of the sub packet, shade the
+           // samples.  The loop condition will break us out of this
+           // loop.
+           if(n == j){
+             RayPacket subPacket(rays, m, n);
+             matl->shade(subContext, subPacket);
+           }
+
+           m = n+1;
+         }
+       }
+
+       i = j;
+      }
+      break;
+
+    case Intersection:
+      {
+       rays.setColor(i, Color(RGBColor(0,0,0)));
+       ++i;
+      }
+      break;
+    }
+  }
+}       
+
+void NPREdges::traceRays(const RenderContext& context, RayPacket& rays, Real)
+{
+  traceRays(context, rays);
+}
+
+NPREdges::PixelType NPREdges::type(const RayPacket& sample, const RayPacket 
*const surround[], int i) const {
+  if(sample.wasHit(i)){
+    // The sample ray hit a primitive; check if this is an intersection 
sample.
+    const Primitive *hit = sample.getHitPrimitive(i);
+    for(int j=0; j<4; j++)
+      if(!(surround[j]->wasHit(i) && (surround[j]->getHitPrimitive(i) == 
hit)))
+       return Intersection;
+  }
+  else{
+    // The sample ray struck the background; check if this is an 
intersection sample.
+    for(int j=0; j<4; j++)
+      if(surround[j]->wasHit(i))
+       return Intersection;
+    
+    // If all the surround rays strike the background, this is a background 
sample.
+    return Background;
+  }
+
+  // If we reach here, the sample ray struck a primitive, and so did
+  // all the surround rays.
+  return Shade;
+}
+
+void NPREdges::setNormalThreshold(Real val){
+  normalThreshold = val*val*lineWidth*lineWidth;
+}

Added: trunk/Engine/Renderers/NPREdges.h
==============================================================================
--- (empty file)
+++ trunk/Engine/Renderers/NPREdges.h   Fri Dec 21 17:20:30 2007
@@ -0,0 +1,55 @@
+
+#ifndef Manta_Engine_NPREdges_h
+#define Manta_Engine_NPREdges_h
+
+#include <Interface/Renderer.h>
+
+#include <string>
+#include <vector>
+
+using namespace Manta;
+
+namespace Manta {
+  class Raytracer;
+
+  using namespace std;
+
+  class NPREdges : public Renderer {
+  public:
+    NPREdges() {}
+    NPREdges(const vector<string>& args);
+    virtual ~NPREdges();
+    virtual void setupBegin(const SetupContext&, int numChannels);
+    virtual void setupDisplayChannel(SetupContext&);
+    virtual void setupFrame(const RenderContext& context);
+
+    virtual void traceEyeRays(const RenderContext&, RayPacket& rays);
+    virtual void traceRays(const RenderContext&, RayPacket& rays);
+    virtual void traceRays(const RenderContext&, RayPacket& rays, Real 
cutoff);
+
+    static Renderer* create(const vector<string>& args);
+
+    void setNormalThreshold(Real val);
+
+  protected:
+    typedef enum {
+      Background,
+      Shade,
+      Intersection
+    } PixelType ;
+
+    PixelType type(const RayPacket& sample, const RayPacket *const 
surround[], int i) const;
+
+    Raytracer *raytracer;
+
+    Real imageX, imageY;
+    Real lineWidth, normalThreshold;
+    bool computeCreases;
+
+  private:
+    NPREdges(const NPREdges&);
+    NPREdges& operator=(const NPREdges&);
+  };
+}
+
+#endif

Modified: trunk/Engine/Renderers/Raytracer.cc
==============================================================================
--- trunk/Engine/Renderers/Raytracer.cc (original)
+++ trunk/Engine/Renderers/Raytracer.cc Fri Dec 21 17:20:30 2007
@@ -85,6 +85,7 @@
                << " depth " << rays.getDepth()
                << " origin "<< rays.getOrigin(j)
                << " direction "<< rays.getDirection(j)
+              << " image coords ("<< rays.getImageCoordinates(i, 0) << ", " 
<< rays.getImageCoordinates(i, 1) << ")"
                << "\n";
         }
       }

Modified: trunk/Interface/RayPacket.h
==============================================================================
--- trunk/Interface/RayPacket.h (original)
+++ trunk/Interface/RayPacket.h Fri Dec 21 17:20:30 2007
@@ -596,15 +596,14 @@
       return hitFinal(start, mask, t, matl, prim, tex);
     }
     // No mask
-    inline __m128 hit(int start, __m128 t, const Material* matl,
-                                                                             
           const Primitive* prim, const TexCoordMapper* tex) {
+    inline __m128 hit(int start, __m128 t, const Material* matl, const 
Primitive* prim, const TexCoordMapper* tex) {
       __m128 mask = _mm_cmpgt_ps(t, _mm_set1_ps(T_EPSILON));
       return hitFinal(start, mask, t, matl, prim, tex);
     }
 
 #endif // #ifdef MANTA_SSE
 
-    bool wasHit(int which)
+    bool wasHit(int which) const
     {
       return data->hitMatl[which] != 0;
     }

Modified: trunk/scenes/CMakeLists.txt
==============================================================================
--- trunk/scenes/CMakeLists.txt (original)
+++ trunk/scenes/CMakeLists.txt Fri Dec 21 17:20:30 2007
@@ -87,6 +87,14 @@
    TARGET_LINK_LIBRARIES(scene_triangleSceneViewer ${MANTA_SCENE_LINK})
 ENDIF(SCENE_TRIANGLESCENEVIEWER)
 
+# Tylenol (acetaminophen aka paracetamol) molecule with CPK coloring.
+# Test silhouette edge and intersection edge rendering.
+SET(SCENE_TYLENOL 0 CACHE BOOL "tylenol molecule")
+IF(SCENE_TYLENOL)
+   ADD_LIBRARY(scene_tylenol tylenol.cc)
+   TARGET_LINK_LIBRARIES(scene_tylenol ${MANTA_SCENE_LINK})
+ENDIF(SCENE_TYLENOL)
+
 # Lazily evaluated LTs for NRRD particle datasets
 IF(BUILD_DYNLT)
    ADD_LIBRARY(scene_dynlt dynlt.cc)

Added: trunk/scenes/tylenol.cc
==============================================================================
--- (empty file)
+++ trunk/scenes/tylenol.cc     Fri Dec 21 17:20:30 2007
@@ -0,0 +1,106 @@
+#include <Core/Color/ColorDB.h>
+#include <Core/Util/Preprocessor.h>
+#include <Interface/LightSet.h>
+#include <Interface/Scene.h>
+#include <Model/AmbientLights/ConstantAmbient.h>
+#include <Model/Backgrounds/ConstantBackground.h>
+#include <Model/Groups/Group.h>
+#include <Model/Lights/HeadLight.h>
+#include <Model/Lights/PointLight.h>
+#include <Model/Materials/Lambertian.h>
+#include <Model/Primitives/Sphere.h>
+
+#include <iostream>
+#include <map>
+#include <string>
+
+using namespace Manta;
+
+class AtomNotFound{
+public:
+  AtomNotFound(const std::string& name) : name(name) {}
+  std::string name;
+};
+
+struct atom{
+  atom() : vdw_radius(0), r(0), g(0), b(0) {}
+
+  atom(float vdw_radius, float r, float g, float b) :
+    vdw_radius(vdw_radius), r(r), g(b), b(b) {}
+
+  float vdw_radius; // van der waals radius
+  float r, g, b; // CPK color
+};
+
+typedef std::map<std::string, atom> atom_table;
+static atom_table atom_data;
+
+Sphere *new_atom(const std::string& name, float x, float y, float z){
+  // Place atom named by `name' at position (x,y,z), measured in any
+  // units, but Angstroms for PDB database.
+  atom_table::const_iterator i = atom_data.find(name);
+  if(i == atom_data.end())
+    throw AtomNotFound(name);
+
+  const atom& A = i->second;
+
+  return new Sphere(new Lambertian(Color(RGBColor(A.r,A.g,A.b))),
+                   Vector(x,y,z), A.vdw_radius);
+}
+
+MANTA_PLUGINEXPORT
+Scene *make_scene(const ReadContext&, const vector<string>& args)
+{
+  // Create atom data.  Radius data taken from
+  // http://en.wikipedia.org/wiki/Van_der_Waals_radius, accessed
+  // 2007/12/13.  Color data taken from
+  // http://jmol.sourceforge.net/jscolors/, accessed same day.
+  try{
+    atom_data["C"] = atom(1.7, 0.565,0.565,0.565);
+    atom_data["H"] = atom(1.20,1.000,1.000,1.000);
+    atom_data["O"] = atom(1.52,1.000,0.051,0.051);
+    atom_data["N"] = atom(1.55,0.188,0.314,0.973);
+  }
+
+  catch(AtomNotFound& anf){
+    std::cerr << "error: atom `" << anf.name << "' not in atom database." << 
std::endl;
+    return 0;
+  }
+
+  Group *group = new Group;
+
+  // Molecule geometry data taken from
+  // 
http://redpoll.pharmacy.ualberta.ca/drugbank/drugBank/drugPDB_unzipped/APRD00252_PDB.txt
+  group->add(new_atom("C", 3.979, 0.149, 0.137));
+  group->add(new_atom("C", 2.516, -0.124, -0.100));
+  group->add(new_atom("O", 2.172, -1.145, -0.657));
+  group->add(new_atom("N", 1.590, 0.767, 0.306));
+  group->add(new_atom("C", 0.227, 0.462, 0.192));
+  group->add(new_atom("C", -0.217, -0.833, 0.424));
+  group->add(new_atom("C", -1.560, -1.132, 0.312));
+  group->add(new_atom("C", -2.466, -0.140, -0.034));
+  group->add(new_atom("C", -2.023, 1.154, -0.266));
+  group->add(new_atom("C", -0.681, 1.456, -0.149));
+  group->add(new_atom("O", -3.788, -0.436, -0.144));
+  group->add(new_atom("H", 4.572, -0.677, -0.257));
+  group->add(new_atom("H", 4.263, 1.073, -0.367));
+  group->add(new_atom("H", 4.161, 0.249, 1.207));
+  group->add(new_atom("H", 1.867, 1.619, 0.678));
+  group->add(new_atom("H", 0.489, -1.605, 0.693));
+  group->add(new_atom("H", -1.905, -2.140, 0.492));
+  group->add(new_atom("H", -2.728, 1.925, -0.536));
+  group->add(new_atom("H", -0.336, 2.463, -0.330));
+  group->add(new_atom("H", -4.180, -0.295, 0.728));
+            
+  Scene *scene = new Scene;
+  scene->setBackground(new ConstantBackground(Color(RGBColor(0.6,0.6,0.6))));
+  scene->setObject(group);
+
+  LightSet *lights = new LightSet;
+  //lights->add(new PointLight(Vector(0,10,0), Color(RGBColor(1,1,1))));
+  lights->add(new HeadLight(3, Color(RGBColor(1,1,1))));
+  lights->setAmbientLight(new 
ConstantAmbient(ColorDB::getNamedColor("black")));
+  scene->setLights(lights);
+  
+  return scene;
+}





Archive powered by MHonArc 2.6.16.

Top of page