光线追踪—层次包围盒( Bounding Volume Hierarchies )

原理:
通过一个简单的包围盒把物体包围起来,射线和场景中的物体求交之前,会先和这个包围盒进行求交,如果该射线没有碰到该包围盒,表明该直线一定不会和包围盒里的物体相交;如果该射线碰到该包围盒,那么再来计算射线是否和包围盒中的物体相交。
作用:
BVH可以使渲染器更加高效,值得注意的是盒子是可以重叠的。当然,盒子内也可以放其他的盒子,这样我们会得到一个树形结构。
怎样判断和盒子是否相交?
2D包围盒的边界是一条线,而3D包围盒的边界是一个面
射线可以看为是一个方程:P(t)=A+B*t
将这个公式实际应用
在这里插入图片描述
实现步骤:
1.修改Vector3D,Point3D中的代码,方便接下来的索引操作(对于索引器的操作:索引值为0返回x,值为1返回y,值为2返回z。)

public class Vector3D
    {
    
    
        public double[] data = new double[3];
        public double this[int index]
        {
    
    
            get => data[index];
            set => data[index] = value;
        }
        private double _x;
        private double _y;
        private double _z;
        public double X
        {
    
    
            get => data[0];
            set => data[0] = value;
        }
         public double Y
        {
    
    
            get => data[1];
            set => data[1] = value;
        }
        public double Z
        {
    
    
            get => data[2];
            set => data[2] = value;
        }
        //...省略其他代码...//
  }

2.创建AABB包围盒的类

public class AABB
    {
    
    
        Vector3D _min;
        Vector3D _max;
        public AABB()
        {
    
    
        }
        public AABB(Vector3D a,Vector3D b)
        {
    
    
            _min = a;
            _max = b;
        }
        public Vector3D Min {
    
     get => _min; set => _min = value; }
        public Vector3D Max {
    
     get => _max; set => _max = value; }
         public bool hit(Ray r, double tmin,double tmax) 
        {
    
    
            for (var a = 0; a < 3; a++)
            {
    
    
                var t0 = Math.Min((Min[a] - r.Origin[a]) / r.Direction[a],
                                  (Max[a] - r.Origin[a]) / r.Direction[a]);
                var t1 = Math.Max((Min[a] - r.Origin[a]) / r.Direction[a],
                                  (Max[a] - r.Origin[a]) / r.Direction[a]);
                tmin = Math.Max(t0, tmin);
                tmax = Math.Min(t1, tmax);
                if (tmax <= tmin)
                    return false;
            }
            return true;
        }
    }       

3.在GeometryObject基类中添加抽象函数:

//生成物体的包围盒
public abstract bool Bounding_box(double t0,double t1,out AABB box);

4.修改子类Sphere和Moving_sphere
Sphere

public override bool Bounding_box(double t0, double t1,out AABB box)
        {
    
    
            box = new AABB(Center - new Point3D(Radius, Radius, Radius), 
                           Center + new Point3D(Radius, Radius, Radius)); 
            return true;
        }

Moving_sphere

public override bool Bounding_box(double t0, double t1,out AABB box)
{
    
    
    AABB aabb1 = new AABB();
    //得到t0和t1两个时刻的包围盒
    AABB box0=new AABB(Center0 - new Point3D(radius, radius, radius),
                       Center0 + new Point3D(radius, radius, radius)); 
    AABB box1 = new AABB(Center1 - new Point3D(radius, radius, radius),
                         Center1 + new Point3D(radius, radius, radius));
    //将t0时的盒子和t1时的盒子放进一个大盒子里
    box = Surrounding_box(box0, box1); 
    return true;
}

5.在GeometryObject中添加Surrounding_box函数

public AABB Surrounding_box(AABB box0, AABB box1)
{
    
    
    Vector3D small = new Vector3D(Math.Min(box0.Min.X, box1.Min.X),
                                  Math.Min(box0.Min.Y, box1.Min.Y),
                                  Math.Min(box0.Min.Z, box1.Min.Z));
    Vector3D big = new Vector3D(Math.Max(box0.Max.X, box1.Max.X),
                                Math.Max(box0.Max.Y, box1.Max.Y),
                                Math.Max(box0.Max.Z, box1.Max.Z));
    return new AABB(small, big);
}

