// 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) }
// Shade returns the emitted colour after intersecting the material func (m *LambertMaterial) Shade(intersection *ray.Intersection, raytracer Raytracer) *hdrcolour.Colour { randomRayDir := *raytracer.RandomGen().Vec3HemiCos(intersection.Normal) randomRayStart := *maths.AddVectors(intersection.Point, intersection.Normal.Scaled(maths.Epsilon)) ray := &ray.Ray{Start: randomRayStart, Direction: randomRayDir, Depth: intersection.Incoming.Depth + 1} colour := raytracer.Raytrace(ray) colour.MultiplyBy(m.Colour.GetColour(intersection)) return colour }
// Shade returns the emitted colour after intersecting the material func (m *ReflectiveMaterial) Shade(intersection *ray.Intersection, raytracer Raytracer) *hdrcolour.Colour { incoming := intersection.Incoming colour := m.Colour.GetColour(intersection) reflectedRay := &ray.Ray{Depth: incoming.Depth + 1} reflectedRay.Direction = *incoming.Direction.Reflected(intersection.Normal) reflectedRay.Start = *maths.AddVectors(intersection.Point, intersection.Normal.Scaled(maths.Epsilon)) return hdrcolour.MultiplyColours(raytracer.Raytrace(reflectedRay), colour) }
// 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 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 }