ultraviolet/projection/
rh_ydown.rs

1//! Projection matrices that are intended to be used when the base coordinate
2//! system (i.e. the one used by the application code) is right-handed, with the
3//! x-axis pointing right, y-axis pointing *down*, and z-axis pointing *into the
4//! screen*.
5
6use crate::mat::*;
7use crate::vec::*;
8
9/// Orthographic projection matrix for use with OpenGL.
10///
11/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
12/// and the destination space is left-handed
13/// and y-up, with Z (depth) clip extending from -1.0 (close) to 1.0 (far).
14#[inline]
15pub fn orthographic_gl(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4 {
16    let rml = right - left;
17    let rpl = right + left;
18    let tmb = top - bottom;
19    let tpb = top + bottom;
20    let fmn = far - near;
21    let fpn = far + near;
22    Mat4::new(
23        Vec4::new(2.0 / rml, 0.0, 0.0, 0.0),
24        Vec4::new(0.0, -2.0 / tmb, 0.0, 0.0),
25        Vec4::new(0.0, 0.0, -2.0 / fmn, 0.0),
26        Vec4::new(-(rpl / rml), -(tpb / tmb), -(fpn / fmn), 1.0),
27    )
28}
29
30/// Orthographic projection matrix for use with OpenGL.
31///
32/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
33/// and the destination space is left-handed
34/// and y-down, with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
35#[inline]
36pub fn orthographic_vk(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4 {
37    let rml = right - left;
38    let rpl = right + left;
39    let tmb = top - bottom;
40    let tpb = top + bottom;
41    let fmn = far - near;
42    Mat4::new(
43        Vec4::new(2.0 / rml, 0.0, 0.0, 0.0),
44        Vec4::new(0.0, 2.0 / tmb, 0.0, 0.0),
45        Vec4::new(0.0, 0.0, -1.0 / fmn, 0.0),
46        Vec4::new(-(rpl / rml), -(tpb / tmb), -(near / fmn), 1.0),
47    )
48}
49
50/// Orthographic projection matrix for use with WebGPU or DirectX.
51///
52/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
53/// and the destination space is left-handed
54/// and y-up, with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
55#[inline]
56pub fn orthographic_wgpu_dx(
57    left: f32,
58    right: f32,
59    bottom: f32,
60    top: f32,
61    near: f32,
62    far: f32,
63) -> Mat4 {
64    let rml = right - left;
65    let rpl = right + left;
66    let tmb = top - bottom;
67    let tpb = top + bottom;
68    let fmn = far - near;
69    Mat4::new(
70        Vec4::new(2.0 / rml, 0.0, 0.0, 0.0),
71        Vec4::new(0.0, -2.0 / tmb, 0.0, 0.0),
72        Vec4::new(0.0, 0.0, -1.0 / fmn, 0.0),
73        Vec4::new(-(rpl / rml), -(tpb / tmb), -(near / fmn), 1.0),
74    )
75}
76
77/// Perspective projection matrix meant to be used with OpenGL.
78///
79/// * `vertical_fov` should be provided in radians.
80/// * `aspect_ratio` should be the quotient `width / height`.
81///
82/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
83/// (the standard computer graphics coordinate space) and the destination coordinate space is
84/// left-handed and y-up with Z (depth) clip extending from -1.0 (close) to 1.0 (far).
85#[inline]
86pub fn perspective_gl(vertical_fov: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Mat4 {
87    let t = (vertical_fov / 2.0).tan();
88    let sy = 1.0 / t;
89    let sx = sy / aspect_ratio;
90    let nmf = z_near - z_far;
91
92    Mat4::new(
93        Vec4::new(sx, 0.0, 0.0, 0.0),
94        Vec4::new(0.0, -sy, 0.0, 0.0),
95        Vec4::new(0.0, 0.0, (z_far + z_near) / nmf, -1.0),
96        Vec4::new(0.0, 0.0, 2.0 * z_near * z_far / nmf, 0.0),
97    )
98}
99
100/// Perspective projection matrix meant to be used with WebGPU or DirectX.
101///
102/// * `vertical_fov` should be provided in radians.
103/// * `aspect_ratio` should be the quotient `width / height`.
104///
105/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
106/// (the standard computer graphics coordinate space) and the destination coordinate space is
107/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
108#[inline]
109pub fn perspective_wgpu_dx(vertical_fov: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Mat4 {
110    let t = (vertical_fov / 2.0).tan();
111    let sy = 1.0 / t;
112    let sx = sy / aspect_ratio;
113    let nmf = z_near - z_far;
114
115    Mat4::new(
116        Vec4::new(sx, 0.0, 0.0, 0.0),
117        Vec4::new(0.0, -sy, 0.0, 0.0),
118        Vec4::new(0.0, 0.0, z_far / nmf, -1.0),
119        Vec4::new(0.0, 0.0, z_near * z_far / nmf, 0.0),
120    )
121}
122
123/// Perspective projection matrix meant to be used with Vulkan.
124///
125/// * `vertical_fov` should be provided in radians.
126/// * `aspect_ratio` should be the quotient `width / height`.
127///
128/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
129/// (the standard computer graphics coordinate space) and the destination coordinate space is
130/// right-handed and y-down with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
131#[inline]
132pub fn perspective_vk(vertical_fov: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Mat4 {
133    let t = (vertical_fov / 2.0).tan();
134    let sy = 1.0 / t;
135    let sx = sy / aspect_ratio;
136    let nmf = z_near - z_far;
137
138    Mat4::new(
139        Vec4::new(sx, 0.0, 0.0, 0.0),
140        Vec4::new(0.0, sy, 0.0, 0.0),
141        Vec4::new(0.0, 0.0, z_far / nmf, -1.0),
142        Vec4::new(0.0, 0.0, z_near * z_far / nmf, 0.0),
143    )
144}
145
146/// Perspective projection matrix with infinite z-far plane meant to be used with OpenGL.
147///
148/// This is useful for extremely large scenes where having a far clip plane is extraneous anyway,
149/// as allowing it to approach infinity it eliminates several approximate numerical computations
150/// and so can improve z-fighting behavior.
151///
152/// * `vertical_fov` should be provided in radians.
153/// * `aspect_ratio` should be the quotient `width / height`.
154///
155/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
156/// (the standard computer graphics coordinate space) and the destination coordinate space is
157/// left-handed and y-up with Z (depth) clip extending from -1.0 (close) to 1.0 (far).
158#[inline]
159pub fn perspective_infinite_z_gl(vertical_fov: f32, aspect_ratio: f32, z_near: f32) -> Mat4 {
160    let t = (vertical_fov / 2.0).tan();
161    let sy = 1.0 / t;
162    let sx = sy / aspect_ratio;
163
164    Mat4::new(
165        Vec4::new(sx, 0.0, 0.0, 0.0),
166        Vec4::new(0.0, -sy, 0.0, 0.0),
167        Vec4::new(0.0, 0.0, -1.0, -1.0),
168        Vec4::new(0.0, 0.0, -2.0 * z_near, 0.0),
169    )
170}
171
172/// Perspective projection matrix with infinite z-far plane meant to be used with Vulkan.
173///
174/// This is useful for extremely large scenes where having a far clip plane is extraneous anyway,
175/// as allowing it to approach infinity it eliminates several approximate numerical computations
176/// and so can improve z-fighting behavior.
177///
178/// * `vertical_fov` should be provided in radians.
179/// * `aspect_ratio` should be the quotient `width / height`.
180///
181/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
182/// (the standard computer graphics coordinate space) and the destination coordinate space is
183/// right-handed and y-down with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
184#[inline]
185pub fn perspective_infinite_z_vk(vertical_fov: f32, aspect_ratio: f32, z_near: f32) -> Mat4 {
186    let t = (vertical_fov / 2.0).tan();
187    let sy = 1.0 / t;
188    let sx = sy / aspect_ratio;
189
190    Mat4::new(
191        Vec4::new(sx, 0.0, 0.0, 0.0),
192        Vec4::new(0.0, sy, 0.0, 0.0),
193        Vec4::new(0.0, 0.0, -1.0, -1.0),
194        Vec4::new(0.0, 0.0, -z_near, 0.0),
195    )
196}
197
198/// Perspective projection matrix with infinite z-far plane meant to be used with WebGPU or DirectX.
199///
200/// This is useful for extremely large scenes where having a far clip plane is extraneous anyway,
201/// as allowing it to approach infinity it eliminates several approximate numerical computations
202/// and so can improve z-fighting behavior.
203///
204/// * `vertical_fov` should be provided in radians.
205/// * `aspect_ratio` should be the quotient `width / height`.
206///
207/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
208/// (the standard computer graphics coordinate space) and the destination coordinate space is
209/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
210#[inline]
211pub fn perspective_infinite_z_wgpu_dx(vertical_fov: f32, aspect_ratio: f32, z_near: f32) -> Mat4 {
212    let t = (vertical_fov / 2.0).tan();
213    let sy = 1.0 / t;
214    let sx = sy / aspect_ratio;
215
216    Mat4::new(
217        Vec4::new(sx, 0.0, 0.0, 0.0),
218        Vec4::new(0.0, -sy, 0.0, 0.0),
219        Vec4::new(0.0, 0.0, -1.0, -1.0),
220        Vec4::new(0.0, 0.0, -z_near, 0.0),
221    )
222}
223
224/// Perspective projection matrix with reversed z-axis meant to be used with WebGPU, DirectX, or OpenGL.
225///
226/// Reversed-Z provides significantly better precision and therefore reduced z-fighting
227/// for most depth situations, especially when a floating-point depth buffer is used. You'll want to use
228/// a reversed depth comparison function and depth clear value when using this projection.
229///
230/// * `vertical_fov` should be provided in radians.
231/// * `aspect_ratio` should be the quotient `width / height`.
232///
233/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
234/// (the standard computer graphics coordinate space) and the destination coordinate space is
235/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
236///
237/// **Note that in order for this to work properly with OpenGL, you'll need to use the `gl_arb_clip_control` extension
238/// and set the z clip from 0.0 to 1.0 rather than the default -1.0 to 1.0**
239#[inline]
240pub fn perspective_reversed_z_wgpu_dx_gl(
241    vertical_fov: f32,
242    aspect_ratio: f32,
243    z_near: f32,
244    z_far: f32,
245) -> Mat4 {
246    let t = (vertical_fov / 2.0).tan();
247    let sy = 1.0 / t;
248    let sx = sy / aspect_ratio;
249    let nmf = z_near - z_far;
250
251    Mat4::new(
252        Vec4::new(sx, 0.0, 0.0, 0.0),
253        Vec4::new(0.0, -sy, 0.0, 0.0),
254        Vec4::new(0.0, 0.0, -z_far / nmf - 1.0, -1.0),
255        Vec4::new(0.0, 0.0, -z_near * z_far / nmf, 0.0),
256    )
257}
258
259/// Perspective projection matrix with reversed z-axis meant to be used with Vulkan.
260///
261/// Reversed-Z provides significantly better precision and therefore reduced z-fighting
262/// for most depth situations, especially when a floating-point depth buffer is used. You'll want to use
263/// a reversed depth comparison function and depth clear value when using this projection.
264///
265/// * `vertical_fov` should be provided in radians.
266/// * `aspect_ratio` should be the quotient `width / height`.
267///
268/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
269/// (the standard computer graphics coordinate space) and the destination coordinate space is
270/// right-handed and y-down with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
271#[inline]
272pub fn perspective_reversed_z_vk(
273    vertical_fov: f32,
274    aspect_ratio: f32,
275    z_near: f32,
276    z_far: f32,
277) -> Mat4 {
278    let t = (vertical_fov / 2.0).tan();
279    let sy = 1.0 / t;
280    let sx = sy / aspect_ratio;
281    let nmf = z_near - z_far;
282
283    Mat4::new(
284        Vec4::new(sx, 0.0, 0.0, 0.0),
285        Vec4::new(0.0, sy, 0.0, 0.0),
286        Vec4::new(0.0, 0.0, z_far / nmf, -1.0),
287        Vec4::new(0.0, 0.0, -z_near * z_far / nmf, 0.0),
288    )
289}
290
291/// Perspective projection matrix with reversed and infinite z-axis meant to be used with WebGPU, OpenGL, or DirectX.
292///
293/// Reversed-Z provides significantly better precision and therefore reduced z-fighting
294/// for most depth situations, especially when a floating-point depth buffer is used. You'll want to use
295/// a reversed depth comparison function and depth clear value when using this projection.
296///
297/// Infinte-Z is useful for extremely large scenes where having a far clip plane is extraneous anyway,
298/// as allowing it to approach infinity it eliminates several approximate numerical computations
299/// and so can improve z-fighting behavior.
300///
301/// Combining them gives the best of both worlds for large scenes.
302///
303/// * `vertical_fov` should be provided in radians.
304/// * `aspect_ratio` should be the quotient `width / height`.
305///
306/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
307/// (the standard computer graphics coordinate space) and the destination coordinate space is
308/// left-handed and y-up with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
309///
310/// **Note that in order for this to work properly with OpenGL, you'll need to use the `gl_arb_clip_control` extension
311/// and set the z clip from 0.0 to 1.0 rather than the default -1.0 to 1.0**
312#[inline]
313pub fn perspective_reversed_infinite_z_wgpu_dx_gl(
314    vertical_fov: f32,
315    aspect_ratio: f32,
316    z_near: f32,
317) -> Mat4 {
318    let t = (vertical_fov / 2.0).tan();
319    let sy = 1.0 / t;
320    let sx = sy / aspect_ratio;
321
322    Mat4::new(
323        Vec4::new(sx, 0.0, 0.0, 0.0),
324        Vec4::new(0.0, -sy, 0.0, 0.0),
325        Vec4::new(0.0, 0.0, 0.0, -1.0),
326        Vec4::new(0.0, 0.0, z_near, 0.0),
327    )
328}
329
330/// Perspective projection matrix with reversed and infinite z-axis meant to be used with Vulkan.
331///
332/// Reversed-Z provides significantly better precision and therefore reduced z-fighting
333/// for most depth situations, especially when a floating-point depth buffer is used. You'll want to use
334/// a reversed depth comparison function and depth clear value when using this projection.
335///
336/// Infinte-Z is useful for extremely large scenes where having a far clip plane is extraneous anyway,
337/// as allowing it to approach infinity it eliminates several approximate numerical computations
338/// and so can improve z-fighting behavior.
339///
340/// Combining them gives the best of both worlds for large scenes.
341///
342/// * `vertical_fov` should be provided in radians.
343/// * `aspect_ratio` should be the quotient `width / height`.
344///
345/// This matrix is meant to be used when the source coordinate space is right-handed and y-down
346/// (the standard computer graphics coordinate space) and the destination coordinate space is
347/// right-handed and y-down with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
348#[inline]
349pub fn perspective_reversed_infinite_z_vk(
350    vertical_fov: f32,
351    aspect_ratio: f32,
352    z_near: f32,
353) -> Mat4 {
354    let t = (vertical_fov / 2.0).tan();
355    let sy = 1.0 / t;
356    let sx = sy / aspect_ratio;
357
358    Mat4::new(
359        Vec4::new(sx, 0.0, 0.0, 0.0),
360        Vec4::new(0.0, sy, 0.0, 0.0),
361        Vec4::new(0.0, 0.0, 0.0, -1.0),
362        Vec4::new(0.0, 0.0, z_near, 0.0),
363    )
364}