aboutsummaryrefslogtreecommitdiff
path: root/material.go
blob: 4667bd6084012cd01d67030ecdb49fea61fd17b8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
	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) {
	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)
}