package main import "math" const EPS = 0.001 type SDF interface { Distance(Vector3) (float64, Color) } func DistanceOnly(sdf SDF, p Vector3) float64 { dist, _ := sdf.Distance(p) return dist } func Gradient(sdf SDF, p Vector3, eps float64) Vector3 { dx := (DistanceOnly(sdf, p.Add(Vector3{X: eps})) - DistanceOnly(sdf, p.Add(Vector3{X: -eps}))) / (2 * eps) dy := (DistanceOnly(sdf, p.Add(Vector3{Y: eps})) - DistanceOnly(sdf, p.Add(Vector3{Y: -eps}))) / (2 * eps) dz := (DistanceOnly(sdf, p.Add(Vector3{Z: eps})) - DistanceOnly(sdf, p.Add(Vector3{Z: -eps}))) / (2 * eps) return Vector3{X: dx, Y: dy, Z: dz} } // Some transformations see https://iquilezles.org/articles/distfunctions/ type TranslatedSDF struct { primitive SDF translate Vector3 } func (s TranslatedSDF) Distance(p Vector3) (float64, Color) { return s.primitive.Distance(p.Sub(s.translate)) } type RotatedSDF struct { primitive SDF rotVector Vector3 angle float64 } func (s RotatedSDF) Distance(p Vector3) (float64, Color) { rotated_p := Rotate(p, s.rotVector, s.angle) return s.primitive.Distance(rotated_p) } type ScaledSDF struct { primitive SDF scale float64 } func (s ScaledSDF) Distance(p Vector3) (float64, Color) { dist, color := s.primitive.Distance(p.Scale(1 / s.scale)) return dist * s.scale, color } type RepeatSDF struct { primitive SDF cellSize Vector3 } func (s RepeatSDF) Distance(p Vector3) (float64, Color) { x, y, z := p.Unpack() sx, sy, sz := s.cellSize.Unpack() round := math.RoundToEven nearest_cell := Vector3{sx * round(x/sx), sy * round(y/sy), sz * round(z/sz)} return s.primitive.Distance(p.Sub(nearest_cell)) } type UnionSDF struct { primitive1 SDF primitive2 SDF } func (s UnionSDF) Distance(p Vector3) (float64, Color) { d1, color1 := s.primitive1.Distance(p) d2, color2 := s.primitive2.Distance(p) d := math.Min(d1, d2) color := color1 if d2 < d1 { color = color2 } return d, color } type SubstractionSDF struct { primitive1 SDF primitive2 SDF } func (s SubstractionSDF) Distance(p Vector3) (float64, Color) { d1, color1 := s.primitive1.Distance(p) d2, _ := s.primitive2.Distance(p) d := math.Max(-d1, d2) return d, color1 } type IntersectionSDF struct { primitive1 SDF primitive2 SDF } func (s IntersectionSDF) Distance(p Vector3) (float64, Color) { d1, color1 := s.primitive1.Distance(p) d2, color2 := s.primitive2.Distance(p) c1 := color1.GetColor(p) c2 := color2.GetColor(p) d := math.Max(d1, d2) color := c1.Add(c2).Scale(0.5) return d, color } type SmoothUnionSDF struct { primitive1 SDF primitive2 SDF k float64 } func (s SmoothUnionSDF) Distance(p Vector3) (float64, Color) { k := 4 * s.k d1, color1 := s.primitive1.Distance(p) d2, color2 := s.primitive2.Distance(p) c1 := color1.GetColor(p) c2 := color2.GetColor(p) h := math.Max(k-math.Abs(d1-d2), 0.0) d := math.Min(d1, d2) - h*h*0.25/k t := SmoothStep(d2-d1, -k, k) color := Mix(c1, c2, t) return d, color } type SmoothSubstractionSDF struct { primitive1 SDF primitive2 SDF k float64 } func (s SmoothSubstractionSDF) Distance(p Vector3) (float64, Color) { k := 4 * s.k d1, color1 := s.primitive1.Distance(p) d2, color2 := s.primitive2.Distance(p) c1 := color1.GetColor(p) c2 := color2.GetColor(p) h := math.Max(k-math.Abs(-d1-d2), 0.0) d := math.Max(-d1, d2) + h*h*0.25/k t := SmoothStep(d2-d1, -k, k) color := Mix(c1, c2, t) return d, color } type SmoothIntersectionSDF struct { primitive1 SDF primitive2 SDF k float64 } func (s SmoothIntersectionSDF) Distance(p Vector3) (float64, Color) { k := 4 * s.k d1, color1 := s.primitive1.Distance(p) d2, color2 := s.primitive2.Distance(p) c1 := color1.GetColor(p) c2 := color2.GetColor(p) d := math.Max(k-math.Abs(d1-d2), 0.0) t := SmoothStep(d2-d1, -k, k) color := Mix(c1, c2, t) return d, color }