aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hitable.go39
-rw-r--r--material.go63
-rw-r--r--vec3d.go7
3 files changed, 88 insertions, 21 deletions
diff --git a/hitable.go b/hitable.go
index ef5822f..9f612d9 100644
--- a/hitable.go
+++ b/hitable.go
@@ -2,6 +2,7 @@ package main
import "math"
+// HitRecord contains data of a ray hiting a hitable
type HitRecord struct {
time float64
point Vec3D
@@ -9,44 +10,44 @@ type HitRecord struct {
material Material
}
+// Hitable is a physical volume a Ray can hit
type Hitable interface {
- Hit(ray Ray, t_min float64, t_max float64) HitRecord
+ Hit(ray Ray, tmin float64, tmax float64) HitRecord
Normal(point Vec3D, ray Ray) Vec3D
}
+// HitableList ...
type HitableList []Hitable
-func (hitable_list HitableList) Hit(ray Ray, t_min float64, t_max float64) HitRecord {
- closest_hit := HitRecord{T_MAX, Vec3D{}, Vec3D{}, nil}
- for _, hitable := range hitable_list {
- hit := hitable.Hit(ray, t_min, closest_hit.time)
+// Hit finds the closest hit of all hitables
+func (hitableList HitableList) Hit(ray Ray, tmin float64, tmax float64) HitRecord {
+ closestHit := HitRecord{TMax, Vec3D{}, Vec3D{}, nil}
+ for _, hitable := range hitableList {
+ hit := hitable.Hit(ray, tmin, closestHit.time)
if hit.time > 0 {
- closest_hit = hit
+ closestHit = hit
}
}
- if closest_hit.time < T_MAX {
- return closest_hit
- } else {
- return HitRecord{}
+ if closestHit.time < TMax {
+ return closestHit
}
+ return HitRecord{}
}
+// Sphere ...
type Sphere struct {
center Vec3D
radius float64
material Material
}
+// Normal ...
func (sphere Sphere) Normal(point Vec3D, ray Ray) Vec3D {
- out_normal := SubVec3D(point, sphere.center).Unit()
- if DotProduct(ray.direction, out_normal) < 0.0 {
- return out_normal
- } else {
- return out_normal.Invert()
- }
+ return SubVec3D(point, sphere.center).Unit()
}
-func (sphere Sphere) Hit(ray Ray, t_min float64, t_max float64) HitRecord {
+// Hit ...
+func (sphere Sphere) Hit(ray Ray, tmin float64, tmax float64) HitRecord {
oc := SubVec3D(ray.origin, sphere.center)
a := DotProduct(ray.direction, ray.direction)
b := 2.0 * DotProduct(oc, ray.direction)
@@ -55,12 +56,12 @@ func (sphere Sphere) Hit(ray Ray, t_min float64, t_max float64) HitRecord {
if discriminant > 0 {
root := math.Sqrt(discriminant)
time := (-b - root) / (2.0 * a)
- if t_min < time && time < t_max {
+ if tmin < time && time < tmax {
point := ray.PointAt(time)
return HitRecord{time, point, sphere.Normal(point, ray), sphere.material}
}
time = (-b + root) / (2.0 * a)
- if t_min < time && time < t_max {
+ if tmin < time && time < tmax {
point := ray.PointAt(time)
return HitRecord{time, point, sphere.Normal(point, ray), sphere.material}
}
diff --git a/material.go b/material.go
index a36fb52..4667bd6 100644
--- a/material.go
+++ b/material.go
@@ -1,27 +1,86 @@
package main
+import (
+ "math"
+ "math/rand"
+)
+
+// Material generate color when hitable is hit
type Material interface {
Scatter(ray Ray, hit HitRecord) (Ray, Color)
}
+// Lambertian is diffuse material
type Lambertian struct {
albedo Color
}
+// Scatter for Lambertians
func (lambertian Lambertian) Scatter(ray Ray, hit HitRecord) (Ray, Color) {
// TODO: Use the correct distribution
- direction := AddVec3D(hit.normal, RandomInUnitSphere())
+ var normal Vec3D
+ if DotProduct(ray.direction, hit.normal) < 0.0 {
+ normal = hit.normal
+ } else {
+ normal = hit.normal.Invert()
+ }
+ direction := AddVec3D(normal, RandomInUnitSphere())
scattered := Ray{hit.point, direction}
return scattered, lambertian.albedo
}
+// Metal material
type Metal struct {
albedo Color
fuzz float64
}
+// Scatter for Metal
func (metal Metal) Scatter(ray Ray, hit HitRecord) (Ray, Color) {
- reflected := AddVec3D(ray.direction.Unit().Reflect(hit.normal), ScalarProduct(metal.fuzz, RandomInUnitSphere()))
+ var normal Vec3D
+ if DotProduct(ray.direction, hit.normal) < 0.0 {
+ normal = hit.normal
+ } else {
+ normal = hit.normal.Invert()
+ }
+ reflected := AddVec3D(ray.direction.Unit().Reflect(normal), ScalarProduct(metal.fuzz, RandomInUnitSphere()))
scattered := Ray{hit.point, reflected}
return scattered, metal.albedo
}
+
+// Dielectric is glass for instance
+type Dielectric struct {
+ eta float64
+}
+
+// Scatter for Dielectric
+func (dielectric Dielectric) Scatter(ray Ray, hit HitRecord) (Ray, Color) {
+ var etaI, etaT float64
+ var normal Vec3D
+ attenuation := Color{1.0, 1.0, 1.0}
+ if DotProduct(ray.direction, hit.normal) < 0.0 {
+ etaI, etaT = 1.0, dielectric.eta
+ normal = hit.normal
+ } else {
+ etaI, etaT = 1.0, dielectric.eta
+ normal = hit.normal.Invert()
+ }
+ vecI := ray.direction.Unit()
+ cosTheta := FMin(DotProduct(vecI.Invert(), normal), 1.0)
+ sinTheta := math.Sqrt(1.0 - cosTheta*cosTheta)
+ if etaI/etaT*sinTheta > 1.0 || rand.Float64() < Schlick(cosTheta, etaI, etaT) {
+ reflected := ray.direction.Unit().Reflect(normal)
+ scattered := Ray{hit.point, reflected}
+ return scattered, attenuation
+ }
+ refracted := Refract(vecI, normal, etaI, etaT)
+ scattered := Ray{hit.point, refracted}
+ return scattered, attenuation
+}
+
+// Schlick glass refraction probability
+func Schlick(cosine float64, etaI float64, etaT float64) float64 {
+ r0 := (1.0 - etaI/etaT) / (1.0 + etaI/etaT)
+ r0 *= r0
+ return r0 + (1.0-r0)*math.Pow(1.0-cosine, 5)
+}
diff --git a/vec3d.go b/vec3d.go
index 9e4ced0..5b2a691 100644
--- a/vec3d.go
+++ b/vec3d.go
@@ -29,6 +29,13 @@ func (vec Vec3D) Reflect(normal Vec3D) Vec3D {
return SubVec3D(vec, ScalarProduct(2*DotProduct(vec, normal), normal))
}
+func Refract(vecI Vec3D, normal Vec3D, etaI float64, etaT float64) Vec3D {
+ cosTheta := FMin(DotProduct(vecI.Invert(), normal), 1.0)
+ vecTPar := ScalarProduct(etaI/etaT, AddVec3D(vecI, ScalarProduct(cosTheta, normal)))
+ vecTPer := ScalarProduct(-math.Sqrt(1.0-DotProduct(vecTPar, vecTPar)), normal)
+ return AddVec3D(vecTPar, vecTPer)
+}
+
func AddVec3D(vec1 Vec3D, vec2 Vec3D) Vec3D {
return Vec3D{vec1.x + vec2.x, vec1.y + vec2.y, vec1.z + vec2.z}
}