package main import ( "fmt" maths "math" "os" "time" ) 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) { start := time.Now(); 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"); } dur := time.Since(start); fmt.Fprintf(os.Stderr, "Done, in %5.2f seconds! \n", dur.Seconds()); } 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}; }