Manta Interactive Ray Tracer Development Mailing List

Text archives Help


[Manta] r2038 - trunk/Engine/Renderers


Chronological Thread 
  • From: roni@sci.utah.edu
  • To: manta@sci.utah.edu
  • Subject: [Manta] r2038 - trunk/Engine/Renderers
  • Date: Wed, 6 Feb 2008 17:10:51 -0700 (MST)

Author: roni
Date: Wed Feb  6 17:10:51 2008
New Revision: 2038

Modified:
   trunk/Engine/Renderers/NPREdges.cc
   trunk/Engine/Renderers/NPREdges.h
Log:
Different algorithm for detecting intersection and silhouette lines
that incorporates anti-aliasing for line drawing.

New option: -N n.  Will use a regular nxn grid of "surround" samples
to test for strength of edge about a given sample.  The default is
n=2, which gives good lines without a huge slowdown.  n=4 gives much
nicer lines, but at a higher performance cost.


Modified: trunk/Engine/Renderers/NPREdges.cc
==============================================================================
--- trunk/Engine/Renderers/NPREdges.cc  (original)
+++ trunk/Engine/Renderers/NPREdges.cc  Wed Feb  6 17:10:51 2008
@@ -1,6 +1,6 @@
-
 #include <Core/Exceptions/IllegalArgument.h>
 #include <Core/Util/Args.h>
+#include <Core/Util/Preprocessor.h>
 #include <Engine/Renderers/NPREdges.h>
 #include <Engine/Renderers/Raytracer.h>
 #include <Interface/Background.h>
@@ -9,6 +9,8 @@
 #include <Interface/Material.h>
 #include <Interface/RayPacket.h>
 #include <Interface/Scene.h>
+#include <Model/Primitives/Cube.h>
+#include <Model/Primitives/QuadFacedHexahedron.h>
 
 #include <iostream>
 using namespace std;
@@ -23,9 +25,10 @@
 NPREdges::NPREdges(const vector<string>& args)
   : raytracer(new Raytracer),
     imageX(0.0), imageY(0.0),
-    lineWidth(1.0), normalThreshold(0.0),
+    lineWidth(2.0), normalThreshold(0.0),
     computeCreases(true),
-    noshade(false)
+    noshade(false),
+    N(2)
 {
   bool threshold_set = false;
 
@@ -49,6 +52,10 @@
     else if(arg == "-noshade"){
       noshade = true;
     }
+    else if(arg == "-N"){
+      if(!getArg(i, args, N))
+        throw IllegalArgument("NPREdges -N", i, args);
+    }
     else {
       throw IllegalArgument("NPREdges", i, args);
     }
@@ -74,11 +81,11 @@
   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;
+  // to lineWidth pixels.  This is used as the stencil width.  The
+  // screen coordinates map to [-1,1], so we want lineWidth times
+  // 2/res.
+  imageX = 2*lineWidth/x;
+  imageY = 2*lineWidth/y;
 }
 
 void NPREdges::setupFrame(const RenderContext&)
@@ -116,54 +123,66 @@
 
   // Create parallel ray packets, each containing one member of a
   // "surround" for each ray in the rays parameter.
-  RayPacketData surroundData[4];
-
-  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 };
+  RayPacketData *stencil_data = MANTA_STACK_ALLOC(RayPacketData, N*N);
+  RayPacket *stencil = MANTA_STACK_ALLOC(RayPacket, N*N);
 
-  const unsigned surroundSize = sizeof(surround)/sizeof(surround[0]);
+  // Each ray in each stencil raypacket is at a constant offset from
+  // the rays in the sample raypacket, so they can inherit the shape
+  // flag.
+  for(int i=0; i<N*N; i++){
+    new (stencil_data+i)RayPacketData;
+    new (stencil+i)RayPacket(stencil_data[i], rays.shape, rays.begin(), 
rays.end(), rays.getDepth(), RayPacket::HaveImageCoordinates);
+  }
 
   // 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);
