func (p Sphere) Intersect(ray, origin glm.Vec3) (b bool, raylen float64, normal glm.Vec3) { normal = glm.Vec3{} line := *p.Pos.Subtract(&origin) // Solve using cosine law for scalar coefficient of ray. raylens := quadraticRoots(ray.Dot(&ray), 2*line.Dot(&ray), line.Dot(&line)-p.Rad*p.Rad) switch len(raylens) { case 0: return false, 0, normal case 1: return ray_epsilon_check(-raylens[0], ray, line) case 2: if raylens[0] > raylens[1] { // NOTE: negative inversion tmp := raylens[0] raylens[0] = raylens[1] raylens[1] = tmp } if b, raylen, normal = ray_epsilon_check(-raylens[1], ray, line); b { return b, raylen, normal } else { return ray_epsilon_check(-raylens[0], ray, line) } } return false, 0, normal }
func (p Mesh) Intersect(ray, origin glm.Vec3) (b bool, raylen float64, normal glm.Vec3) { // If we're debugging just draw the bounding box. if ONLY_DRAW_BOUNDS { return p.Bound.Intersect(ray, origin) } // Check bounding box. if b, _, _ := p.Bound.Intersect(ray, origin); !b { return false, 0, glm.Vec3{} } // Go through faces and do face intersections. raylen = 10000000.0 for i, f := range p.Faces { ray_proj := p.Normals[i].Dot(&ray) // Intersect the ray with the plane. if math.Abs(ray_proj) > Epsilon { new_raylen := p.Verts[f[0]].Subtract(&origin).Dot(&p.Normals[i]) / ray_proj // check that the ray origin is not coincident with the plane if new_raylen > Epsilon { // project face to 2D maj_axis := major_axis(p.Normals[i]) verts2d := project_2dface(f, p.Verts, maj_axis) test_pt := drop_axis(*origin.Add(ray.Scale(new_raylen)), maj_axis) // clip the ray to the bounds of the 2D face. if same_side(verts2d[0], verts2d[1], verts2d[2], test_pt) && same_side(verts2d[1], verts2d[2], verts2d[0], test_pt) && same_side(verts2d[2], verts2d[0], verts2d[1], test_pt) { b = true if new_raylen < raylen { raylen = new_raylen normal = p.Normals[i] } } } } } return }
func same_side(a, b, c, test glm.Vec3) bool { t := b.Subtract(&a) return t.Cross(test.Subtract(&a)).Dot(t.Cross(c.Subtract(&a))) > Epsilon }
func trace(root []scene.Primitive, ambient glm.Vec3, ray, origin *glm.Vec3, lights []scene.Light, depth int) (*glm.Vec3, bool) { if hit, node, raylen, normal := intersectNodes(root, ray, origin); hit { // ambient silhouette mat := root[node].GetMaterial() colour := glm.NewVec3(ambient.Elem[0]*mat.Ambient.Elem[0], ambient.Elem[1]*mat.Ambient.Elem[1], ambient.Elem[2]*mat.Ambient.Elem[2]) // setup for casting secondary (shadow) rays. intersection := origin.Add(ray.Scale(raylen)) diffuse := glm.Vec3{} specular := glm.Vec3{} ray.Normalize() normal.Normalize() // cast shadow ray. for _, light := range lights { shadow_ray := light.Pos.Subtract(intersection) if hit, _, _, _ = intersectNodes(root, shadow_ray, intersection); !hit { shadow_ray.Normalize() // add diffuse/specular components. diffuse_coef := normal.Dot(shadow_ray) if diffuse_coef > 0.00001 { fmt.Println(diffuse_coef) diffuse_tmp := mat.Diffuse.Scale(diffuse_coef) diffuse.Iadd(glm.NewVec3(diffuse_tmp.Elem[0]*light.Colour.Elem[0], diffuse_tmp.Elem[1]*light.Colour.Elem[1], diffuse_tmp.Elem[2]*light.Colour.Elem[2])) } reflected_shadow_ray := shadow_ray.Subtract(normal.Scale(2 * diffuse_coef)) specular_coef := math.Abs(math.Pow(reflected_shadow_ray.Dot(ray), mat.Shininess)) if specular_coef > 0.00001 { specular_tmp := mat.Specular.Scale(specular_coef) specular.Iadd(glm.NewVec3(specular_tmp.Elem[0]*light.Colour.Elem[0], specular_tmp.Elem[1]*light.Colour.Elem[1], specular_tmp.Elem[2]*light.Colour.Elem[2])) } } } colour.Iadd(&diffuse).Iadd(&specular) // cast reflectance ray. if REFLECTIONS { reflected := ray.Subtract(normal.Scale(2 * normal.Dot(ray))) if depth < MAX_DEPTH { if reflected_color, hit := trace(root, ambient, reflected, intersection, lights, depth+1); hit { colour.Iscale(1 - mat.Mirror).Iadd(reflected_color.Scale(mat.Mirror)) } } } return colour, true } return &glm.Vec3{}, false }