Exemplo n.º 1
0
    private BVHNode BuildNodeRecursively(NodeSpec spec, int depth)
    {
        // 节点只有一个图元的时候没必要再继续分割
        if (spec.numRef <= MIN_LEAF_SIZE || depth >= MAX_DEPTH)
        {
            return(CreatLeaf(spec));
        }

        // 挑选使用object split还是spatial split
        float        leafSAH      = spec.bounds.Area * spec.numRef;
        float        nodeSAH      = spec.bounds.Area * 0.125f;//spec.Bounds.Area * 2; // 节点遍历的固定开销,2是个经验值(不一定是最好的)
        ObjectSplit  objectSplit  = FindObjectSplit(spec, nodeSAH);
        SpatialSplit spatialSplit = SpatialSplit.New();

        if (depth < MAX_SPATIAL_DEPTH)
        {
            var overlap = objectSplit.leftBounds;
            overlap.Intersect(objectSplit.rightBounds);

            if (overlap.Area >= _minOverlap)
            {
                spatialSplit = FindSpatialSplit(spec, nodeSAH);
            }
        }

        // 叶节点胜出,不论是Object还是Spatial slpit,分割后的
        float minSAH = Mathf.Min(Mathf.Min(leafSAH, objectSplit.sah), spatialSplit.sah);

        if (minSAH == leafSAH && spec.numRef <= MAX_LEAF_SIZE)
        {
            return(CreatLeaf(spec));
        }

        // spatial split胜出,尝试执行spatial split
        NodeSpec left  = NodeSpec.New();
        NodeSpec right = NodeSpec.New();

        if (minSAH == spatialSplit.sah)
        {
            PerformSpatialSplit(ref left, ref right, spec, spatialSplit);
        }

        // objcet split胜出,或spatial split并未取得实质性进展,执行object split
        if (left.numRef == 0 || right.numRef == 0)
        {
            PerformObjectSplit(ref left, ref right, spec, objectSplit);
        }

        _numDuplicates += left.numRef + right.numRef - spec.numRef;

        // 由于后文取下标的方式,一定是先右后左
        var rightNode = BuildNodeRecursively(right, depth + 1);
        var leftNode  = BuildNodeRecursively(left, depth + 1);

        return(new InnerNode(spec.bounds, leftNode, rightNode));
    }
Exemplo n.º 2
0
    private SpatialSplit FindSpatialSplit(NodeSpec spec, float nodeSAH)
    {
        // _bins变量每一次分割都被复用
        var origin     = spec.bounds.min;
        var binSize    = (spec.bounds.max - origin) / N_SPATIAL_BINS;
        var invBinSize = new Vector3(1f / binSize.x, 1f / binSize.y, 1f / binSize.z);

        for (int dim = 0; dim < 3; dim++)
        {
            for (int i = 0; i < N_SPATIAL_BINS; i++)
            {
                _bins[dim, i] = SpatialBin.New();
            }
        }

        // 把图元分配到3个维度的bin中
        for (int refIdx = _refStack.Count - spec.numRef; refIdx < _refStack.Count; refIdx++)
        {
            var pRef = _refStack[refIdx];
            // ....Vector3Int.FloorToInt 误用了 celling...查半天。。。。
            var firstBin = MathUtils.ClampV3Int(Vector3Int.FloorToInt((pRef.bounds.min - origin).Multiply(invBinSize)), Vector3Int.zero, new Vector3Int(N_SPATIAL_BINS - 1, N_SPATIAL_BINS - 1, N_SPATIAL_BINS - 1));
            var lastBin  = MathUtils.ClampV3Int(Vector3Int.FloorToInt((pRef.bounds.max - origin).Multiply(invBinSize)), firstBin, new Vector3Int(N_SPATIAL_BINS - 1, N_SPATIAL_BINS - 1, N_SPATIAL_BINS - 1));

            for (int dim = 0; dim < 3; dim++)
            {
                var curRef = pRef;
                // 从左到右分割,curRef并不更新图元索引,只更新包围盒
                for (int i = firstBin[dim]; i < lastBin[dim]; i++)
                {
                    PrimitiveRef leftRef, rightRef;
                    SplitReference(out leftRef, out rightRef, curRef, dim, origin[dim] + binSize[dim] * (i + 1));
                    _bins[dim, i].bounds.Union(leftRef.bounds);
                    curRef = rightRef;
                }

                _bins[dim, lastBin[dim]].bounds.Union(curRef.bounds); // 分割后图元最右边的包围盒也算进来
                                                                      // 只对分割后图元所在的第一个和最后一个bin添加图元引用计数
                _bins[dim, firstBin[dim]].enter++;
                _bins[dim, lastBin[dim]].exit++;
            }
        }

        // 根据分割好的bins,来选择最佳分割平面,跟FindObjectSplit类似,只不过是以bin为单位,而不是图元
        SpatialSplit split = SpatialSplit.New();

        for (byte dim = 0; dim < 3; dim++)
        {
            // 从右到左,记录每一种可能的分割后,处在“右边”包围盒的
            AABB rightBounds = AABB.New();
            for (int i = N_SPATIAL_BINS - 1; i > 0; i--)
            {
                rightBounds.Union(_bins[dim, i].bounds);
                _rightBounds[i - 1] = rightBounds; //_rightBounds用来临时记录右边包围盒的,被复用
            }

            AABB leftBounds = AABB.New();
            int  leftNum    = 0;
            int  rightNum   = spec.numRef;
            for (int i = 1; i < N_SPATIAL_BINS; i++)
            {
                leftBounds.Union(_bins[dim, i - 1].bounds);
                leftNum  += _bins[dim, i - 1].enter;
                rightNum -= _bins[dim, i - 1].exit;

                float sah = nodeSAH + leftBounds.Area * leftNum + _rightBounds[i - 1].Area * rightNum;
                if (sah < split.sah)
                {
                    split.sah = sah;
                    split.dim = dim;
                    split.pos = origin[dim] + binSize[dim] * i;
                }
            }
        }

        return(split);
    }