希望每个人都很好。我在从 pbrt v2 实现 kd 树时遇到了一点问题。它很快,但渲染后模型上的所有法线都是错误的。有趣的是,如果我更改 pMin 和 pMax 中的值边界框构造函数,我得到了具有正确法线的正确模型,但渲染时间要长得多。我不知道是什么原因造成的。顺便说一下,pbrt v2 使用左手坐标系,而我使用右手坐标系系统。我尝试将坐标更改为左手系统,并从左手系统而不是右手系统导入模型,但问题仍然存在。在某一时刻它有效,但模型位置颠倒了。最好向您展示我的代码,以便您更好地理解我的困境。
*边界框代码*
class BBox
{
public:
Point pMin,pMax;
BBox(){
pMin = Point(INFINITY,INFINITY,INFINITY);
pMax = Point(-INFINITY,-INFINITY,-INFINITY);
}
BBox(const Point& p) : pMin(p),pMax(p) { }
BBox(const Point& p1,const Point& p2) {
pMin = Point(min(p1.x,p2.x),min(p1.y,p2.y),min(p1.z,p2.z));
pMax = Point(max(p1.x,p2.x),max(p1.y,p2.y),max(p1.z,p2.z));
}
Point boxCentroid() const {
float x = (pMin.x+pMax.x)/2;
float y = (pMin.y+pMax.y)/2;
float z = (pMin.z+pMax.z)/2;
return Point(x,y,z);
}
BBox Union(const BBox &b,const Point& p) {
BBox ret = b;
ret.pMin.x = min(b.pMin.x,p.x);
ret.pMin.y = min(b.pMin.y,p.y);
ret.pMin.z = min(b.pMin.z,p.z);
ret.pMax.x = max(b.pMax.x,p.x);
ret.pMax.y = max(b.pMax.y,p.y);
ret.pMax.z = max(b.pMax.z,p.z);
return ret;
}
friend BBox Union(const BBox& b,const BBox& b2){
BBox ret;
ret.pMin.x = min(b.pMin.x, b2.pMin.x);
ret.pMin.y = min(b.pMin.y, b2.pMin.y);
ret.pMin.z = min(b.pMin.z, b2.pMin.z);
ret.pMax.x = max(b.pMax.x, b2.pMax.x);
ret.pMax.y = max(b.pMax.y, b2.pMax.y);
ret.pMax.z = max(b.pMax.z, b2.pMax.z);
return ret;
}
bool Overlaps(const BBox &b) const {
bool x = (pMax.x >= b.pMin.x) && (pMin.x <= b.pMax.x);
bool y = (pMax.y >= b.pMin.y) && (pMin.y <= b.pMax.y);
bool z = (pMax.z >= b.pMin.z) && (pMin.z <= b.pMax.z);
return (x && y && z);
}
bool Inside(const Point& pt) const {
return (pt.x >= pMin.x && pt.x <= pMax.x &&
pt.y >= pMin.y && pt.y <= pMax.y &&
pt.z >= pMin.z && pt.z <= pMax.z);
}
void Expand(float delta) {
pMin -= Vector(delta,delta,delta);
pMax += Vector(delta,delta,delta);
}
float SurfaceArea() const {
Vector d = pMax - pMin;
return 2.f * (d.x*d.y + d.x*d.z + d.y*d.z);
}
float Volume() const {
Vector d = pMax - pMin;
return d.x*d.y*d.z;
}
int MaximumExtent() const {
Vector diag = pMax - pMin;
if (diag.x > diag.y && diag.x > diag.z)
return 0;
else if (diag.y > diag.z)
return 1;
else
return 2;
}
const Point &operator[](int i) const {
//Assert(i == 0 || i == 1);
return (&pMin)[i];
}
Point &operator[](int i) {
//Assert(i == 0 || i == 1);
return (&pMin)[i];
}
Point Lerp(float tx, float ty, float tz) const {
return Point(::Lerp(tx, pMin.x, pMax.x), ::Lerp(ty, pMin.y, pMax.y),
::Lerp(tz, pMin.z, pMax.z));
}
Vector Offset(const Point &p) const {
return Vector((p.x - pMin.x) / (pMax.x - pMin.x),
(p.y - pMin.y) / (pMax.y - pMin.y),
(p.z - pMin.z) / (pMax.z - pMin.z));
}
void BBox::BoundingSphere(Point *c, float *rad) const {
*c = pMin * 0.5f + pMax * 0.5f;
*rad = Inside(*c) ? Distance(*c, pMax) : 0.f;
}
bool IntersectP(const Ray &ray,float *hitt0,float *hitt1) const {
float t0 = 0.00001f, t1 = INFINITY;
for (int i = 0; i < 3; ++i) {
// Update interval for _i_th bounding box slab
float invRayDir = 1.f / ray.d[i];
float tNear = (pMin[i] - ray.o[i]) * invRayDir;
float tFar = (pMax[i] - ray.o[i]) * invRayDir;
// Update parametric interval from slab intersection $t$s
if (tNear > tFar) swap(tNear, tFar);
t0 = tNear > t0 ? tNear : t0;
t1 = tFar < t1 ? tFar : t1;
if (t0 > t1) return false;
}
if (hitt0) *hitt0 = t0;
if (hitt1) *hitt1 = t1;
return true;
}
};
如果我使用具有上述 pMin 和 pMax 值的当前默认 BBox 构造函数。我得到这个
但是,如果我将其更改为
pMin = Point(-INFINITY,-INFINITY,-INFINITY);
pMax = Point(INFINITY,INFINITY,INFINITY);
我得到了正确的模型。这个...
但这需要更长的时间。(7秒到51分钟)你看到问题了吗???无论如何我需要帮助。感谢任何帮助。谢谢这是其余必要的代码(KD-TREE)和(CAMERA)
*KD树代码*
struct BoundEdge
{
//BoundEdge Methods
BoundEdge() { }
BoundEdge(float tt,int pn,bool starting){
t = tt;
primNum = pn;
type = starting ? START : END;
}
bool operator<(const BoundEdge &e) const {
if (t == e.t)
return (int)type < (int)e.type;
else return t < e.t;
}
float t;
int primNum;
enum { START,END } type;
};
struct KdAccelNode
{
void initLeaf(uint32_t *primNums,int np,MemoryArena &arena);
void initInterior(uint32_t axis,uint32_t ac,float s)
{
split = s;
flags = axis;
aboveChild |= (ac<<2);
}
float SplitPos() const { return split; }
uint32_t nPrimitives() const { return nPrims >> 2; }
uint32_t SplitAxis() const { return flags & 3; }
bool isLeaf() const { return (flags & 3)==3; }
uint32_t AboveChild() const { return aboveChild >> 2; }
union{
float split;
uint32_t onePrimitive;
uint32_t *primitives;
};
private:
union{
uint32_t flags;
uint32_t nPrims;
uint32_t aboveChild;
};
};
struct KdToDo{
const KdAccelNode *node;
float tMIN,tMAX;
};
class KdTreeAccel : public Primitive
{
public:
KdTreeAccel(const vector<Primitive*> &p,int icost,int tcost,float ebonus,int maxp,int md)
:primitives(p),isectCost(icost),traversalCost(tcost),emptyBonus(ebonus),maxPrims(maxp),maxDepth(md)
{
//Build Kd-Tree
nextFreeNode = nAllocedNodes = 0;
if(maxDepth <= 0)
maxDepth = Round2Int(8+1.3f * Log2Int(float(primitives.size())));
//<Compute Bounds>
vector<BBox> primBounds;
primBounds.reserve(primitives.size());
for(uint32_t i=0;i<primitives.size();++i)
{
BBox b = primitives[i]->BoxBound();
bounds = Union(bounds,b);
primBounds.push_back(b);
}
//<Allocate Working Memory>
BoundEdge *edges[3];
for(int i=0;i<3;++i)
edges[i] = new BoundEdge[2*primitives.size()];
uint32_t *prims0 = new uint32_t[primitives.size()];
uint32_t *prims1 = new uint32_t[(maxDepth+1) * primitives.size()];
//<Initialize primNums
uint32_t *primNums = new uint32_t[primitives.size()];
for(uint32_t i=0;i<primitives.size();++i)
primNums[i] = i;
//<Start Recursive Construction
buildTree(0,bounds,primBounds,primNums,primitives.size(),maxDepth,edges,prims0,prims1);
//<Free Memory
delete[] primNums;
for (int i = 0; i < 3; ++i)
delete[] edges[i];
delete[] prims0;
delete[] prims1;
}
virtual bool intersect(const Ray& r,float t_min,float t_max,primitive_record &rec) const;
virtual bool intersect_p(const Ray& r,float t_min,float t_max) const;
virtual float area() const;
virtual Vector _normal(const Point& in_point) const;
virtual Point randomSamplePoint() const;
virtual BBox BoxBound() const ;
virtual Point centroid() const ;
private:
void buildTree(int nodeNum,const BBox &nodeBounds,const vector<BBox> &allPrimBounds,uint32_t *primNums,
int nPrimitives,int depth,BoundEdge *edges[3],uint32_t *prims0,uint32_t *prims1,
int badRefines=0);
int isectCost,traversalCost,maxPrims,maxDepth;
float emptyBonus;
vector<Primitive*> primitives;
KdAccelNode *nodes;
int nAllocedNodes,nextFreeNode;
BBox bounds;
MemoryArena arena;
};
void KdAccelNode::initLeaf(uint32_t *primNums,int np,MemoryArena &arena)
{
flags = 3;
nPrims |= (np<<2);
//Store primitive id for leaf node
if(np == 0)
onePrimitive = 0;
else if(np == 1)
onePrimitive = primNums[0];
else{
primitives = arena.Alloc<uint32_t>(np);
for(int i=0;i<np;++i)
primitives[i] = primNums[i];
}
}
void KdTreeAccel::buildTree(int nodeNum,const BBox &nodeBounds,const vector<BBox> &allPrimBounds,uint32_t *primNums,
int nPrimitives,int depth,BoundEdge *edges[3],uint32_t *prims0,uint32_t *prims1,
int badRefines)
{
//Get Next Free Node
assert(nodeNum == nextFreeNode);
if(nextFreeNode == nAllocedNodes){
int nAlloc = max(2*nAllocedNodes,512);
KdAccelNode *n = AllocAligned<KdAccelNode>(nAlloc);
if(nAllocedNodes > 0){
memcpy(n,nodes,nAllocedNodes*sizeof(KdAccelNode));
FreeAligned(nodes);
}
nodes = n;
nAllocedNodes = nAlloc;
}
++nextFreeNode;
//Initialize leaf Node
if(nPrimitives <= maxPrims || depth == 0){
nodes[nodeNum].initLeaf(primNums,nPrimitives,arena);
return;
}
//Initialize Interior Node
//--Choose split axis
int bestAxis = -1, bestOffset = -1;
float bestCost = INFINITY;
float oldCost = isectCost * float(nPrimitives);
float totalSA = nodeBounds.SurfaceArea();
float invTotalSA = 1.f / totalSA;
Vector d = nodeBounds.pMax - nodeBounds.pMin;
uint32_t axis = nodeBounds.MaximumExtent();
int retries = 0;
retrySplit:
for (int i = 0; i < nPrimitives; ++i) {
int pn = primNums[i];
const BBox &bbox = allPrimBounds[pn];
edges[axis][2*i] = BoundEdge(bbox.pMin[axis], pn, true);
edges[axis][2*i+1] = BoundEdge(bbox.pMax[axis], pn, false);
}
sort(&edges[axis][0], &edges[axis][2*nPrimitives]);
int nBelow = 0, nAbove = nPrimitives;
for (int i = 0; i < 2*nPrimitives; ++i) {
if (edges[axis][i].type == BoundEdge::END) --nAbove;
float edget = edges[axis][i].t;
if (edget > nodeBounds.pMin[axis] &&
edget < nodeBounds.pMax[axis]) {
//Compute cost for split at ith edge
uint32_t otherAxis0 = (axis + 1) % 3, otherAxis1 = (axis + 2) % 3;
float belowSA = 2 * (d[otherAxis0] * d[otherAxis1] +
(edget - nodeBounds.pMin[axis]) *
(d[otherAxis0] + d[otherAxis1]));
float aboveSA = 2 * (d[otherAxis0] * d[otherAxis1] +
(nodeBounds.pMax[axis] - edget) *
(d[otherAxis0] + d[otherAxis1]));
float pBelow = belowSA * invTotalSA;
float pAbove = aboveSA * invTotalSA;
float eb = (nAbove == 0 || nBelow == 0) ? emptyBonus : 0.f;
float cost = traversalCost +
isectCost * (1.f - eb) * (pBelow * nBelow + pAbove * nAbove);
if (cost < bestCost) {
bestCost = cost;
bestAxis = axis;
bestOffset = i;
}
}
if (edges[axis][i].type == BoundEdge::START) ++nBelow;
}
assert(nBelow == nPrimitives && nAbove == 0);
//--Create leaf if np good splits found
if (bestAxis == -1 && retries < 2) {
++retries;
axis = (axis+1) % 3;
goto retrySplit;
}
if (bestCost > oldCost) ++badRefines;
if ((bestCost > 4.f * oldCost && nPrimitives < 16) ||
bestAxis == -1 || badRefines == 3) {
nodes[nodeNum].initLeaf(primNums, nPrimitives, arena);
return;
}
//--Classify primitives with respect to split
int n0 = 0, n1 = 0;
for (int i = 0; i < bestOffset; ++i)
if (edges[bestAxis][i].type == BoundEdge::START)
prims0[n0++] = edges[bestAxis][i].primNum;
for (int i = bestOffset+1; i < 2*nPrimitives; ++i)
if (edges[bestAxis][i].type == BoundEdge::END)
prims1[n1++] = edges[bestAxis][i].primNum;
//--Recursively initialize children nodes
float tsplit = edges[bestAxis][bestOffset].t;
BBox bounds0 = nodeBounds, bounds1 = nodeBounds;
bounds0.pMax[bestAxis] = bounds1.pMin[bestAxis] = tsplit;
buildTree(nodeNum+1, bounds0,
allPrimBounds, prims0, n0, depth-1, edges,
prims0, prims1 + nPrimitives, badRefines);
uint32_t aboveChild = nextFreeNode;
nodes[nodeNum].initInterior(bestAxis, aboveChild, tsplit);
buildTree(aboveChild, bounds1, allPrimBounds, prims1, n1,
depth-1, edges, prims0, prims1 + nPrimitives, badRefines);
}
bool KdTreeAccel::intersect(const Ray& r,float t_min,float t_max,primitive_record &rec) const {
//Compute initial Parametric range of ray inside kd-tree extent
float tMIN,tMAX;
if(!bounds.IntersectP(r,&tMIN,&tMAX))
return false;
//Prepare to traverse kd-tree for ray
Vector invDir(1.f/r.d.x,1.f/r.d.y,1.f/r.d.z);
#define MAX_TODO 64
KdToDo todo[MAX_TODO];
int todoPos = 0;
//Traverse kd-tree nodes in order for ray
bool hit = false;
const KdAccelNode *node = &nodes[0];
while (node != NULL) {
if(t_max < tMIN) break;
if(!node->isLeaf())
{
//Process kd-tree interior node
int axis = node->SplitAxis();
float tplane = (node->SplitPos() - r.o[axis]) * invDir[axis];
const KdAccelNode *firstChild, *secondChild;
int belowFirst = (r.o[axis] < node->SplitPos()) ||
(r.o[axis] == node->SplitPos() && r.d[axis] <= 0);
if (belowFirst) {
firstChild = node + 1;
secondChild = &nodes[node->AboveChild()];
}
else {
firstChild = &nodes[node->AboveChild()];
secondChild = node + 1;
}
if (tplane > tMAX || tplane <= 0)
node = firstChild;
else if (tplane < tMIN)
node = secondChild;
else {
todo[todoPos].node = secondChild;
todo[todoPos].tMIN = tplane;
todo[todoPos].tMAX = tMAX;
++todoPos;
node = firstChild;
tMAX = tplane;
}
}
else
{
//Check for intersections inside leaf node
uint32_t nPrimitives = node->nPrimitives();
if (nPrimitives == 1) {
primitive_record tempRec;
Primitive* prim = primitives[node->onePrimitive];
//Check one prim inside leaf node
if(prim->intersect(r,t_min,t_max,tempRec))
{
rec = tempRec;
hit = true;
}
}
else {
primitive_record tempRec;
float closest_so_far = t_max;
uint32_t *prims = node->primitives;
for (uint32_t i = 0; i < nPrimitives; ++i)
{
//Check one prim inside leaf node
if(primitives[prims[i]]->intersect(r,t_min,closest_so_far,tempRec))
{
hit = true;
closest_so_far = tempRec.t;
rec = tempRec;
}
}
}
//Grab next node to process from todo list
if (todoPos > 0) {
--todoPos;
node = todo[todoPos].node;
tMIN = todo[todoPos].tMIN;
tMAX = todo[todoPos].tMAX;
}
else
break;
}
}
return hit;
}
*相机代码*
Vector random_in_unit_disk() {
Vector p;
do{
p = 2.f*Vector(drand48(),drand48(),0)-Vector(1.f,1.f,0.f);
}while(Dot(p,p) >= 1.f);
return p;
}
class Camera {
public:
Vector lower_left_corner;
Vector horizontal;
Vector vertical;
Point origin;
Vector u,v,w;
float lens_radius; //Focus
float time0,time1;
Camera(Point lookfrom,Point lookat,Vector vup,
float vfov,float aspect,float aperture,float focus_dist,float t0,float t1){
time0 = t0;
time1 = t1;
lens_radius = aperture/2;
float theta = vfov*M_PI/180.f;
float half_height = tanf(theta/2.f);
float half_width = aspect*half_height;
origin = lookfrom;
w = Normalize(lookfrom - lookat);
u = Normalize(Cross(vup,w));
v = Cross(w,u);
lower_left_corner = toVector(origin) - half_width*focus_dist*u - half_height*focus_dist*v - focus_dist*w;
horizontal = 2*half_width*focus_dist*u;
vertical = 2*half_height*focus_dist*v;
}
Ray get_ray(float s,float t) {
Vector rd = lens_radius*random_in_unit_disk();
Vector offset = u * rd.x + v * rd.y;
float time = time0 + drand48()*(time1-time0);
return Ray(origin + offset,lower_left_corner + s*horizontal + t*vertical - toVector(origin) - offset,time);
}
};