diff options
author | Nat Lasseter <user@4574.co.uk> | 2024-06-24 16:01:18 +0100 |
---|---|---|
committer | Nat Lasseter <user@4574.co.uk> | 2024-06-24 16:01:18 +0100 |
commit | b7814d6822d03517fc846b95865965618be7d406 (patch) | |
tree | 2afc2a5de6d3ca82f465f51686d47dc032d2efb4 |
Initial Commit, finished Chapter 6
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | Readme | 8 | ||||
-rw-r--r-- | hittable.go | 87 | ||||
-rw-r--r-- | interval.go | 18 | ||||
-rw-r--r-- | ray.go | 26 | ||||
-rw-r--r-- | rtiaw.go | 64 | ||||
-rw-r--r-- | vec3.go | 65 |
8 files changed, 284 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fddca93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.swp +rtiaw +out.ppm diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7bcf3da --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +rtiaw: $(wildcard *.go) + go build + +out.ppm : rtiaw + ./$< > $@ + +.phony: view clean + +view: out.ppm + xv out.ppm + +clean: + rm -f rtiaw out.ppm @@ -0,0 +1,8 @@ +Ray Tracing in a Weekend in Go this time. + +I wanted to see what speedup I could get going from an interpreted +language to a compiled one. + +Cf. https://git.4574.co.uk/rtiaw.git/ + +https://raytracing.github.io/books/RayTracingInOneWeekend.html diff --git a/hittable.go b/hittable.go new file mode 100644 index 0000000..5320a0f --- /dev/null +++ b/hittable.go @@ -0,0 +1,87 @@ +package main + +import ( + maths "math" +) + +//HitRecord + +type HitRecord struct { + Valid bool + P Vec3 + N Vec3 + T float64 +} + +func (rec *HitRecord) SetFaceNormal(r Ray, out_n Vec3) { + if r.Direction.Dot(out_n) < 0 { + 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 +} + +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() - maths.Pow(s.Radius, 2); + disc := maths.Pow(h, 2) - 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(Splat(s.Radius)); + rec.SetFaceNormal(r, out_n); + 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; +} diff --git a/interval.go b/interval.go new file mode 100644 index 0000000..5f44f51 --- /dev/null +++ b/interval.go @@ -0,0 +1,18 @@ +package main + +type Interval struct { + Min float64 + Max float64 +} + +func (i Interval) Size() float64 { + return i.Max - i.Min; +} + +func (i Interval) Include(x float64) bool { + return i.Min <= x && x <= i.Max; +} + +func (i Interval) Surround(x float64) bool { + return i.Min < x && x < i.Max; +} @@ -0,0 +1,26 @@ +package main + +import ( + maths "math" +) + +type Ray struct { + Origin Vec3 + Direction Vec3 +} + +func (r Ray) At(t float64) Vec3 { + return r.Origin.Add(r.Direction.Mul(Splat(t))); +} + +func (r Ray) Colour(world Hittable) Vec3 { + hit := world.Hit(r, Interval{0, maths.Inf(1)}) + if hit.Valid { + return hit.N.Add(Splat(1)).Div(Splat(2)); + } + + unit_dir := r.Direction.Unit(); + a := (unit_dir.Y + 1) / 2; + return Vec3{0.5, 0.7, 1.0}.Mul(Splat(a)). + Add(Splat(1).Mul(Splat(1.0 - a))); +} diff --git a/rtiaw.go b/rtiaw.go new file mode 100644 index 0000000..9f900b6 --- /dev/null +++ b/rtiaw.go @@ -0,0 +1,64 @@ +package main + +import ( + "fmt" + "os" + "time" +) + +func main() { + start := time.Now(); + + image_width := 400; + aspect := 16.0 / 9.0; + + var world Hittables; + world.Add(Sphere{Vec3{0, 0 , -1}, 0.5}); + world.Add(Sphere{Vec3{0, -100.5, -1}, 100 }); + + image_height := int(float64(image_width) / aspect); + if image_height < 1 { + image_height = 1; + } + + focal_length := 1.0; + viewport_height := 2.0; + viewport_width := viewport_height * + (float64(image_width) / float64(image_height)); + camera_centre := Splat(0); + + viewport_u := Vec3{viewport_width, 0, 0}; + viewport_v := Vec3{0, -viewport_height, 0}; + + pd_u := viewport_u.Div(Splat(float64(image_width))); + pd_v := viewport_v.Div(Splat(float64(image_height))); + + viewport_upperleft := camera_centre. + Sub(Vec3{0, 0, focal_length}). + Sub(viewport_u.Div(Splat(2))). + Sub(viewport_v.Div(Splat(2))); + pixel00_loc := viewport_upperleft. + Add(pd_u.Add(pd_v).Div(Splat(2))); + + fmt.Printf("P3\n%d %d\n255\n", image_width, image_height); + for row := 0; row < image_height; row++ { + fmt.Fprintf(os.Stderr, + "[GScanlines remaining: %3d...", + (image_height - row)); + for col := 0; col < image_width; col++ { + pixel_centre := pixel00_loc. + Add(pd_u.Mul(Splat(float64(col)))). + Add(pd_v.Mul(Splat(float64(row)))); + ray_direction := pixel_centre.Sub(camera_centre); + ray := Ray{camera_centre, ray_direction} + pixel_colour := ray.Colour(world); + fmt.Printf("%s ", pixel_colour.ToPPM()); + } + fmt.Printf("\n"); + } + + dur := time.Since(start); + fmt.Fprintf(os.Stderr, + "[GDone, in %5.2f seconds! \n", + dur.Seconds()); +} @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + maths "math" +) + +type Vec3 struct { + X, Y, Z float64 +} + +func (v Vec3) Neg() Vec3 { + return Vec3{-v.X, -v.Y, -v.Z}; +} + +func (v Vec3) Add(w Vec3) Vec3 { + return Vec3{v.X + w.X, v.Y + w.Y, v.Z + w.Z}; +} + +func (v Vec3) Sub(w Vec3) Vec3 { + return Vec3{v.X - w.X, v.Y - w.Y, v.Z - w.Z}; +} + +func (v Vec3) Mul(w Vec3) Vec3 { + return Vec3{v.X * w.X, v.Y * w.Y, v.Z * w.Z}; +} + +func (v Vec3) Div(w Vec3) Vec3 { + return Vec3{v.X / w.X, v.Y / w.Y, v.Z / w.Z}; +} + +func (v Vec3) MagSqr() float64 { + return v.Dot(v); +} + +func (v Vec3) Mag() float64 { + return maths.Sqrt(v.MagSqr()); +} + +func (v Vec3) Unit() Vec3 { + m := v.Mag(); + return Vec3{v.X / m, v.Y / m, v.Z / m}; +} + +func (v Vec3) Dot(w Vec3) float64 { + return v.X * w.X + v.Y * w.Y + v.Z * w.Z; +} + +func (v Vec3) Cross(w Vec3) Vec3 { + return Vec3{v.Y * w.Z - v.Z * w.Y, + v.Z * w.X - v.X * w.Z, + v.X * w.Y - v.Y * w.X}; +} + +func (v Vec3) ToPPM() string { + r := uint8(255.999 * v.X); + g := uint8(255.999 * v.Y); + b := uint8(255.999 * v.Z); + + return fmt.Sprintf("%3d %3d %3d", r, g, b); +} + +func Splat(f float64) Vec3 { + return Vec3{f, f, f}; +} |