From 6ed5b21890da5b4d1428680cbce24fc0a8392ed1 Mon Sep 17 00:00:00 2001 From: Crizomb <62544756+Crizomb@users.noreply.github.com> Date: Wed, 24 Sep 2025 23:26:46 +0200 Subject: [PATCH] Hopefully before really raymarching testing --- color.go | 5 + primitives_sdf.go | 34 +++++++ bouncing_ball.go => ray_marching.go | 0 sdf.go | 153 +++++++++++++++++++++++----- vec3.go | 41 +++++++- 5 files changed, 206 insertions(+), 27 deletions(-) create mode 100644 color.go create mode 100644 primitives_sdf.go rename bouncing_ball.go => ray_marching.go (100%) diff --git a/color.go b/color.go new file mode 100644 index 0000000..7679bbb --- /dev/null +++ b/color.go @@ -0,0 +1,5 @@ +package main + +type Color interface { + GetColor(Vector3) Vector3 +} diff --git a/primitives_sdf.go b/primitives_sdf.go new file mode 100644 index 0000000..843d82f --- /dev/null +++ b/primitives_sdf.go @@ -0,0 +1,34 @@ +package main + +// Sphere +type Sphere struct { + center Vector3 + radius float64 + color Color +} + +func (s Sphere) Distance(p Vector3) (float64, Color) { + return p.Sub(s.center).Length(), s.color +} + +// Box +type Box struct { + center Vector3 + dimensions Vector3 + color Color +} + +func (s Box) Distance(p Vector3) (float64, Color) { + q := p.Sub(s.center).Abs().Sub(s.dimensions) + return q.Max(0.0).Length() + min(max(q.X, max(q.Y, q.Z)), 0.0), s.color +} + +type Plane struct { + normal Vector3 + height float64 + color Color +} + +func (s Plane) Distance(p Vector3) (float64, Color) { + return p.Dot(s.normal) + s.height, s.color +} diff --git a/bouncing_ball.go b/ray_marching.go similarity index 100% rename from bouncing_ball.go rename to ray_marching.go diff --git a/sdf.go b/sdf.go index 1220641..9c70ab9 100644 --- a/sdf.go +++ b/sdf.go @@ -1,67 +1,168 @@ package main +import "math" + const EPS = 0.001 type SDF interface { - Distance(Vector3) float64 + 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 := (sdf.Distance(p.Add(Vector3{X: eps})) - sdf.Distance(p.Add(Vector3{X: -eps}))) / (2 * eps) - dy := (sdf.Distance(p.Add(Vector3{Y: eps})) - sdf.Distance(p.Add(Vector3{Y: -eps}))) / (2 * eps) - dz := (sdf.Distance(p.Add(Vector3{Z: eps})) - sdf.Distance(p.Add(Vector3{Z: -eps}))) / (2 * eps) + 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 { +type TranslatedSDF struct { primitive SDF translate Vector3 } -func (s translatedSDF) Distance(p Vector3) float64 { +func (s TranslatedSDF) Distance(p Vector3) (float64, Color) { return s.primitive.Distance(p.Sub(s.translate)) } -type rotatedSDF struct { +type RotatedSDF struct { primitive SDF rotVector Vector3 angle float64 } -func (s rotatedSDF) Distance(p Vector3) float64 { - rotated_p := rotate(p, s.rotVector, s.angle) +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 { +type ScaledSDF struct { primitive SDF scale float64 } -func (s scaledSDF) Distance(p Vector3) float64 { - return s.primitive.Distance(p.Scale(1/s.scale)) * s.scale +func (s ScaledSDF) Distance(p Vector3) (float64, Color) { + dist, color := s.primitive.Distance(p.Scale(1 / s.scale)) + return dist * s.scale, color } -type infiniteRep struct { +type RepeatSDF struct { primitive SDF cellSize Vector3 } -// TODO -// func (s infiniteRep) Distance(p Vector3) float64 { -// x, y, z := p.Unpack() -// sx, sy, sz := s.cellSize.Unpack() -// } - -// Sphere - -type Sphere struct { - center Vector3 - radius float64 +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)) } -func (s Sphere) Distance(p Vector3) float64 { - return p.Sub(s.center).Length() +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 } diff --git a/vec3.go b/vec3.go index 2ed1396..0e1729d 100644 --- a/vec3.go +++ b/vec3.go @@ -8,6 +8,10 @@ type Vector3 struct { X, Y, Z float64 } +func (u Vector3) GetColor(p Vector3) Vector3 { + return u +} + func (u Vector3) Add(v Vector3) Vector3 { return Vector3{u.X + v.X, u.Y + v.Y, u.Z + v.Z} } @@ -49,6 +53,19 @@ func (u Vector3) Round() Vector3 { return Vector3{round(u.X), round(u.Y), round(u.Z)} } +func (u Vector3) Abs() Vector3 { + abs := math.Abs + return Vector3{abs(u.X), abs(u.Y), abs(u.Z)} +} + +func (u Vector3) Max(x float64) Vector3 { + return Vector3{max(u.X, x), max(u.Y, x), max(u.Z, x)} +} + +func (u Vector3) Min(x float64) Vector3 { + return Vector3{min(u.X, x), min(u.Y, x), min(u.Z, x)} +} + // i incident, n normal. Both vector should be normalized func Reflect(i Vector3, n Vector3) Vector3 { y := i.Dot(n) @@ -58,7 +75,7 @@ func Reflect(i Vector3, n Vector3) Vector3 { // Todo : Refract // Rodrigues' rotation formula. rotVector should be normalized -func rotate(u Vector3, rotVector Vector3, angle float64) Vector3 { +func Rotate(u Vector3, rotVector Vector3, angle float64) Vector3 { cos, sin := math.Cos(angle), math.Sin(angle) vec1 := u.Scale(cos) vec2 := rotVector.Cross(u).Scale(sin) @@ -66,6 +83,28 @@ func rotate(u Vector3, rotVector Vector3, angle float64) Vector3 { return vec1.Add(vec2).Add(vec3) } +func Mix(u Vector3, v Vector3, k float64) Vector3 { + l := (1 - k) + return Vector3{u.X*l + v.X*k, u.Y*l + v.Y*k, u.Z*l + v.Z*k} +} + func (v Vector3) Unpack() (float64, float64, float64) { return v.X, v.Y, v.Z } + +// Others maths thingies + +func Clamp(x float64, a float64, b float64) float64 { + return min(max(x, a), b) +} + +func (v Vector3) Clamp(a float64, b float64) Vector3 { + x, y, z := v.Unpack() + x, y, z = Clamp(x, a, b), Clamp(y, a, b), Clamp(z, a, b) + return Vector3{x, y, z} +} + +func SmoothStep(x float64, a float64, b float64) float64 { + x = Clamp((x-a)/(b-a), 0, 1) + return x * x * (3.0 - 2.0*x) +}