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