光线跟踪F# - 缺少三角形图中的洞:正确点击?

时间:2016-05-22 16:50:20

标签: f# raytracing kdtree

我已经研究了很长一段时间并且遇到了这个bug。

我们在F#中为学校项目建立了一个光线追踪器。 (链接解释Ray tracer:https://blog.frogslayer.com/kd-trees-for-faster-ray-tracing-with-triangles/

我们为三角形,边界框创建了一个命中函数,一个KD树来处理具有数千个三角形的图形,例如Stanford Bunny和KD树的遍历算法。

我们已经调试了KD树的创建,确保为浮点添加epsilon值并检查边界框之间的重复项是否被删除。我们确信我们正确地分割了场景中的形状列表,但是我们在尝试渲染的图中得到了“洞”。

这是我们的KD树实现,我附上了这些漏洞的图片:

Stanford Bunny

Helix

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

1 个答案:

答案 0 :(得分:0)

感谢您的回复!我发现这个错误是我们对数字的反映,与程序的数据结构无关。

但是谢谢你! :-)