aboutsummaryrefslogtreecommitdiff
path: root/camera.go
blob: 8ca0664bc78a455959bb1e8a19a009f1ef86c090 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main

import (
  "fmt"
  maths "math"
  "os"
)

type Camera struct {
  ImageWidth uint
  ImageHeight uint
  Centre Vec3
  PDU Vec3
  PDV Vec3
  Pixel00 Vec3
  AntiAliasing uint
  MaxDepth uint
  DOF float64
  DefDiskU Vec3
  DefDiskV Vec3
}

func NewCamera(wi uint, ar float64,
               aa uint, md uint,
               vfov float64, lookfrom Vec3,
               lookat Vec3, vup Vec3,
               dof float64, fd float64) (cam Camera) {
  cam.ImageWidth = wi;
  cam.ImageHeight = uint(float64(cam.ImageWidth) / ar);
  if cam.ImageHeight < 1 {
    cam.ImageHeight = 1;
  }

  cam.AntiAliasing = aa;
  cam.MaxDepth = md;
  cam.Centre = lookfrom;
  cam.DOF = dof;

  theta := vfov * maths.Pi / 180;
  h := maths.Tan(theta / 2)
  viewport_height := 2.0 * h * fd;
  viewport_width := viewport_height *
                       float64(cam.ImageWidth) /
                       float64(cam.ImageHeight);

  w := lookfrom.Sub(lookat).Unit();
  u := vup.Cross(w).Unit();
  v := w.Cross(u);

  viewport_u := SplatVec3(viewport_width).Mul(u);
  viewport_v := SplatVec3(viewport_height).Mul(v.Neg());

  cam.PDU = viewport_u.Div(SplatVec3(float64(cam.ImageWidth)));
  cam.PDV = viewport_v.Div(SplatVec3(float64(cam.ImageHeight)));

  viewport_upperleft := cam.Centre.
                        Sub(SplatVec3(fd).Mul(w)).
                        Sub(viewport_u.Div(SplatVec3(2))).
                        Sub(viewport_v.Div(SplatVec3(2)));
  cam.Pixel00 = viewport_upperleft.
                 Add(cam.PDU.Add(cam.PDV).Div(SplatVec3(2)));

  def_radius := fd * maths.Tan((cam.DOF / 2) * (maths.Pi / 180));
  cam.DefDiskU = SplatVec3(def_radius).Mul(u);
  cam.DefDiskV = SplatVec3(def_radius).Mul(v);

  return;
}

func (cam Camera) Render(world Hittable) {
  fmt.Printf("P3\n%d %d\n255\n", cam.ImageWidth, cam.ImageHeight);
  for row := uint(0); row < cam.ImageHeight; row++ {
    fmt.Fprintf(os.Stderr,
                "Scanlines remaining: %3d...",
                (cam.ImageHeight - row));
    for col := uint(0); col < cam.ImageWidth; col++ {
      pixel_colour := SplatVec3(0);
      for i := uint(0); i < cam.AntiAliasing; i++ {
        r := cam.GetRay(row, col);
        pixel_colour = pixel_colour.Add(r.Colour(world, cam.MaxDepth));
      }
      pixel_colour = pixel_colour.
                     Div(SplatVec3(float64(cam.AntiAliasing)));
      fmt.Printf("%s    ", pixel_colour.ToPPM());
    }
    fmt.Printf("\n");
  }
}

func (cam Camera) GetRay(row uint, col uint) Ray {
  offset := unit_square();
  pixel_sample := cam.Pixel00.
                  Add(cam.PDU.Mul(SplatVec3(float64(col) + offset.X))).
                  Add(cam.PDV.Mul(SplatVec3(float64(row) + offset.Y)));
  origin := cam.Centre;
  if cam.DOF > 0 {
    p := RandomVec3OnUnitDisk();
    origin = cam.Centre.
              Add(SplatVec3(p.X).Mul(cam.DefDiskU)).
              Add(SplatVec3(p.Y).Mul(cam.DefDiskV));
  }
  direction := pixel_sample.Sub(origin);
  return Ray{origin, direction, true};
}

func unit_square() Vec3 {
  i := Interval{-0.5, 0.5};
  return Vec3{i.Sample(), i.Sample(), 0};
}