package main import "fmt" // for IO and standard library import "os" // for handling the progress bar import "math" // for maths // ================ VEC3 CLASS ===================== type Vec3 struct { E [3]float32 } //Basic vector functions func NewVec3(e0, e1, e2 float32) Vec3 { return Vec3{E: [3]float32{e0, e1, e2}} } func (v Vec3) X() float32 { return v.E[0] } // can be executed as v.X() in main func (v Vec3) Y() float32 { return v.E[1] } func (v Vec3) Z() float32 { return v.E[2] } func (v Vec3) Neg() Vec3 { return NewVec3(-v.E[0], -v.E[1], -v.E[2]) } func (v Vec3) Get(i int) float32 { return v.E[i] } func (v *Vec3) Set(i int, val float32) { v.E[i] = val } func (v *Vec3) Add(v2 Vec3) { v.E[0] += v2.E[0] v.E[1] += v2.E[1] v.E[2] += v2.E[2] } func (v *Vec3) Mult(t float32) { v.E[0] *= t v.E[1] *= t v.E[2] *= t } func (v *Vec3) Div(t float32) { if t != 0 { v.E[0] /= t v.E[1] /= t v.E[2] /= t } } func (v Vec3) Length() float32 { return float32(math.Sqrt(float64(v.E[0]*v.E[0] + v.E[1]*v.E[1] + v.E[2]*v.E[2]))) } // Vector utility functions func (v Vec3) String() string { return fmt.Sprintf("%v %v %v", v.E[0], v.E[1], v.E[2]) } func (v *Vec3) Sub(v2 Vec3) { v.E[0] -= v2.E[0] v.E[1] -= v2.E[1] v.E[2] -= v2.E[2] } func (v *Vec3) MultVec(v2 Vec3) { v.E[0] *= v2.E[0] v.E[1] *= v2.E[1] v.E[2] *= v2.E[2] } func (v *Vec3) DivVec(v2 Vec3) { v.E[0] /= v2.E[0] v.E[1] /= v2.E[1] v.E[2] /= v2.E[2] } func Dot(v1 Vec3, v2 Vec3) float32 { return (v1.E[0]*v2.E[0] + v1.E[1]*v2.E[1] + v1.E[2]*v2.E[2]) } func Cross(v1 Vec3, v2 Vec3) Vec3 { return NewVec3(v1.E[1]*v2.E[2] - v1.E[2]*v2.E[1], v1.E[2]*v2.E[0] - v1.E[0]*v2.E[2], v1.E[0]*v2.E[1] - v1.E[1]*v2.E[0]) } func Unit_vector(v Vec3) Vec3 { v.Div(v.Length()) return v } // ============== COLOUR CLASS ============== func Write_color(v Vec3) { fmt.Println(int(255.999*v.E[0]), int(255.999*v.E[1]), int(255.999*v.E[2])) } // ============== RAY CLASS ================= type Point3 Vec3 type Ray struct { Orig Point3 Dir Vec3 } func NewRay(orig Point3, dir Vec3) *Ray { return &Ray{Orig: orig, Dir: dir} } func (r *Ray) Origin() Point3 { return r.Orig } func (r *Ray) Direction() Vec3 { return r.Dir } func (r *Ray) At(t float32) Point3 { return Point3{ X: r.Orig.X + t*r.Dir.X, Y: r.Orig.Y + t*r.Dir.Y, Z: r.Orig.Z + t*r.Dir.Z } } // =============== MAIN ===================== func main() { // Image var aspect_ratio := 16.0 / 9.0; var image_width int = 400; var image_height int = int(image_width / aspect_ratio) if image_height < 1 { image_height = 1 } // Viewport var viewport_height := 2.0 var viewport_width := viewport_height * float32(image_width/image_height) // Rendering fmt.Println("P3") fmt.Println(image_width, " ", image_height, "\n255") for j := 0; j < image_height; j++ { fmt.Fprintf(os.Stderr, "\rScanlines remaining: %d ", image_height-j) for i := 0; i < image_width; i++ { // r := float32(i) / float32(image_width - 1) // g := float32(j) / float32(image_height-1) // b := 0 // ir := int(r * 256) // ig := int(g * 256) // ib := int(b * 256) // fmt.Println(ir, " ", ig, " ", ib) pixel_colour := NewVec3(float32(i) / float32(image_width - 1), float32(j) / float32(image_height-1), 0) Write_color(pixel_colour) } } // INFO: The pixels are written out in rows. // Image file can be created with // go run main.go > image.ppm }