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)); }
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); }