0

希望每个人都很好。我在从 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);
    }

};
4

0 回答 0