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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
//! Generates rays from screen coordinates
//!
//! Generates rays in world space from screen coordinates.
//! Future versions should also simulate depth of field.
//!
//! # Examples
//!
//! ```
//! use rendering::camera::{CameraSettings, Camera};
//! use rendering::core::{Vector3f, Vector2f, Vector2i};
//!
//! let set = CameraSettings {
//! origin: Vector3f::new(10.0),
//! target: Vector3f::new(0.0),
//! up: Vector3f::new_xyz(0.0, 1.0, 0.0),
//! fov: 90.0,
//! filmsize: Vector2i::new(10),
//! focus: None,
//! aperture: 0.0,
//! };
//!
//! let cam = Camera::new(&set);
//!
//! let (r, _) = cam.generate_ray(&Vector2f::new(5.0));
//! let dir = r.direction;
//!
//! assert!(
//! dir.x == -0.6031558065478413 &&
//! dir.y == -0.6599739684616743 &&
//! dir.z == -0.4479257014065748
//! );
//!
//! ```
use crate::Float;
use crate::core::{Vector3f, Vector2f, Vector2i, Ray};
use crate::sample::Sampler;
/// A simple perspective camera
pub struct Camera {
/// The camera origin in the screen
origin: Vector3f,
/// Vector from camera origin to the screen lower left corner of the film plane
screen_origin: Vector3f,
/// Scaling vectors from screen_origin
qx: Vector3f,
qy: Vector3f,
/// Value for depth of view
lens_radius: Option<Float>,
}
/// Settings for initializing camera
pub struct CameraSettings {
/// Where rays originate from
pub origin: Vector3f,
/// Point where center of image is pointed at
pub target: Vector3f,
/// Vector that will be up in the resulting image
pub up: Vector3f,
/// The vertical field of view in degrees.
/// Currently must be between [0; 180[.
pub fov: Float,
/// The film aspect ratio, height / width
pub filmsize: Vector2i,
/// The lens aperture
///
/// Depth of view is disabled if None
pub aperture: Option<Float>,
/// The distance to the focus plane
///
/// if None it will be set to the distance between origin and target
pub focus: Option<Float>,
}
impl Camera {
/// Create a new camera look at a target
pub fn new(set: &CameraSettings) -> Camera {
let filmsize = Vector2f::from(set.filmsize);
// Calculate translation vectors
let mut forward = set.target - set.origin;
let focus = set.focus.unwrap_or_else(|| forward.length());
forward.norm_in();
let right = set.up.cross(&forward).norm();
let newup = forward.cross(&right).norm();
let aspect = (filmsize.y) / (filmsize.x);
// Calculate screen size from fov and focus distance
let width = 2.0 * focus * (set.fov / 2.0).to_radians().tan();
let height = aspect * width;
// Calculate screen scaling vectors
let qx = right * (width / (filmsize.x - 1.0));
let qy = newup * (height / (filmsize.y - 1.0));
let screen_origin = forward * focus - (right * (width/2.0)) + (newup * (height/2.0));
Camera {
origin: set.origin,
screen_origin,
qx,
qy,
lens_radius: set.aperture.map(|a| a / 2.0),
}
}
/// Generates a ray a screen space point
///
/// The point coordinates should be between [0,1) with (0, 0) being the upper left corner
///
/// Will return a ray and a weight
///
/// The direction of the returned way is normalized
pub fn generate_ray(&self, point: &Vector2f, sampler: &mut dyn Sampler) -> (Ray, Float) {
// Depth of view origin offset
let ooffset = match self.lens_radius {
Some(r) => {
let rand_dir = sampler.get_in_circle() * r;
self.qx * rand_dir.x + self.qy * rand_dir.y
},
None => Vector3f::ZERO,
};
let dir = self.screen_origin + (self.qx * point.x) - (self.qy * point.y) - ooffset;
(
Ray { origin: self.origin + ooffset, direction: dir.norm() },
1.0
)
}
}
|