void KDTree::calcCostOfPlane( unsigned Nl, unsigned Nr, unsigned Nc, AABB& parentBox, float pos ) { //Split this AABB into two boxes at this point in the axis. AABB l, r; //Split the box at this plane splitBoxAtPos(l, r, parentBox, pos); //Calculate the surface area of the left, right, and parent boxes float SAP, SAR, SAL; SAP = SurfaceArea(parentBox); SAL = SurfaceArea(l); SAR = SurfaceArea(r); //The current cost of this plane is now going to be the minimum of adding the coplanar //triangles to each child box float lHeuristic, rHeuristic; lHeuristic = (SAL / SAP); rHeuristic = (SAR / SAP); float coLeft = lHeuristic * (Nl + Nc) + rHeuristic * Nr; float coRight = lHeuristic * Nl + rHeuristic * (Nr + Nc); curCost = min( coLeft, coRight ); }
// This function adds an object to an existing bounding volume hierarchy. // It first constructs a bounding box for the new object, then finds the // optimal place to insert it into the hierachy using branch-and-bound, and // finally inserts it at the determined location, updating all the costs // associated with the nodes in the hierarchy as a side-effect. void abvh::Insert( const Object *obj, double relative_cost ) { AABB box( GetBox( *obj ) ); double bound = 0.0; node *n = new node; n->bbox = box; n->object = obj; n->SA = SurfaceArea( box ); n->EC = 1.0; n->SEC_ = relative_cost; n->AIC = relative_cost * n->SA; n->SAIC = 0.0; // There are no child volumes yet. // If this is the first node being added to the hierarchy, // it becomes the root. if( root == NULL ) { root = n; return; } // Look for the optimal place to add the new node. // The branch-and-bound procedure will figure out where // to put it so as to cause the smallest increase in the // estimated cost of the tree. bound = Infinity; node *insert_here = NULL; Branch_and_Bound( root, n, insert_here, bound ); // Now actually insert the node in the optimal place. insert_here->AddChild( n ); }
// sample a ray from light void Square::sample_l( const LightSample& ls , Ray& r , Vector& n , float* pdf ) const { float u = 2 * ls.u - 1.0f; float v = 2 * ls.v - 1.0f; r.m_fMin = 0.0f; r.m_fMax = FLT_MAX; r.m_Ori = transform( Point( radius * u , 0.0f , radius * v ) ); r.m_Dir = transform( UniformSampleHemisphere( sort_canonical() , sort_canonical() ) ); n = transform.invMatrix.Transpose()( Vector( 0.0f , 1.0f , 0.0f ) ); if( pdf ) *pdf = 1.0f / ( SurfaceArea() * TWO_PI ); }
// sample a point on shape Point Square::sample_l( const LightSample& ls , const Point& p , Vector& wi , Vector& n , float* pdf ) const { float u = 2 * ls.u - 1.0f; float v = 2 * ls.v - 1.0f; Point lp = transform( Point( radius * u , 0.0f , radius * v ) ); n = transform( Vector( 0 , 1 , 0 ) ); Vector delta = lp - p; wi = Normalize( delta ); float dot = Dot( -wi , n ); if( pdf ) { if( dot <= 0 ) *pdf = 0.0f; else *pdf = delta.SquaredLength() / ( SurfaceArea() * dot ); } return lp; }
// This function inserts the given child node into the bounding volume // hierarchy and adjusts all the associated costs that are stored in // the tree. void node::AddChild( node *new_child ) { double old_AIC; double increment; AABB new_volume( new_child->bbox ); if( child == NULL ) { // The current node is a leaf, so we must convert it into // an internal node. To do this, copy its current contents into // a new node, which will become a child of this one. child = new node; child->bbox = bbox; child->object = object; child->parent = this; // Fill in all the fields of the current node, which has just changed // into an internal node with a single child. EC = 1; SA = child->SA; SEC_ = child->EC; // There is only one child. SAIC = child->AIC; // There is only one child. AIC = SA * SEC_ + SAIC; } // Splice the new child into the linked list of children. new_child->sibling = child; new_child->parent = this; child = new_child; // Update the summed external cost and the summed AIC as a result // of adding the new child node. These do not depend on surface // area, so we needn't consider any expansion of the bounding volume. SEC_ += new_child->EC; SAIC += new_child->AIC; // Now take bounding volume expansion into account due to the new child. bool expanded = false; if( bbox.Expand( new_volume ) ) { expanded = true; SA = SurfaceArea( bbox ); } // Compute new area cost & how much it increased due to new child. old_AIC = AIC; AIC = SA * SEC_ + SAIC; increment = AIC - old_AIC; // Propagate the information upward, in two phases. The first phase // deals with volumes that get expanded. Once a volume is reached that does // not expand, there will be no more expansion all the way to the root. node *n = this; while( expanded ) { expanded = false; node *prev = n; n = n->parent; if( n != NULL && n->bbox.Expand( new_volume ) ) { expanded = true; old_AIC = n->AIC; n->SA = SurfaceArea( n->bbox ); n->SAIC += increment; n->AIC = n->SA * n->SEC_ + n->SAIC; increment = n->AIC - old_AIC; } } // From this point up to the root there will be no more expansion. // However, we must still propagate information upward. while( n != NULL ) { n->SAIC += increment; n->AIC += increment; n = n->parent; } }
// This method determines the optimal location to insert a new node // into the hierarchy; that is, the place that will create the smallest // increase in the expected cost of intersecting a random ray with the // new hierarchy. This is accomplished with branch-and-bound, which finds // the same solution as a brute-force search (i.e. attempting to insert // the new object at *each* possible location in the hierarchy), but // does so efficiently by pruning large portions of the tree. bool abvh::Branch_and_Bound( node *the_root, // Root of the tree to be modified. node *the_node, // The node to add to the hierarchy. node *&best_parent, // The best node to add it to. double &bound ) // Smallest bound achieved thus far. { double a_delta; // AIC increase due to Root bbox expansion. double r_delta; // AIC increase due to new child of Root. double c_bound; // bound - a_delta, or less. // Compute the increase in the bounding box of the root due // to adding the new object. This is used in computing // the new area cost no matter where it ends up. AABB root_box( the_root->bbox ); bool expanded = root_box.Expand( the_node->bbox ); // Compute the common sub-expression of the new area cost // and the increment over this that would occur if we // made the new object a child of the_root. if( expanded ) { double new_area = SurfaceArea( root_box ); a_delta = ( new_area - the_root->SA ) * the_root->SEC_; c_bound = bound - a_delta; if( c_bound <= 0.0 ) return false; // Early cutoff. r_delta = new_area * the_node->EC + the_node->AIC; } else { a_delta = 0.0; c_bound = bound; r_delta = the_root->SA * the_node->EC + the_node->AIC; } // See if adding the new node directly to the root of this tree // achieves a better bound than has thus far been obtained (via // previous searches). If so, update all the parameters to reflect // this better choice. bool improved = false; if( r_delta < c_bound ) { bound = a_delta + r_delta; // This is < *bound. best_parent = the_root; c_bound = r_delta; improved = true; } // Compute the smallest increment in total area cost (over // "common") which would result from adding the new node // to one of the children of the_root. If any of them obtains // a better bound than achieved previously, then the "best_parent" // and "bound" parameters will be updated as a side effect. for( node *c = the_root->child; c != NULL; c = c->sibling ) { if( Branch_and_Bound( c, the_node, best_parent, c_bound ) ) { bound = c_bound + a_delta; improved = true; } } return improved; }