// Vec3HemiCos returns a random unit vector chosen on a // cosine-weighed hemisphere defined by normal func (r *Random) Vec3HemiCos(normal *maths.Vec3) *maths.Vec3 { ox := maths.CrossProduct(maths.NewVec3(42, 56, -15), normal) for math.Abs(ox.Length()) < maths.Epsilon { ox = maths.CrossProduct(r.Vec3Sphere(), normal) } oy := maths.CrossProduct(ox, normal) ox.Normalise() oy.Normalise() u := r.Float01() radius := math.Sqrt(u) theta := r.Float02Pi() vec := normal.Scaled(math.Sqrt(math.Max(0, 1-u))) vec.Add(ox.Scaled(radius * math.Cos(theta))) vec.Add(oy.Scaled(radius * math.Sin(theta))) return vec }
// Init of Mesh sets the Triangle indices in the Faces array, calculates the bounding box // and KD tree, sets the surfaceOx and Oy and Cross Products of the sides of each triangle func (m *Mesh) Init() { allIndices := make([]int, len(m.Faces)) for i := range allIndices { allIndices[i] = i } m.BoundingBox = m.GetBoundingBox() m.tree = m.newKDtree(m.BoundingBox, allIndices, 0) for i := range m.Faces { triangle := &m.Faces[i] A := &m.Vertices[triangle.Vertices[0]].Coordinates B := &m.Vertices[triangle.Vertices[1]].Coordinates C := &m.Vertices[triangle.Vertices[2]].Coordinates AB := maths.MinusVectors(B, A) AC := maths.MinusVectors(C, A) triangle.AB = AB triangle.AC = AC triangle.ABxAC = maths.CrossProduct(AB, AC) surfaceA := &m.Vertices[triangle.Vertices[0]].UV surfaceB := &m.Vertices[triangle.Vertices[1]].UV surfaceC := &m.Vertices[triangle.Vertices[2]].UV surfaceAB := maths.MinusVectors(surfaceB, surfaceA) surfaceAC := maths.MinusVectors(surfaceC, surfaceA) // Solve using Cramer: // |surfaceAB.X * px + surfaceAX.X *qx + 1 = 0 // |surfaceAB.Y * px + surfaceAX.Y *qx = 0 // and // surfaceAB.X * py + surfaceAX.X *qy = 0 // surfaceAB.X * py + surfaceAX.X *qy + 1 = 0 px, qx := maths.SolveEquation(surfaceAB, surfaceAC, maths.NewVec3(1, 0, 0)) py, qy := maths.SolveEquation(surfaceAB, surfaceAC, maths.NewVec3(0, 1, 0)) triangle.surfaceOx = maths.AddVectors(AB.Scaled(px), AC.Scaled(qx)) triangle.surfaceOy = maths.AddVectors(AB.Scaled(py), AC.Scaled(qy)) } }
// IntersectTriangle find whether there's an intersection point between the ray and the triangle // using barycentric coordinates and calculate the distance func IntersectTriangle(ray *ray.Ray, A, B, C *maths.Vec3) (bool, float64) { AB := maths.MinusVectors(B, A) AC := maths.MinusVectors(C, A) reverseDirection := ray.Direction.Negative() distToA := maths.MinusVectors(&ray.Start, A) ABxAC := maths.CrossProduct(AB, AC) det := maths.DotProduct(ABxAC, reverseDirection) reverseDet := 1 / det if math.Abs(det) < maths.Epsilon { return false, maths.Inf } lambda2 := maths.MixedProduct(distToA, AC, reverseDirection) * reverseDet lambda3 := maths.MixedProduct(AB, distToA, reverseDirection) * reverseDet gamma := maths.DotProduct(ABxAC, distToA) * reverseDet if gamma < 0 { return false, maths.Inf } if lambda2 < 0 || lambda2 > 1 || lambda3 < 0 || lambda3 > 1 || lambda2+lambda3 > 1 { return false, maths.Inf } return true, gamma }
// IntersectTriangle checks if the bounding box intersects with a triangle // 1) To have a vertex in the box // 2) The edge of the triangle intersects with the box // 3) The middle of the triangle to be inside the box, while the vertices aren't func (b *BoundingBox) IntersectTriangle(A, B, C *maths.Vec3) bool { if b.Inside(A) || b.Inside(B) || b.Inside(C) { return true } // we construct the ray from A->B, A->C, etc and check whether it intersects with the box triangle := [3]*maths.Vec3{A, B, C} ray := &ray.Ray{} for rayStart := 0; rayStart < 3; rayStart++ { for rayEnd := rayStart + 1; rayEnd < 3; rayEnd++ { ray.Start = *(triangle[rayStart]) ray.Direction = *maths.MinusVectors(triangle[rayEnd], triangle[rayStart]) ray.Init() // Check if there's intersection between AB and the box (example) if b.Intersect(ray) { // we check whether there's intersection between BA and the Box too // to be sure the edge isn't outside the box // _____ // | | <----AB there is intersection, but BA-----> // |____| ray.Start = *triangle[rayEnd] ray.Direction = *maths.MinusVectors(triangle[rayStart], triangle[rayEnd]) ray.Init() if b.Intersect(ray) { return true } } } } // we have to check if the inside of the triangle intersects with the box AB := maths.MinusVectors(B, A) AC := maths.MinusVectors(C, A) ABxAC := maths.CrossProduct(AB, AC) distance := maths.DotProduct(A, ABxAC) rayEnd := &maths.Vec3{} for edgeMask := 0; edgeMask < 7; edgeMask++ { for axis := uint(0); axis < 3; axis++ { if edgeMask&(1<<axis) != 0 { continue } if edgeMask&1 != 0 { ray.Start.X = b.MaxVolume[0] } else { ray.Start.X = b.MinVolume[0] } if edgeMask&2 != 0 { ray.Start.Y = b.MaxVolume[1] } else { ray.Start.Y = b.MinVolume[1] } if edgeMask&4 != 0 { ray.Start.Z = b.MaxVolume[2] } else { ray.Start.Z = b.MinVolume[2] } *rayEnd = ray.Start rayEnd.SetDimension(int(axis), b.MaxVolume[axis]) if (maths.DotProduct(&ray.Start, ABxAC)-distance)*(maths.DotProduct(rayEnd, ABxAC)-distance) <= 0 { ray.Direction = *maths.MinusVectors(rayEnd, &ray.Start) ray.Init() intersected, distance := IntersectTriangle(ray, A, B, C) if intersected && distance < 1.0000001 { return true } } } } return false }