Text archives Help
- 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.