Пример #1
0
// Vec3Hemi returns a random unit vector in the hemisphere defined by normal
func (r *Random) Vec3Hemi(normal *maths.Vec3) *maths.Vec3 {
	vec := r.Vec3Sphere()
	if maths.DotProduct(vec, normal) < 0 {
		vec.Negate()
	}
	return vec
}
Пример #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)

}
Пример #3
0
func TestVectors(t *testing.T) {
	assert := assert.New(t)

	rnd := New(42)

	vec := rnd.Vec3Sphere()

	assert.InDelta(1, vec.Length(), maths.Epsilon, "Random unit vector's length should be 1")

	norm := vec

	vec = rnd.Vec3Hemi(norm)
	if maths.DotProduct(vec, norm) < 0 {
		t.Error("Random hemi vector is in the wrong hemisphere")
	}

	vec = rnd.Vec3HemiCos(norm)
	if maths.DotProduct(vec, norm) < 0 {
		t.Error("Random cosine-weighed hemi vector is in the wrong hemisphere")
	}
}
Пример #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
}
Пример #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
}
Пример #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
}