+    // (low_x, low_y) is the bottom left of a box of size (imageX,
+    // imageY), with the sample ray intersecting it in the center.
+    const Real inc_x = imageX / N;
+    const Real inc_y = imageY / N;
+    const Real low_x = rays.getImageCoordinates(i, 0) - 0.5*imageX + 
0.5*inc_x;
+    const Real low_y = rays.getImageCoordinates(i, 1) - 0.5*imageY + 
0.5*inc_y;
+
+    if(debugFlag){
+      std::cout << "sample ray: (" << rays.getImageCoordinates(i, 0) << ", " 
<< rays.getImageCoordinates(i, 1) << ")" << std::endl;
+    }
+
+    for(int j=0; j<N; j++){
+      const Real ypos = low_y + j*inc_y;
+      for(int k=0; k<N; k++){
+        stencil[j*N+k].setPixel(i, eye, low_x + k*inc_x, ypos);
+
+        if(debugFlag){
+          std::cout << "stencil ray (" << j << ", " << k << "): (" << (low_x 
+ k*inc_x) << ", " << ypos << ")" << std::endl;
+        }
+      }
+    }
   }
 
   // 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=0; i<N*N; i++){
+    context.camera->makeRays(context, stencil[i]);
+    stencil[i].resetHits();
+    context.scene->getObject()->intersect(context, stencil[i]);
   }
 
