retrofire_core/render/
shader.rs

1//! Fragment and vertex shaders.
2//!
3//! Shaders are functions that are used to customize vertex and fragment
4//! handling during rendering.
5//!
6//! A *vertex shader* is responsible for transforming and projecting each
7//! vertex in the rendered geometry, usually using a modelview matrix to move
8//! the vertex to the view (camera) space and a projection matrix to transform
9//! it to the clip space. Vertex shaders can also perform any other per-vertex
10//! calculations and pass on the results as attributes of the output vertex.
11//!
12//! A *fragment shader* is used to compute the color of each individual pixel,
13//! or fragment, drawn to the render target. The fragment shader receives as
14//! input any vertex attributes interpolated across the primitive being
15//! rasterized, such as color, texture coordinate, or normal vector.
16
17use crate::{
18    geom::Vertex,
19    math::{Color4, vec::ProjVec3},
20};
21
22use super::raster::Frag;
23
24/// Trait for vertex shaders, used to transform vertices and perform other
25/// per-vertex computations.
26///
27/// # Type parameters
28/// * `In`: Type of the input vertex.
29/// * `Uni`: Type of custom "uniform" (non-vertex-specific) data, such as
30///   transform matrices, passed to the shader.
31pub trait VertexShader<In, Uni> {
32    /// The type of the output vertex.
33    type Output;
34    /// Transforms `vertex` and does performs other per-vertex computations
35    /// needed, outputting a new vertex of type `Self::Output`. Custom data
36    /// that is not vertex-specific can be passed in the `uniform` parameter.
37    ///
38    /// # Panics
39    /// `shade_vertex` should never panic.
40    fn shade_vertex(&self, vertex: In, uniform: Uni) -> Self::Output;
41}
42
43/// Trait for fragment shaders, used to compute the color of each individual
44/// pixel, or fragment, rendered.
45///
46/// # Type parameters
47/// * `Var`: The varying of the input fragment.
48pub trait FragmentShader<Var> {
49    /// Computes the color of `frag`. Returns either `Some(color)`, or `None`
50    /// if the fragment should be discarded.
51    ///
52    /// # Panics
53    /// `shade_fragment` should never panic.
54    fn shade_fragment(&self, frag: Frag<Var>) -> Option<Color4>;
55}
56
57impl<F, In, Out, Uni> VertexShader<In, Uni> for F
58where
59    F: Fn(In, Uni) -> Out,
60{
61    type Output = Out;
62
63    fn shade_vertex(&self, vertex: In, uniform: Uni) -> Out {
64        self(vertex, uniform)
65    }
66}
67
68impl<F, Var, Out> FragmentShader<Var> for F
69where
70    F: Fn(Frag<Var>) -> Out,
71    Out: Into<Option<Color4>>,
72{
73    fn shade_fragment(&self, frag: Frag<Var>) -> Option<Color4> {
74        self(frag).into()
75    }
76}
77
78pub fn new<Vs, Fs, Vtx, Var, Uni>(vs: Vs, fs: Fs) -> Shader<Vs, Fs>
79where
80    Vs: VertexShader<Vtx, Uni, Output = Vertex<ProjVec3, Var>>,
81    Fs: FragmentShader<Var>,
82{
83    Shader::new(vs, fs)
84}
85
86/// A type that composes a vertex and a fragment shader.
87#[derive(Copy, Clone)]
88pub struct Shader<Vs, Fs> {
89    pub vertex_shader: Vs,
90    pub fragment_shader: Fs,
91}
92
93impl<Vs, Fs> Shader<Vs, Fs> {
94    /// Returns a new `Shader` with `vs` as the vertex shader
95    /// and `fs` as the fragment shader.
96    pub const fn new<In, Uni, Pos, Attr>(vs: Vs, fs: Fs) -> Self
97    where
98        Vs: VertexShader<In, Uni, Output = Vertex<Pos, Attr>>,
99        Fs: FragmentShader<Attr>,
100    {
101        Self {
102            vertex_shader: vs,
103            fragment_shader: fs,
104        }
105    }
106}
107
108impl<In, Vs, Fs, Uni> VertexShader<In, Uni> for Shader<Vs, Fs>
109where
110    Vs: VertexShader<In, Uni>,
111{
112    type Output = Vs::Output;
113
114    fn shade_vertex(&self, vertex: In, uniform: Uni) -> Self::Output {
115        self.vertex_shader.shade_vertex(vertex, uniform)
116    }
117}
118
119impl<Vs, Fs, Var> FragmentShader<Var> for Shader<Vs, Fs>
120where
121    Fs: FragmentShader<Var>,
122{
123    fn shade_fragment(&self, frag: Frag<Var>) -> Option<Color4> {
124        self.fragment_shader.shade_fragment(frag)
125    }
126}