2024-01-25 19:41:11 +00:00
|
|
|
package main
|
|
|
|
|
2024-01-29 16:25:29 +00:00
|
|
|
import "fmt" // for IO and standard library
|
|
|
|
import "os" // for handling the progress bar
|
|
|
|
import "math" // for maths
|
2024-02-12 12:05:10 +00:00
|
|
|
import "unsafe"
|
2024-01-25 19:41:11 +00:00
|
|
|
|
2024-01-28 13:32:04 +00:00
|
|
|
// ================ 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
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (v Vec3) Add(v2 Vec3) Vec3 {
|
|
|
|
return NewVec3(v.E[0]+v2.E[0], v.E[1]+v2.E[1], v.E[2]+v2.E[2])
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (v Vec3) Mult(t float32) Vec3 {
|
|
|
|
return NewVec3(v.E[0]*t, v.E[1]*t, v.E[2]*t)
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (v Vec3) Div(t float32) Vec3 {
|
2024-01-28 13:32:04 +00:00
|
|
|
if t != 0 {
|
2024-02-03 21:48:01 +00:00
|
|
|
return NewVec3(v.E[0] / t, v.E[1] / t, v.E[2] / t)
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
2024-02-03 21:48:01 +00:00
|
|
|
return v
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
|
2024-01-28 13:32:04 +00:00
|
|
|
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])
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (v Vec3) Sub(v2 Vec3) Vec3 {
|
|
|
|
return NewVec3(v.E[0]-v2.E[0], v.E[1]-v2.E[1], v.E[2]-v2.E[2])
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (v Vec3) MultVec(v2 Vec3) Vec3 {
|
|
|
|
return NewVec3(v.E[0]*v2.E[0], v.E[1]*v2.E[1], v.E[2]*v2.E[2])
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (v Vec3) DivVec(v2 Vec3) Vec3 {
|
|
|
|
if v2.E[0] != 0 && v2.E[1] != 0 && v2.E[2] != 0 {
|
|
|
|
return NewVec3(v.E[0]/v2.E[0], v.E[1]/v2.E[1], v.E[2]/v2.E[2])
|
|
|
|
}
|
|
|
|
return v
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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])
|
|
|
|
}
|
|
|
|
|
2024-02-12 12:05:10 +00:00
|
|
|
func q_rsqrt(v Vec3) float32 {
|
|
|
|
var x float32 = v.E[0]*v.E[0] + v.E[1]*v.E[1] + v.E[2]*v.E[2]
|
|
|
|
i := *(*int32)(unsafe.Pointer(&x)) // evil floating point bit level hacking
|
|
|
|
i = 0x5f3759df - (i >> 1) // what the fuck?
|
|
|
|
y := *(*float32)(unsafe.Pointer(&i))
|
|
|
|
return y
|
|
|
|
}
|
|
|
|
|
2024-01-28 13:32:04 +00:00
|
|
|
func Unit_vector(v Vec3) Vec3 {
|
2024-02-12 12:05:10 +00:00
|
|
|
new_v := v.Mult(q_rsqrt(v))
|
2024-02-10 17:34:15 +00:00
|
|
|
return new_v
|
2024-01-28 13:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ============== 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]))
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func NewColor(e0, e1, e2 float32) Vec3 {
|
|
|
|
return Vec3{E: [3]float32{e0, e1, e2}}
|
|
|
|
}
|
|
|
|
|
2024-01-29 16:25:29 +00:00
|
|
|
// ============== RAY CLASS =================
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func NewPoint3(e0, e1, e2 float32) Vec3 {
|
|
|
|
return Vec3{E: [3]float32{e0, e1, e2}}
|
|
|
|
}
|
2024-01-29 16:25:29 +00:00
|
|
|
type Ray struct {
|
2024-02-03 21:48:01 +00:00
|
|
|
Orig Vec3
|
2024-01-29 16:25:29 +00:00
|
|
|
Dir Vec3
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func NewRay(orig Vec3, dir Vec3) *Ray {
|
2024-01-29 16:25:29 +00:00
|
|
|
return &Ray{Orig: orig, Dir: dir}
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (r *Ray) Origin() Vec3 {
|
2024-01-29 16:25:29 +00:00
|
|
|
return r.Orig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Ray) Direction() Vec3 {
|
|
|
|
return r.Dir
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func (r *Ray) At(t float32) Vec3 {
|
|
|
|
return Vec3{
|
|
|
|
E: [3]float32{
|
|
|
|
r.Orig.X() + t*r.Dir.X(),
|
|
|
|
r.Orig.Y() + t*r.Dir.Y(),
|
|
|
|
r.Orig.Z() + t*r.Dir.Z(),
|
|
|
|
},
|
2024-01-29 16:25:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
func Ray_color(r *Ray) Vec3 {
|
2024-02-12 12:05:10 +00:00
|
|
|
var t float32 = hit_sphere(NewPoint3(0,0,-1), 0.5, r)
|
|
|
|
if (t > 0.0) {
|
|
|
|
N := Unit_vector(r.At(t).Sub(NewVec3(0,0,-1)))
|
|
|
|
return NewColor(N.X()+1, N.Y()+1, N.Z()+1).Mult(0.5)
|
2024-02-10 17:34:15 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
unit_direction := Unit_vector(r.Direction())
|
2024-02-10 17:34:15 +00:00
|
|
|
a := (unit_direction.Y() + 1.0)*0.5
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
return NewColor(1.0,1.0,1.0).Mult(float32(1.0-a)).Add(NewColor(0.5,0.7,1.0).Mult(a))
|
|
|
|
}
|
|
|
|
|
2024-02-10 17:34:15 +00:00
|
|
|
// =============== SPHERE ===================
|
|
|
|
|
2024-02-12 12:05:10 +00:00
|
|
|
func hit_sphere(center Vec3, radius float32, r *Ray) float32 {
|
2024-02-10 17:34:15 +00:00
|
|
|
oc := r.Origin().Sub(center)
|
|
|
|
a := Dot(r.Direction(), r.Direction())
|
|
|
|
b := Dot(oc, r.Direction()) * 2.0
|
|
|
|
c := Dot(oc, oc) - radius*radius
|
|
|
|
discriminant := b*b - 4*a*c
|
2024-02-12 12:05:10 +00:00
|
|
|
if (discriminant < 0) {
|
|
|
|
return -1.0
|
2024-02-10 17:34:15 +00:00
|
|
|
} else {
|
2024-02-12 12:05:10 +00:00
|
|
|
return (-b-float32(math.Sqrt(float64(discriminant)))) / (2.0*a)
|
2024-02-10 17:34:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-28 13:32:04 +00:00
|
|
|
// =============== MAIN =====================
|
2024-01-25 19:41:11 +00:00
|
|
|
func main() {
|
|
|
|
|
|
|
|
// Image
|
2024-02-03 21:48:01 +00:00
|
|
|
aspect_ratio := 16.0 / 9.0;
|
2024-01-29 16:25:29 +00:00
|
|
|
var image_width int = 400;
|
2024-02-03 21:48:01 +00:00
|
|
|
|
|
|
|
// Calculate the image height
|
|
|
|
var image_height int = int(float64(image_width) / aspect_ratio)
|
2024-01-29 16:25:29 +00:00
|
|
|
if image_height < 1 {
|
|
|
|
image_height = 1
|
|
|
|
}
|
2024-02-03 21:48:01 +00:00
|
|
|
// Camera
|
|
|
|
var focal_length float32 = 1.0
|
|
|
|
var viewport_height float32 = 2.0
|
2024-02-10 17:34:15 +00:00
|
|
|
var viewport_width float32 = viewport_height * float32(image_width)/float32(image_height)
|
2024-02-03 21:48:01 +00:00
|
|
|
camera_center := NewVec3(0,0,0)
|
|
|
|
|
|
|
|
// Calculate the vectors across the horizontal and down the vertical viewport edges
|
|
|
|
viewport_u := NewVec3(viewport_width, 0, 0)
|
|
|
|
viewport_v := NewVec3(0, -viewport_height, 0)
|
|
|
|
|
|
|
|
//Calculate the horizontal and vertical delta vectors from pixel to pixel
|
|
|
|
pixel_delta_u := viewport_u.Div(float32(image_width))
|
|
|
|
pixel_delta_v := viewport_v.Div(float32(image_height))
|
|
|
|
|
|
|
|
// Calculate the location of the upper left pixel
|
|
|
|
viewport_upper_left := camera_center.Sub(camera_center).Sub(NewVec3(0, 0, focal_length)).Sub(viewport_u.Div(2)).Sub(viewport_v.Div(2))
|
|
|
|
pixel00_loc := viewport_upper_left.Add((pixel_delta_u.Add(pixel_delta_v)).Div(2))
|
|
|
|
// Rendering
|
2024-01-25 19:41:11 +00:00
|
|
|
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++ {
|
2024-01-28 13:32:04 +00:00
|
|
|
// 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)
|
|
|
|
|
2024-02-03 21:48:01 +00:00
|
|
|
//pixel_colour := NewVec3(float32(i) / float32(image_width - 1), float32(j) / float32(image_height-1), 0)
|
|
|
|
//Write_color(pixel_colour)
|
|
|
|
pixel_center := pixel00_loc.Add(pixel_delta_u.Mult(float32(i))).Add(pixel_delta_v.Mult(float32(j)))
|
|
|
|
ray_direction := pixel_center.Sub(camera_center)
|
|
|
|
r := NewRay(camera_center, ray_direction)
|
|
|
|
|
|
|
|
pixel_color := Ray_color(r)
|
|
|
|
Write_color(pixel_color)
|
2024-01-25 19:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// INFO: The pixels are written out in rows.
|
|
|
|
// Image file can be created with
|
2024-01-28 13:32:04 +00:00
|
|
|
// go run main.go > image.ppm
|
2024-01-25 19:41:11 +00:00
|
|
|
}
|