-  for(int i=rays.begin(); i<rays.end(); i++){
+  // TODO(choudhury): amortize the calls to computeFFNormal in the
+  // Shade case by looking for a run of Shade cases in the ray packet.
+  for(int i=rays.begin(); i<rays.end(); ){
     int count;
 
-    switch(type(rays, surround, i, count)){
+    switch(type(rays, stencil, i, count)){
     case Intersection:
       {
         if(debugFlag)
           cerr << "count was " << count << endl;
 
-        float lightness = abs(count - 2) / 2.0;
+        float lightness = Abs(count - N*N/2.0) / (N*N/2.0);
         rays.setColor(i, rays.getColor(i)*lightness);
+
+        ++i;
       }
       break;
 
@@ -171,40 +190,63 @@
       {
         if(debugFlag)
           cerr << "Ray is background all-surround" << endl;
+
+        ++i;
       }
       break;
 
     case Shade:
       {
-        if(debugFlag)
+        if(debugFlag){
           cerr << "Ray is primitive all-surround" << endl;
+        }
 
+        // Find a run of Shade samples.
+        int j;
+        for(j=i+1; j<rays.end(); j++)
+          if(type(rays, stencil, j) != Shade)
+            break;
+
+        // TODO(choudhury): this procedure only works properly for
+        // square pixels (i.e., for imageX == imageY).  I think with
+        // the fov stuff that keeps the image from distorting this is
+        // the case but need to make sure.
+        //
         // Check for crease line.
-        RayPacket normRight(surrRight, i, i+1);
-        RayPacket normLeft(surrLeft, i, i+1);
-        RayPacket normUp(surrUp, i, i+1);
-        RayPacket normDown(surrDown, i, i+1);
+        RayPacket normRight(stencil[N-1], i, j);
+        RayPacket normLeft(stencil[N*(N-1)], i, j);
+        RayPacket normUp(stencil[N*N-1], i, j);
+        RayPacket normDown(stencil[0], i, j);
 
         normRight.computeFFNormals(context);
         normLeft.computeFFNormals(context);
         normUp.computeFFNormals(context);
         normDown.computeFFNormals(context);
 
-        const Vector v1 = normRight.getFFNormal(i) - normLeft.getFFNormal(i);
-        const Vector v2 = normUp.getFFNormal(i) - normDown.getFFNormal(i);
-
-        if(debugFlag){
-          cerr << "right normal: " << normRight.getFFNormal(i) << endl
-               << "left normal: " << normLeft.getFFNormal(i) << endl
-               << "up normal: " << normUp.getFFNormal(i) << endl
-               << "down normal: " << normDown.getFFNormal(i) << endl
-               << "v1 (right minus left): " << v1 << endl
-               << "v2 (right minus left): " << v2 << endl;
+        for(int k=i; k<j; k++){
+          // TODO(choudhury): check if the primitive is an instance of
+          // one of the creased-primitive types.
+          if(!(typeid(*rays.getHitPrimitive(k)) == typeid(Cube) || 
typeid(*rays.getHitPrimitive(k)) == typeid(QuadFacedHexahedron)))
+            continue;
+
+          const Vector v1 = normRight.getFFNormal(k) - 
normLeft.getFFNormal(k);
+          const Vector v2 = normUp.getFFNormal(k) - normDown.getFFNormal(k);
+
+          if(debugFlag){
+            cerr << "right normal: " << normRight.getFFNormal(k) << endl
+                 << "left normal: " << normLeft.getFFNormal(k) << endl
+                 << "up normal: " << normUp.getFFNormal(k) << endl
+                 << "down normal: " << normDown.getFFNormal(k) << endl
+                 << "v1 (right minus left): " << v1 << endl
+                 << "v2 (right minus left): " << v2 << endl;
+          }
+
+          if(v1.length2() + v2.length2() > normalThreshold){
+            rays.setColor(k, Color::black());
+          }
         }
 
-        if(v1.length2() + v2.length2() > normalThreshold){
-          rays.setColor(i, Color::black());
-        }
+        i = j;
       }
       break;
     }
@@ -216,57 +258,63 @@
   traceRays(context, rays);
 }
 
-NPREdges::PixelType NPREdges::type(const RayPacket& sample, const RayPacket 
*const surround[], int i) const {
+NPREdges::PixelType NPREdges::type(const RayPacket& sample, const RayPacket 
*stencil, 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)))
+    for(int j=0; j<N*N; j++)
+      if(!(stencil[j].wasHit(i) && (stencil[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))
+    for(int j=0; j<N*N; j++)
+      if(stencil[j].wasHit(i))
        return Intersection;
     
-    // If all the surround rays strike the background, this is a background 
sample.
+    // If all the stencil 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.
+  // all the stencil rays.
   return Shade;
 }
 
-NPREdges::PixelType NPREdges::type(const RayPacket& sample, const RayPacket 
*const surround[], int i, int& count) const {
+NPREdges::PixelType NPREdges::type(const RayPacket& sample, const RayPacket 
*stencil, int i, int& count) const {
+  const int debugFlag = sample.getAllFlags() & RayPacket::DebugPacket;
+
   if(sample.wasHit(i)){
     // The sample ray hit a primitive; check if this is an intersection 
sample.
     count = 0;
     const Primitive *hit = sample.getHitPrimitive(i);
-    for(int j=0; j<4; j++)
-      if(!(surround[j]->wasHit(i) && (surround[j]->getHitPrimitive(i) == 
hit))){
+    for(int j=0; j<N*N; j++)
+      if(!(stencil[j].wasHit(i) && (stencil[j].getHitPrimitive(i) == hit))){
         ++count;
       }
+
+    if(debugFlag)
+      std::cout << "count = " << count << std::endl;
+
     if(count > 0)
       return Intersection;
   }
   else{
     // The sample ray struck the background; check if this is an 
intersection sample.
     count = 0;
-    for(int j=0; j<4; j++)
-      if(surround[j]->wasHit(i))
+    for(int j=0; j<N*N; j++)
+      if(stencil[j].wasHit(i))
         ++count;
 
     if(count > 0)
       return Intersection;
     
-    // If all the surround rays strike the background, this is a background 
sample.
+    // If all the stencil 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.
+  // all the stencil rays.
   return Shade;
 }
 

Modified: trunk/Engine/Renderers/NPREdges.h
==============================================================================
--- trunk/Engine/Renderers/NPREdges.h   (original)
+++ trunk/Engine/Renderers/NPREdges.h   Wed Feb  6 17:10:51 2008
@@ -1,4 +1,3 @@
-
 #ifndef Manta_Engine_NPREdges_h
 #define Manta_Engine_NPREdges_h
 
@@ -38,14 +37,16 @@
       Intersection
     } PixelType ;
 
-    PixelType type(const RayPacket& sample, const RayPacket *const 
surround[], int i) const;
-    PixelType type(const RayPacket& sample, const RayPacket *const 
surround[], int i, int& count) const;
+    PixelType type(const RayPacket& sample, const RayPacket *stencil, int i) 
const;
+    PixelType type(const RayPacket& sample, const RayPacket *stencil, int i, 
int& count) const;
 
     Raytracer *raytracer;
 
     Real imageX, imageY;
     Real lineWidth, normalThreshold;
     bool computeCreases, noshade;
+
+    int N;
 
   private:
     NPREdges(const NPREdges&);




  • [Manta] r2038 - trunk/Engine/Renderers, roni, 02/06/2008

Archive powered by MHonArc 2.6.16.

Top of page