From b7814d6822d03517fc846b95865965618be7d406 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Mon, 24 Jun 2024 16:01:18 +0100 Subject: Initial Commit, finished Chapter 6 --- .gitignore | 3 +++ Makefile | 13 +++++++++ Readme | 8 ++++++ hittable.go | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ interval.go | 18 +++++++++++++ ray.go | 26 ++++++++++++++++++ rtiaw.go | 64 +++++++++++++++++++++++++++++++++++++++++++++ vec3.go | 65 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 284 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 Readme create mode 100644 hittable.go create mode 100644 interval.go create mode 100644 ray.go create mode 100644 rtiaw.go create mode 100644 vec3.go 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 diff --git a/Readme b/Readme new file mode 100644 index 0000000..39a0b43 --- /dev/null +++ b/Readme @@ -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; +} diff --git a/ray.go b/ray.go new file mode 100644 index 0000000..cd4f6aa --- /dev/null +++ b/ray.go @@ -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, + "Scanlines 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, + "Done, in %5.2f seconds! \n", + dur.Seconds()); +} diff --git a/vec3.go b/vec3.go new file mode 100644 index 0000000..65d14e5 --- /dev/null +++ b/vec3.go @@ -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}; +} -- cgit v1.2.3