package main import ( maths "math" ) //HitRecord type HitRecord struct { Valid bool P Vec3 N Vec3 T float64 F bool Mat Material } func (rec *HitRecord) SetFaceNormal(r Ray, out_n Vec3) { rec.F = r.Direction.Dot(out_n) < 0; if rec.F { rec.N = out_n; } else { rec.N = out_n.Neg(); } } //Hittable type Hittable interface { Hit(r Ray, ray_t Interval) (rec HitRecord) } //Sphere < Hittable type Sphere struct { Origin Vec3 Radius float64 Mat Material } func (s Sphere) Hit(r Ray, ray_t Interval) (rec HitRecord) { oc := s.Origin.Sub(r.Origin); a := r.Direction.MagSqr(); h := r.Direction.Dot(oc); c := oc.MagSqr() - s.Radius * s.Radius; disc := h * h - a * c; if disc < 0 { return; } sqrtd := maths.Sqrt(disc); root := (h - sqrtd) / a; if !ray_t.Surround(root) { root = (h + sqrtd) / a; if !ray_t.Surround(root) { return; } } rec.T = root; rec.P = r.At(rec.T); out_n := rec.P.Sub(s.Origin).Div(SplatVec3(s.Radius)); rec.SetFaceNormal(r, out_n); rec.Mat = s.Mat; rec.Valid = true; return; } //Hittables < Hittable type Hittables struct { Objects []Hittable } func (l *Hittables) Add(o Hittable) { l.Objects = append(l.Objects, o); } func (l Hittables) Hit(r Ray, ray_t Interval) (rec HitRecord) { closest_so_far := ray_t.Max; for _, o := range l.Objects { temp_rec := o.Hit(r, Interval{ray_t.Min, closest_so_far}); if temp_rec.Valid { rec = temp_rec; closest_so_far = rec.T; } } return; }