aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile13
-rw-r--r--Readme8
-rw-r--r--hittable.go87
-rw-r--r--interval.go18
-rw-r--r--ray.go26
-rw-r--r--rtiaw.go64
-rw-r--r--vec3.go65
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
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};
+}