aboutsummaryrefslogtreecommitdiff
path: root/material.go
blob: 6b3a52810472a4280c593fdfbb9e9980dc903cc3 (plain)
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
87
88
89
package main

import (
  maths "math"
)

//Material

type Material interface {
  Attenuation() Vec3
  Scatter(r Ray, hit HitRecord) Ray
}

//Lambertian < Material

type Lambertian struct {
  Albedo Vec3
}

func (l Lambertian) Attenuation() Vec3 {
  return l.Albedo;
}

func (l Lambertian) Scatter(r Ray, hit HitRecord) Ray {
  scat_dir := hit.N.Add(RandomVec3OnUnitSphere());
  if scat_dir.NearZero() {
    scat_dir = hit.N;
  }
  return Ray{hit.P, scat_dir, true};
}

//Metal < Material

type Metal struct {
  Albedo Vec3
  Fuzz float64
}

func (m Metal) Attenuation() Vec3 {
  return m.Albedo;
}

func (m Metal) Scatter(r Ray, hit HitRecord) Ray {
  refl := r.Direction.Reflect(hit.N).Unit().
          Add(SplatVec3(m.Fuzz).Mul(RandomVec3OnUnitSphere()));
  valid := refl.Dot(hit.N) > 0;
  return Ray{hit.P, refl, valid};
}

//Dielectric < Material

type Dielectric struct {
  RefractionIndex float64
}

func (d Dielectric) Attenuation() Vec3 {
  return SplatVec3(1);
}

func (d Dielectric) Scatter(r Ray, hit HitRecord) Ray {
  ri := d.RefractionIndex;
  if hit.F {
    ri = 1.0 / ri;
  }

  unit_dir := r.Direction.Unit();
  cos_theta := maths.Min(unit_dir.Neg().Dot(hit.N), 1.0);
  sin_theta := maths.Sqrt(1.0 - cos_theta * cos_theta);

  cannot_refract := ri * sin_theta > 1.0;

  var dir Vec3;
  if cannot_refract || schlick_reflect(cos_theta, ri) {
    dir = unit_dir.Reflect(hit.N);
  } else {
    dir = unit_dir.Refract(hit.N, ri);
  }

  return Ray{hit.P, dir, true};
}

func schlick_reflect(cos_theta float64, ri float64) bool {
  r0 := (1 - ri) / (1 + ri);
  r0 = r0 * r0;
  ct5 := 1 - cos_theta;
  ct5 = ct5 * ct5 * ct5 * ct5 * ct5;
  approx := r0 + (1 - r0) * ct5;
  return approx > Interval{0, 1}.Sample();
}