6.新建BVHnode类

class BVHnode:GeometryObject
{
    
    
    GeometryObject left;
    GeometryObject right;
    AABB box;
    public BVHnode(){
    
    }
    private static double random()
    {
    
    
        var seed = Guid.NewGuid().GetHashCode();
        Random r = new Random(seed);
        int i = r.Next(0, 100000);
        return (double)i / 100000;
    }
        //用来排序的嵌套函数
        int Compare(GeometryObject a, GeometryObject b, int i)
        {
    
    
            AABB l = new AABB(), r = new AABB();
            //if (!a.BoundingBox(0, 0, ref l) || !b.BoundingBox(0, 0, ref r)) throw new Exception("NULL");
            return l.Min[i] - r.Min[i] < 0 ? -1 : 1;
        }
        //用来排序的分割数组的嵌套函数。
        GeometryObject[] SplitArray(GeometryObject[] Source, int StartIndex, int EndIndex)
        {
    
    
            var result = new GeometryObject[EndIndex - StartIndex + 1];
            for (var i = 0; i <= EndIndex - StartIndex; i++) result[i] = Source[i + StartIndex];
            return result;
        } 
        public BVHnode(GeometryObject[] p, int n, double time0, double time1)
        {
    
    
            //随机一个轴。 x轴:0 y轴:1 z轴:3
            int method = (int)(3 * random());
            //转换为List然后使用排序,最后再转换为Array
            //排序规则使用lambda表达式转向比较函数,并加入轴向参数
            var temp_list = p.ToList();
            temp_list.Sort((a, b) => Compare(a, b, method));
            p = temp_list.ToArray();
            //检测当前子节点数量,如果大于2则继续分割。
            switch (n)
            {
    
    
                case 1:
                    left = right = p[0];
                    break;
                case 2:
                    left = p[0];
                    right = p[1];
                    break;
                default: //拆分
                    left = new BVHnode(SplitArray(p, 0, n / 2 - 1), n / 2, time0, time1);
                    right = new BVHnode(SplitArray(p, n / 2, n - 1), n - n / 2, time0, time1);
                    break;
            }
                        //根据子节点生成当前节点的包围盒
            AABB box_left = new AABB(), box_right = new AABB();
            //if (!left.BoundingBox(time0, time1, ref box_left) || !right.BoundingBox(time0, time1, ref box_right))
            //    throw new Exception("no bounding box in bvh_node constructor");
            box = Surrounding_box(box_left, box_right);
        }
                
        public override bool Bounding_box(double t0, double t1,out AABB b)
        {
    
    
            b=box;
            return true;
        }
                //检查光线是否和盒子相交
        public override bool Hit(Ray ray, out ShadeRec sr)
        {
    
    
            double t_min=0, t_max= 1e-5;
            if (box.hit(ray, t_min, t_max))
            {
    
    
                ShadeRec left_rec, right_rec;
                bool hit_left = left.Hit(ray,out left_rec);
                bool hit_right = right.Hit(ray,out right_rec);
                if (hit_left && hit_right)
                {
    
    
                    if (left_rec.HitT < right_rec.HitT)
                        sr = left_rec;
                    else
                        sr = right_rec; 
                    return true;
                }
                else if (hit_left)
                {
    
    
                    sr = left_rec; 
                    return true;
                }
                else if (hit_right)
                {
    
    
                    sr = right_rec; 
                    return true;
                }
                else
                {
    
    
                    sr = null;
                    return false;
                }
            }
            else
            {
    
    
                sr = null;
                return false;
            }
        }
        
        public override bool ShadowHit(Ray ray)
        {
    
    
            throw new NotImplementedException();
        }
    }

参考专栏:https://zhuanlan.zhihu.com/c_189375388
码云地址:https://gitee.com/xmr123/Ray-Tracing/tree/BVH

猜你喜欢

转载自blog.csdn.net/sunshine543123/article/details/107041122