wgpu_playground/
projection.rs

1//! Projection matrices that are intended to be used when the base coordinate system (i.e. the one used by the application code)
2//! is right-handed with the the x-axis pointing right, y-axis pointing *up*, and z-axis pointing *out of the screen*.
3
4/// Orthographic projection matrix.
5///
6/// This matrix is meant to be used when the source coordinate space is right-handed and y-up
7/// (the standard computer graphics coordinate space)and the destination space is left-handed
8/// and y-up, with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
9#[inline]
10pub fn orthographic(
11    left: f32,
12    right: f32,
13    bottom: f32,
14    top: f32,
15    near: f32,
16    far: f32,
17) -> [[f32; 4]; 4] {
18    let rml = right - left;
19    let rpl = right + left;
20    let tmb = top - bottom;
21    let tpb = top + bottom;
22    let fmn = far - near;
23    [
24        [2.0 / rml, 0.0, 0.0, 0.0],
25        [0.0, 2.0 / tmb, 0.0, 0.0],
26        [0.0, 0.0, -1.0 / fmn, 0.0],
27        [-(rpl / rml), -(tpb / tmb), -(near / fmn), 1.0],
28    ]
29}
30
31/// Perspective projection matrix.
32///
33/// * `vertical_fov` should be provided in radians.
34/// * `aspect_ratio` should be the quotient `width / height`.
35///
36/// This matrix is meant to be used when the source coordinate space is right-handed and y-up
37/// (the standard computer graphics coordinate space) and the destination coordinate space is
38/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
39#[inline]
40pub fn perspective(vertical_fov: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> [[f32; 4]; 4] {
41    let t = (vertical_fov / 2.0).tan();
42    let sy = 1.0 / t;
43    let sx = sy / aspect_ratio;
44    let nmf = z_near - z_far;
45
46    [
47        [sx, 0.0, 0.0, 0.0],
48        [0.0, sy, 0.0, 0.0],
49        [0.0, 0.0, z_far / nmf, -1.0],
50        [0.0, 0.0, z_near * z_far / nmf, 0.0],
51    ]
52}
53
54/// Perspective projection matrix with infinite z-far plane.
55///
56/// This is useful for extremely large scenes where having a far clip plane is extraneous anyway,
57/// as allowing it to approach infinity it eliminates several approximate numerical computations
58/// and so can improve z-fighting behavior.
59///
60/// * `vertical_fov` should be provided in radians.
61/// * `aspect_ratio` should be the quotient `width / height`.
62///
63/// This matrix is meant to be used when the source coordinate space is right-handed and y-up
64/// (the standard computer graphics coordinate space) and the destination coordinate space is
65/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
66#[inline]
67pub fn perspective_infinite_z(vertical_fov: f32, aspect_ratio: f32, z_near: f32) -> [[f32; 4]; 4] {
68    let t = (vertical_fov / 2.0).tan();
69    let sy = 1.0 / t;
70    let sx = sy / aspect_ratio;
71
72    [
73        [sx, 0.0, 0.0, 0.0],
74        [0.0, sy, 0.0, 0.0],
75        [0.0, 0.0, -1.0, -1.0],
76        [0.0, 0.0, -z_near, 0.0],
77    ]
78}
79
80/// Perspective projection matrix with reversed z-axis.
81///
82/// Reversed-Z provides significantly better precision and therefore reduced z-fighting
83/// for most depth situations, especially when a floating-point depth buffer is used. You'll want to use
84/// a reversed depth comparison function and depth clear value when using this projection.
85///
86/// * `vertical_fov` should be provided in radians.
87/// * `aspect_ratio` should be the quotient `width / height`.
88///
89/// This matrix is meant to be used when the source coordinate space is right-handed and y-up
90/// (the standard computer graphics coordinate space) and the destination coordinate space is
91/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
92#[inline]
93pub fn perspective_reversed_z(
94    vertical_fov: f32,
95    aspect_ratio: f32,
96    z_near: f32,
97    z_far: f32,
98) -> [[f32; 4]; 4] {
99    let t = (vertical_fov / 2.0).tan();
100    let sy = 1.0 / t;
101    let sx = sy / aspect_ratio;
102    let nmf = z_near - z_far;
103
104    [
105        [sx, 0.0, 0.0, 0.0],
106        [0.0, sy, 0.0, 0.0],
107        [0.0, 0.0, -z_far / nmf - 1.0, -1.0],
108        [0.0, 0.0, -z_near * z_far / nmf, 0.0],
109    ]
110}
111
112/// Perspective projection matrix with reversed and infinite z-axis.
113///
114/// Reversed-Z provides significantly better precision and therefore reduced z-fighting
115/// for most depth situations, especially when a floating-point depth buffer is used. You'll want to use
116/// a reversed depth comparison function and depth clear value when using this projection.
117///
118/// Infinte-Z is useful for extremely large scenes where having a far clip plane is extraneous anyway,
119/// as allowing it to approach infinity it eliminates several approximate numerical computations
120/// and so can improve z-fighting behavior.
121///
122/// Combining them gives the best of both worlds for large scenes.
123///
124/// * `vertical_fov` should be provided in radians.
125/// * `aspect_ratio` should be the quotient `width / height`.
126///
127/// This matrix is meant to be used when the source coordinate space is right-handed and y-up
128/// (the standard computer graphics coordinate space) and the destination coordinate space is
129/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
130#[inline]
131pub fn perspective_reversed_infinite_z(
132    vertical_fov: f32,
133    aspect_ratio: f32,
134    z_near: f32,
135) -> [[f32; 4]; 4] {
136    let t = (vertical_fov / 2.0).tan();
137    let sy = 1.0 / t;
138    let sx = sy / aspect_ratio;
139
140    [
141        [sx, 0.0, 0.0, 0.0],
142        [0.0, sy, 0.0, 0.0],
143        [0.0, 0.0, 0.0, -1.0],
144        [0.0, 0.0, z_near, 0.0],
145    ]
146}