Beispiel #1
0
// ShootRay generates a ray coming out of the camera, going through the
// specified coordinates of the screen
func (c *PinholeCamera) ShootRay(x, y float64) *ray.Ray {
	r := &ray.Ray{}
	r.Start = c.Focus

	intersection := &maths.Vec3{}
	*intersection = c.TopLeft
	intersection.Add(maths.MinusVectors(&c.TopRight, &c.TopLeft).Scaled(x))
	intersection.Add(maths.MinusVectors(&c.BottomLeft, &c.TopLeft).Scaled(y))

	r.Direction = *maths.MinusVectors(intersection, &r.Start).Normalised()
	return r
}
Beispiel #2
0
// Shade returns the emitted colour after intersecting the material
func (m *RefractiveMaterial) Shade(intersection *ray.Intersection, raytracer Raytracer) *hdrcolour.Colour {
	incoming := &intersection.Incoming.Direction
	normal := intersection.Normal
	ior := m.IOR.GetFac(intersection)
	colour := m.Colour.GetColour(intersection)
	refracted := &maths.Vec3{}
	startPoint := &maths.Vec3{}

	if maths.DotProduct(incoming, normal) < 0 {
		refracted = maths.Refract(incoming, normal, 1/ior)
	} else {
		refracted = maths.Refract(incoming, normal.Negative(), ior)
	}

	if refracted != nil {
		// regular refraction - push the starting point a tiny bit
		// through the surface
		startPoint = maths.MinusVectors(
			intersection.Point, normal.FaceForward(incoming).Scaled(maths.Epsilon),
		)
	} else {
		// total inner reflection
		refracted = incoming.Reflected(normal.FaceForward(incoming))
		// push the starting point a tiny bit away from the surface
		startPoint = maths.AddVectors(
			intersection.Point, normal.FaceForward(incoming).Scaled(maths.Epsilon),
		)
	}

	newRay := &ray.Ray{Depth: intersection.Incoming.Depth + 1}
	newRay.Start = *startPoint
	newRay.Direction = *refracted
	return hdrcolour.MultiplyColours(raytracer.Raytrace(newRay), colour)

}
Beispiel #3
0
// 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))
	}
}
Beispiel #4
0
// 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
}
Beispiel #5
0
// 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
}
Beispiel #6
0
// IntersectTriangle returns whether there's an intersection between the ray and the triangle,
// using barycentric coordinates and takes the point only if it's closer to the
// previously found intersection and the point is within the bounding box
func (m *Mesh) intersectTriangle(ray *ray.Ray, triangle *Triangle, intersection *ray.Intersection, boundingBox *BoundingBox) bool {
	// lambda2 * AB + lambda3 * AC - intersectDist*rayDir = distToA
	// If the triangle is ABC, this gives you A
	A := &m.Vertices[triangle.Vertices[0]].Coordinates
	distToA := maths.MinusVectors(&ray.Start, A)
	rayDir := ray.Direction
	ABxAC := triangle.ABxAC
	// We will find the barycentric coordinates using Cramer's formula, so we'll need the determinant
	// det is (AB^AC)*dir of the ray, but we're gonna use 1/det, so we find the recerse:
	det := -maths.DotProduct(ABxAC, &rayDir)
	if math.Abs(det) < maths.Epsilon {
		return false
	}
	reverseDet := 1 / det
	intersectDist := maths.DotProduct(ABxAC, distToA) * reverseDet
	if intersectDist < 0 || intersectDist > intersection.Distance {
		return false
	}
	// lambda2 = (dist^dir)*AC / det
	// lambda3 = -(dist^dir)*AB / det
	lambda2 := maths.MixedProduct(distToA, &rayDir, triangle.AC) * reverseDet
	lambda3 := -maths.MixedProduct(distToA, &rayDir, triangle.AB) * reverseDet
	if lambda2 < 0 || lambda2 > 1 || lambda3 < 0 || lambda3 > 1 || lambda2+lambda3 > 1 {
		return false
	}

	ip := maths.AddVectors(&ray.Start, (&rayDir).Scaled(intersectDist))

	// If we aren't inside the bounding box, there could be a closer intersection
	// within the bounding box
	if boundingBox != nil && !boundingBox.Inside(ip) {
		return false
	}
	intersection.Point = ip
	intersection.Distance = intersectDist
	if triangle.Normal != nil {
		intersection.Normal = triangle.Normal
	} else {
		// We solve intersection.normal = Anormal + AB normal * lambda2 + ACnormal * lambda 3
		Anormal := &m.Vertices[triangle.Vertices[0]].Normal
		Bnormal := &m.Vertices[triangle.Vertices[1]].Normal
		Cnormal := &m.Vertices[triangle.Vertices[2]].Normal
		ABxlambda2 := maths.MinusVectors(Bnormal, Anormal).Scaled(lambda2)
		ACxlambda3 := maths.MinusVectors(Cnormal, Anormal).Scaled(lambda3)
		intersection.Normal = maths.AddVectors(Anormal, maths.AddVectors(ABxlambda2, ACxlambda3))
	}
	uvA := &m.Vertices[triangle.Vertices[0]].UV
	uvB := &m.Vertices[triangle.Vertices[1]].UV
	uvC := &m.Vertices[triangle.Vertices[2]].UV

	// We solve intersection.uv = uvA + uvAB * lambda2 + uvAC * lambda 3
	uvABxlambda2 := maths.MinusVectors(uvB, uvA).Scaled(lambda2)
	uvACxlambda3 := maths.MinusVectors(uvC, uvA).Scaled(lambda3)
	uv := maths.AddVectors(uvA, maths.AddVectors(uvABxlambda2, uvACxlambda3))

	intersection.U = uv.X
	intersection.V = uv.Y

	intersection.SurfaceOx = triangle.surfaceOx
	intersection.SurfaceOy = triangle.surfaceOy

	intersection.Incoming = ray
	intersection.Material = triangle.Material
	return true
}