190 lines
3.3 KiB
Go
190 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"os"
|
|
)
|
|
|
|
// the book uses C++'s double, so we use float64 to match
|
|
type vec3 struct {
|
|
X float64
|
|
Y float64
|
|
Z float64
|
|
}
|
|
|
|
// point3 and color are just aliases for vec3.
|
|
type point3 = vec3
|
|
type color = vec3
|
|
|
|
func (v *vec3) invert() *vec3 {
|
|
return &vec3{-v.X, -v.Y, -v.Z}
|
|
}
|
|
|
|
func (v *vec3) add(i *vec3) *vec3 {
|
|
v.X += i.X
|
|
v.Y += i.Y
|
|
v.Z += i.Z
|
|
return v
|
|
}
|
|
|
|
func (v *vec3) mult(i float64) *vec3 {
|
|
v.X *= i
|
|
v.Y *= i
|
|
v.Z *= i
|
|
return v
|
|
}
|
|
|
|
func (v *vec3) div(i float64) *vec3 {
|
|
return v.mult(1 / i)
|
|
}
|
|
|
|
func (v *vec3) len() float64 {
|
|
return math.Sqrt(v.len_sq())
|
|
}
|
|
|
|
func (v *vec3) len_sq() float64 {
|
|
return v.X*v.X + v.Y*v.Y + v.Z*v.Z
|
|
}
|
|
|
|
func (v *vec3) String() string {
|
|
return fmt.Sprintf("%f %f %f", v.X, v.Y, v.Z)
|
|
}
|
|
|
|
func sum(a, b *vec3) *vec3 {
|
|
return &vec3{
|
|
a.X + b.X,
|
|
a.Y + b.Y,
|
|
a.Z + b.Z,
|
|
}
|
|
}
|
|
|
|
func sub(a, b *vec3) *vec3 {
|
|
return &vec3{
|
|
a.X - b.X,
|
|
a.Y - b.Y,
|
|
a.Z - b.Z,
|
|
}
|
|
}
|
|
|
|
func mult2(a, b *vec3) *vec3 {
|
|
return &vec3{
|
|
a.X * b.X,
|
|
a.Y * b.Y,
|
|
a.Z * b.Z,
|
|
}
|
|
}
|
|
|
|
func mult1(a *vec3, t float64) *vec3 {
|
|
return &vec3{
|
|
a.X * t,
|
|
a.Y * t,
|
|
a.Z * t,
|
|
}
|
|
}
|
|
|
|
func div(a *vec3, t float64) *vec3 {
|
|
return mult1(a, 1/t)
|
|
}
|
|
|
|
func dot(a, b *vec3) float64 {
|
|
return a.X*b.X + a.Y*b.Y + a.Z*b.Z
|
|
}
|
|
|
|
func cross(a, b *vec3) *vec3 {
|
|
return &vec3{
|
|
a.Y*b.Z - a.Z*b.Y,
|
|
a.Z*b.X - a.X*b.Z,
|
|
a.X*b.Y - a.Y*b.X,
|
|
}
|
|
}
|
|
|
|
func unit(v *vec3) *vec3 {
|
|
return v.div(v.len())
|
|
}
|
|
|
|
type ray struct {
|
|
origin *point3
|
|
direction *vec3
|
|
}
|
|
|
|
func (r *ray) at(t float64) *point3 {
|
|
return sum(r.origin, mult1(r.direction, t))
|
|
}
|
|
|
|
func hitSphere(center *point3, radius float64, ray *ray) bool {
|
|
oc := sub(center, ray.origin)
|
|
a := dot(ray.direction, ray.direction)
|
|
b := dot(ray.direction, oc) * -2.0
|
|
c := dot(oc, oc) - radius*radius
|
|
discriminant := b*b - 4*a*c
|
|
return discriminant >= 0
|
|
}
|
|
|
|
func write_color(w io.Writer, color *color) {
|
|
r := color.X
|
|
g := color.Y
|
|
b := color.Z
|
|
|
|
ir := int(255.999 * r)
|
|
ig := int(255.999 * g)
|
|
ib := int(255.999 * b)
|
|
|
|
fmt.Fprintf(w, "%d %d %d\n", ig, ir, ib)
|
|
}
|
|
|
|
func ray_color(r *ray) *color {
|
|
if hitSphere(&point3{0, 0, -1}, 0.5, r) {
|
|
return &color{0, 1, 0}
|
|
}
|
|
|
|
unitDirection := unit(r.direction)
|
|
a := 0.5 * (unitDirection.Y + 1.0)
|
|
return sum(mult1(&color{1.0, 1.0, 1.0}, 1.0-a), mult1(&color{0.7, 0.5, 1.0}, a))
|
|
}
|
|
|
|
func main() {
|
|
aspectRatio := 16.0 / 9.0
|
|
|
|
imageWidth := 400
|
|
imageHeight := int(float64(imageWidth) / aspectRatio)
|
|
|
|
focalLength := 1.0
|
|
viewportHeight := 2.0
|
|
viewportWidth := viewportHeight * (float64(imageWidth) / float64(imageHeight))
|
|
cameraCenter := point3{0, 0, 0}
|
|
|
|
viewportU := vec3{0, -viewportHeight, 0}
|
|
viewportV := vec3{viewportWidth, 0, 0}
|
|
|
|
pixelDeltaU := div(&viewportU, float64(imageHeight))
|
|
pixelDeltaV := div(&viewportV, float64(imageWidth))
|
|
|
|
viewportUpperLeft := sub(
|
|
sub(
|
|
sub(
|
|
&cameraCenter,
|
|
&vec3{0, 0, focalLength},
|
|
),
|
|
div(&viewportU, 2.0)),
|
|
div(&viewportV, 2.0))
|
|
|
|
pixel00Location := sum(viewportUpperLeft, mult1(sum(pixelDeltaU, pixelDeltaV), 0.5))
|
|
|
|
fmt.Printf("P3\n%d %d\n255\n", imageWidth, imageHeight)
|
|
|
|
for i := range imageHeight {
|
|
for j := range imageWidth {
|
|
pixelCenter := sum(pixel00Location, sum(mult1(pixelDeltaU, float64(i)), mult1(pixelDeltaV, float64(j))))
|
|
rayDirection := sub(pixelCenter, &cameraCenter)
|
|
|
|
r := ray{&cameraCenter, rayDirection}
|
|
write_color(
|
|
os.Stdout,
|
|
ray_color(&r),
|
|
)
|
|
}
|
|
}
|
|
}
|