package main import ( maths "math" ) //Material type Material interface { Attenuation() Vec3 Scatter(r Ray, hit HitRecord) Ray } //Lambertian < Material type Lambertian struct { Albedo Vec3 } func (l Lambertian) Attenuation() Vec3 { return l.Albedo; } func (l Lambertian) Scatter(r Ray, hit HitRecord) Ray { scat_dir := hit.N.Add(RandomVec3OnUnitSphere()); if scat_dir.NearZero() { scat_dir = hit.N; } return Ray{hit.P, scat_dir, true}; } //Metal < Material type Metal struct { Albedo Vec3 Fuzz float64 } func (m Metal) Attenuation() Vec3 { return m.Albedo; } func (m Metal) Scatter(r Ray, hit HitRecord) Ray { refl := r.Direction.Reflect(hit.N).Unit(). Add(SplatVec3(m.Fuzz).Mul(RandomVec3OnUnitSphere())); valid := refl.Dot(hit.N) > 0; return Ray{hit.P, refl, valid}; } //Dielectric < Material type Dielectric struct { RefractionIndex float64 } func (d Dielectric) Attenuation() Vec3 { return SplatVec3(1); } func (d Dielectric) Scatter(r Ray, hit HitRecord) Ray { ri := d.RefractionIndex; if hit.F { ri = 1.0 / ri; } unit_dir := r.Direction.Unit(); cos_theta := maths.Min(unit_dir.Neg().Dot(hit.N), 1.0); sin_theta := maths.Sqrt(1.0 - cos_theta * cos_theta); cannot_refract := ri * sin_theta > 1.0; var dir Vec3; if cannot_refract || schlick_reflect(cos_theta, ri) { dir = unit_dir.Reflect(hit.N); } else { dir = unit_dir.Refract(hit.N, ri); } return Ray{hit.P, dir, true}; } func schlick_reflect(cos_theta float64, ri float64) bool { r0 := (1 - ri) / (1 + ri); r0 = r0 * r0; ct5 := 1 - cos_theta; ct5 = ct5 * ct5 * ct5 * ct5 * ct5; approx := r0 + (1 - r0) * ct5; return approx > Interval{0, 1}.Sample(); }