我已经研究了很长一段时间并且遇到了这个bug。
我们在F#中为学校项目建立了一个光线追踪器。 (链接解释Ray tracer:https://blog.frogslayer.com/kd-trees-for-faster-ray-tracing-with-triangles/)
我们为三角形,边界框创建了一个命中函数,一个KD树来处理具有数千个三角形的图形,例如Stanford Bunny和KD树的遍历算法。
我们已经调试了KD树的创建,确保为浮点添加epsilon值并检查边界框之间的重复项是否被删除。我们确信我们正确地分割了场景中的形状列表,但是我们在尝试渲染的图中得到了“洞”。
这是我们的KD树实现,我附上了这些漏洞的图片:
module TmKdtree
open Point
open Shapes
type BoundingBox = BasicShape.BoundingBox
type Shape = BasicShape.Shape
type TmKdtree =
| Leaf of BasicShape.Triangle list * BoundingBox
| Node of BasicShape.Triangle list * TmKdtree * TmKdtree * BoundingBox * (string*Point)
let epsilon = 0.001
//Making a boundingbox for the KD-tree, by finding max H point in the boundingboxlist and min l point in the boundingbox list.
let mkKdBbox (shapes : BasicShape.Triangle list) : BoundingBox =
let shapeX = List.map(fun x -> x:> Shape) shapes
let sbbox = List.map (fun (c:Shape) -> c.getBounding().Value) shapeX
let bL = List.map (fun (b:BasicShape.BoundingBox) -> b.getL) sbbox
let bH = List.map (fun (b:BasicShape.BoundingBox) -> b.getH) sbbox
let minX = List.minBy (fun x -> Point.getX x) bL
let minY = List.minBy (fun x -> Point.getY x) bL
let minZ = List.minBy (fun x -> Point.getZ x) bL
let maxX = List.maxBy (fun x -> Point.getX x) bH
let maxY = List.maxBy (fun x -> Point.getY x) bH
let maxZ = List.maxBy (fun x -> Point.getZ x) bH
{p1=(mkPoint (Point.getX minX - epsilon) (Point.getY minY - epsilon) (Point.getZ minZ - epsilon) )
; p2=(mkPoint (Point.getX maxX + epsilon) (Point.getY maxY + epsilon) (Point.getZ maxZ + epsilon) )}
//Splitting existing boundingbox according to left and right list of shapes
let BoundingBoxL (bbox:BoundingBox) axis midp : BoundingBox =
match axis with
| "x" -> {p1 = bbox.getL - epsilon; p2 = Point.mkPoint ((Point.getX midp)) ((Point.getY (bbox.getH))) ((Point.getZ (bbox.getH))) + epsilon}
| "y" -> {p1 = bbox.getL - epsilon; p2 = Point.mkPoint (Point.getX (bbox.getH)) ((Point.getY midp)+epsilon) ((Point.getZ (bbox.getH))) + epsilon }
| "z" -> {p1 = bbox.getL - epsilon; p2 = Point.mkPoint (Point.getX (bbox.getH)) (Point.getY (bbox.getH)) (Point.getZ midp) + epsilon}
let BoundingBoxR (bbox:BoundingBox) axis midp : BoundingBox =
match axis with
| "x" -> {p1 = (Point.mkPoint (Point.getX midp) (Point.getY (bbox.getL)) (Point.getZ (bbox.getL))) - epsilon; p2 = bbox.getH + epsilon}
| "y" -> {p1 = (Point.mkPoint (Point.getX (bbox.getL)) (Point.getY midp) (Point.getZ (bbox.getL))) - epsilon; p2 = bbox.getH + epsilon}
| "z" -> {p1 = (Point.mkPoint (Point.getX (bbox.getL)) (Point.getY (bbox.getL)) (Point.getZ midp)) - epsilon; p2 = bbox.getH + epsilon}
//Get left node
let getLeft s =
match s with
| Node(_,l,_,_,_) -> l
| Leaf(_,_) as leaf -> leaf
let getRight s =
match s with
| Node(_,_,r,_,_) -> r
| Leaf(_,_) as leaf -> leaf
//Get the triangle list
let getShapes s =
match s with
| Node(b,_,_,_,_) -> b
| Leaf(b,_) -> b
let getAxis s =
match s with
| Node(_,_,_,_,a) -> a
| Leaf(_,_) -> failwith "leaf ramt af axis"
//Get bounding box
let getBox s =
match s with
| Node(_,_,_,b,_) -> Some b
| Leaf(_,b) -> Some b
let closestHit (triList : BasicShape.Triangle list) ray =
let sndRects = List.map(fun x -> x:> Shape) triList
let min = List.map(fun (x:Shape) -> x.hit ray) sndRects |> List.choose id
match min with
|[] -> None
|_ -> Some(List.minBy (fun (di, nV, mat) -> di) min)
let searchLeaf leaf ray t' =
match leaf with
| Leaf(s,_) -> let hit = closestHit s ray
match hit with
|Some(f,_,_) -> if (f<t') then Some hit else None
|None -> None
| Node(_,_,_,_,_) -> failwith "Expected leaf"
let order(d, left, right) =
if d > 0.0
then (left, right)
else (right, left)
let rec search node ray t t' =
match node with
|Leaf(_,_) -> searchLeaf node ray t'
|Node(_,_,_,_,a') ->
let direction = Ray.getDirection ray (fst a')
let origin = Ray.getOrigin ray (fst a')
let nodeValue = Point.getFromAxis (snd a') (fst a')
if(direction) = 0.0 then
printfn("%s") "flatsite"
if(origin <= nodeValue) then search (getLeft node) ray t t'
else search (getRight node) ray t t'
else
let tHit = (nodeValue - origin) / direction
let (fst, snd) = order(direction,getLeft node, getRight node)
if tHit <= t || tHit < 0.0 then
search snd ray t t'
else if tHit >= t' then
search fst ray t t'
else
match search fst ray t tHit with
|Some hit -> Some hit
|_ -> search snd ray tHit t'
let traverse tree ray (bawx:BasicShape.BoundingBox) =
match(bawx).hit(ray) with
|Some(t,t') -> search tree ray t t'
|None -> None
//Finding the midpoint in the triangles in Shapes-list - we do this (recursively) to find out what axis to split
let rec mkTmKdtree (shapes : BasicShape.Triangle list) (box:BasicShape.BoundingBox) =
//Finding biggest dimension in the shapes list
let axis = snd (box.getLongestAxis)
let axisMidPoint =
let midPoint = List.fold (fun acc (ele:BasicShape.Triangle) -> (acc + ele.getMidPoint())) (Point.mkPoint 0.0 0.0 0.0) shapes
let avgMid = midPoint / float(shapes.Length)
avgMid
let rec largerThanSplit (xs:BasicShape.Triangle list) =
let results = List.choose(fun (elem:BasicShape.Triangle) ->
match axis with
|"x" -> if (Point.getX (elem.getMidPoint())) >= (Point.getX axisMidPoint) then Some elem else None
|"y" -> if (Point.getY (elem.getMidPoint())) >= (Point.getY axisMidPoint) then Some elem else None
|"z" -> if (Point.getZ (elem.getMidPoint())) >= (Point.getZ axisMidPoint) then Some elem else None) xs
results
let rec lessThanSplit (xs:BasicShape.Triangle list) =
let results = List.choose(fun (elem:BasicShape.Triangle) ->
match axis with
|"x" -> if ((Point.getX (elem.getMidPoint())) <= (Point.getX (axisMidPoint))) then Some elem else None
|"y" -> if ((Point.getY (elem.getMidPoint())) <= (Point.getY (axisMidPoint))) then Some elem else None
|"z" -> if ((Point.getZ (elem.getMidPoint())) <= (Point.getZ (axisMidPoint))) then Some elem else None) xs
results
//Creating the left and right list from the above
let rightTest = largerThanSplit shapes
let leftTest = lessThanSplit shapes
//If one of the trees are empty, we add make left and right equivelant.
let left = if(leftTest.IsEmpty && rightTest.Length > 0) then rightTest else leftTest
let right = if(rightTest.IsEmpty && leftTest.Length > 0) then leftTest else rightTest
//Check for duplicates among the lists.
if(((float(left.Length+right.Length-shapes.Length)/float(shapes.Length)) < 0.4) && left.Length <> shapes.Length && right.Length<>shapes.Length) then
let leftTree = mkTmKdtree left (BoundingBoxL box axis axisMidPoint)
let rightTree = mkTmKdtree right (BoundingBoxR box axis axisMidPoint)
Node(shapes,leftTree, rightTree, (box),(axis,axisMidPoint))
else Leaf(shapes, (box))
答案 0 :(得分:0)
感谢您的回复!我发现这个错误是我们对数字的反映,与程序的数据结构无关。
但是谢谢你! :-)