gl_utils/graphics.rs
1//! General graphics-related utility functions
2//!
3//! # Coordinates
4//!
5//! The `gl_Position` output of the last GLSL vertex processing stage (vertex
6//! shader, tessellation shader, or geometry shader) is in *clip space*, that is
7//! 4D homogeneous (projective) coordinates (x, y, z, w). During vertex
8//! post-processing, clip space coordinates are transformed to normalized device
9//! coordinates (NDCs) by perspective divide and clipped to the range
10//! [-1.0, 1.0] in the x, y, and z coorinates. Finally the viewport transform
11//! takes NDCs and outputs *screen space* (or *window space*) coordinates, which
12//! are 2D device coordinates + a 1D depth coordinate, on which rasterization is
13//! performed to produce fragments.
14//!
15//! **2D**
16//!
17//! 2D x,y coordinates normalized to [-1.0, 1.0] can be passed through to vertex
18//! post-processing unmodified. This is the case with shader pipelines that
19//! specify inputs are in "ClipSpace"; the pass-thru vertex shader will add a z
20//! component of 0.0 and a w component of 1.0.
21//!
22//! (Note: as of v0.6.0 none of the default draw2d resources use clip space
23//! rendering pipelines)
24//!
25//! Even though the ultimate output of vertex post-processing is 2D screen space
26//! (device) coordinates, in order to render screen-space input vertices, they
27//! must be transformed to clip space by vertex processing. Two utility
28//! functions are provided to convert between screen space and normalized
29//! coordinates: `graphics::screen_2d_to_ndc_2d` and
30//! `graphics::ndc_2d_to_screen_2d`. Both require screen dimensions as input.
31
32use math_utils as math;
33use math_utils::num_traits as num;
34use math_utils::vek;
35use crate::camera3d;
36
37/// Convenience method that calls `math::orthographic_rh_no` on the given
38/// `FrustmPlanes` struct
39#[inline]
40pub fn projection_mat_orthographic <S : num::real::Real> (
41 ortho : &vek::FrustumPlanes <S>
42) -> math::Matrix4 <S> {
43 math::Matrix4::orthographic_rh_no (*ortho)
44}
45
46/// Convenience method calling `Matrix4::perspective_rh_no` to construct a
47/// right-handed perspective matrix with -1 to 1 clip planes, equivalent to the
48/// `gluPerspective` function.
49///
50/// This will transform points in right-handed camera (view, eye) space
51/// (negative Z axis into the scene, positive Y axis 'up') into left-handed 4D
52/// homogenous clip space where the Z axis is reversed with positive Z into the
53/// screen while X and Y orientations remain unchanged.
54#[inline]
55pub fn projection_mat_perspective <S> (
56 perspective_fov : &camera3d::PerspectiveFov <S>
57) -> math::Matrix4 <S> where
58 S : num::real::Real + num::FloatConst + std::fmt::Debug
59{
60 math::Matrix4::perspective_rh_no (
61 perspective_fov.fovy.0,
62 perspective_fov.aspect,
63 perspective_fov.near,
64 perspective_fov.far
65 )
66}
67
68/// Convert screen coordinate to OpenGL NDC based on a given screen resolution.
69///
70/// # Examples
71///
72/// ```
73/// # extern crate gl_utils;
74/// # extern crate math_utils as math;
75/// # fn main () {
76/// # use gl_utils::graphics::screen_2d_to_ndc_2d;
77/// assert_eq!(
78/// screen_2d_to_ndc_2d ([640, 480].into(), [320.0, 240.0].into()),
79/// [0.0, 0.0].into()
80/// );
81/// math::approx::assert_relative_eq!(
82/// screen_2d_to_ndc_2d ([640, 480].into(), [480.0, 264.0].into()),
83/// [0.5, 0.1].into()
84/// );
85/// assert_eq!(
86/// screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 0.0].into()),
87/// [-1.0, -1.0].into()
88/// );
89/// assert_eq!(
90/// screen_2d_to_ndc_2d ([640, 480].into(), [0.0, 480.0].into()),
91/// [-1.0, 1.0].into()
92/// );
93/// assert_eq!(
94/// screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 0.0].into()),
95/// [1.0, -1.0].into()
96/// );
97/// assert_eq!(
98/// screen_2d_to_ndc_2d ([640, 480].into(), [640.0, 480.0].into()),
99/// [1.0, 1.0].into()
100/// );
101/// # }
102/// ```
103
104pub fn screen_2d_to_ndc_2d (
105 screen_dimensions : math::Vector2 <u16>,
106 screen_coord : math::Point2 <f32>
107) -> math::Point2 <f32> {
108 math::Point2::from ([
109 screen_coord.0.x as f32 / (0.5 * screen_dimensions.x as f32) - 1.0,
110 screen_coord.0.y as f32 / (0.5 * screen_dimensions.y as f32) - 1.0
111 ])
112}
113
114/// Maps OpenGL NDC coordinates to screen coordinates based on a given screen
115/// resolution.
116///
117/// # Examples
118///
119/// ```
120/// # extern crate gl_utils;
121/// # extern crate math_utils as math;
122/// # fn main () {
123/// # use gl_utils::graphics::ndc_2d_to_screen_2d;
124/// assert_eq!(
125/// ndc_2d_to_screen_2d ([640, 480].into(), [0.0, 0.0].into()),
126/// [320.0, 240.0].into()
127/// );
128/// assert_eq!(
129/// ndc_2d_to_screen_2d ([640, 480].into(), [0.5, 0.1].into()),
130/// [480.0, 264.0].into()
131/// );
132/// assert_eq!(
133/// ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, -1.0].into()),
134/// [0.0, 0.0].into()
135/// );
136/// assert_eq!(
137/// ndc_2d_to_screen_2d ([640, 480].into(), [-1.0, 1.0].into()),
138/// [0.0, 480.0].into()
139/// );
140/// assert_eq!(
141/// ndc_2d_to_screen_2d ([640, 480].into(), [1.0, -1.0].into()),
142/// [640.0, 0.0].into()
143/// );
144/// assert_eq!(
145/// ndc_2d_to_screen_2d ([640, 480].into(), [1.0, 1.0].into()),
146/// [640.0, 480.0].into()
147/// );
148/// # }
149/// ```
150
151pub fn ndc_2d_to_screen_2d (
152 screen_dimensions : math::Vector2 <u16>,
153 ndc_coord : math::Point2 <f32>
154) -> math::Point2 <f32> {
155 math::Point2::from ([
156 0.5 * (screen_dimensions.x as f32) * (1.0 + ndc_coord.0.x),
157 0.5 * (screen_dimensions.y as f32) * (1.0 + ndc_coord.0.y)
158 ])
159}