offscreen/
offscreen.rs

1#[path = "common/runtime.rs"]
2mod runtime;
3
4use ombre::prelude::*;
5
6struct Renderer<T> {
7    display_pipeline: gfx::PipelineId,
8    display_bind: gfx::Bindings,
9    offscreen_pipeline: gfx::PipelineId,
10    offscreen_bind: gfx::Bindings,
11    offscreen_pass: gfx::PassId,
12    rx: f32,
13    ry: f32,
14    width: f64,
15    height: f64,
16    backend: T,
17}
18
19impl<T: gfx::RenderBackend> RuntimeRenderer for Renderer<T> {
20    type Error = T::Error;
21
22    fn window_resized(&mut self, size: platform::LogicalSize) {
23        self.width = size.width;
24        self.height = size.height;
25        self.backend.window_resized(size);
26    }
27
28    fn frame(&mut self) -> Result<(), Self::Error> {
29        let (width, height) = (self.width as f32, self.height as f32);
30        let proj = Transform3D::perspective(60.0f32, width / height, 0.01, 100.);
31        let view = Transform3D::<f32>::look_at(
32            Vector3D::new(0.0, 1.5, 9.0),
33            Vector3D::new(0.0, 0.0, 0.0),
34            Vector3D::new(0.0, 1.0, 0.0),
35        );
36        let view_proj = proj * view;
37
38        self.rx += 0.01;
39        self.ry += 0.03;
40
41        let model = Transform3D::rotation(self.ry, Vector3D::new(0., 1.0, 0.))
42            * Transform3D::<f32>::rotation(self.rx, Vector3D::new(1.0, 0., 0.));
43        let vs_params = display_shader::Uniforms {
44            mvp: view_proj * model,
45        };
46        let mut frame = self.backend.frame();
47
48        // Offscreen pass.
49        frame.begin_pass(
50            self.offscreen_pass,
51            gfx::PassAction::clear(gfx::color::Rgba::WHITE),
52        );
53        frame.apply_pipeline(&self.offscreen_pipeline)?;
54        frame.apply_bindings(&self.offscreen_bind)?;
55        frame.apply_uniforms(&vs_params)?;
56        frame.draw(0, 36, 1)?;
57        frame.end_pass();
58
59        // Display pass.
60        frame.begin_default_pass(gfx::PassAction::clear(gfx::color::Rgba::new(
61            0.0, 0., 0.45, 1.,
62        )));
63        frame.apply_pipeline(&self.display_pipeline)?;
64        frame.apply_bindings(&self.display_bind)?;
65        frame.apply_uniforms(&vs_params)?;
66        frame.draw(0, 36, 1)?;
67        frame.end_pass();
68        frame.commit();
69
70        Ok(())
71    }
72}
73
74impl<T: gfx::RenderBackend> Renderer<T> {
75    pub fn init(mut backend: T) -> Result<Self, Box<dyn std::error::Error>> {
76        let color_img = backend.texture(gfx::Texture {
77            data: gfx::Empty,
78            access: gfx::TextureAccess::RenderTarget,
79            size: Size::new(1024, 1024),
80            format: gfx::TextureFormat::Rgba8,
81            ..gfx::Texture::default()
82        })?;
83        let depth_img = backend.texture(gfx::Texture {
84            data: gfx::Empty,
85            size: Size::new(1024, 1024),
86            access: gfx::TextureAccess::RenderTarget,
87            format: gfx::TextureFormat::Depth,
88            ..gfx::Texture::default()
89        })?;
90        let offscreen_pass = backend.pass(gfx::PassDescriptor {
91            color_attachment: Some(color_img),
92            depth_attachment: Some(depth_img),
93        })?;
94
95        #[rustfmt::skip]
96        let vertices: &[f32] = &[
97            // position          color                   uvs
98            -1.0, -1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     0.0, 0.0,
99             1.0, -1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     1.0, 0.0,
100             1.0,  1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     1.0, 1.0,
101            -1.0,  1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     0.0, 1.0,
102
103            -1.0, -1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     0.0, 0.0,
104             1.0, -1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     1.0, 0.0,
105             1.0,  1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     1.0, 1.0,
106            -1.0,  1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     0.0, 1.0,
107
108            -1.0, -1.0, -1.0,    0.5, 0.5, 1.0, 1.0,     0.0, 0.0,
109            -1.0,  1.0, -1.0,    0.5, 0.5, 1.0, 1.0,     1.0, 0.0,
110            -1.0,  1.0,  1.0,    0.5, 0.5, 1.0, 1.0,     1.0, 1.0,
111            -1.0, -1.0,  1.0,    0.5, 0.5, 1.0, 1.0,     0.0, 1.0,
112
113             1.0, -1.0, -1.0,    1.0, 0.5, 0.0, 1.0,     0.0, 0.0,
114             1.0,  1.0, -1.0,    1.0, 0.5, 0.0, 1.0,     1.0, 0.0,
115             1.0,  1.0,  1.0,    1.0, 0.5, 0.0, 1.0,     1.0, 1.0,
116             1.0, -1.0,  1.0,    1.0, 0.5, 0.0, 1.0,     0.0, 1.0,
117
118            -1.0, -1.0, -1.0,    0.0, 0.5, 1.0, 1.0,     0.0, 0.0,
119            -1.0, -1.0,  1.0,    0.0, 0.5, 1.0, 1.0,     1.0, 0.0,
120             1.0, -1.0,  1.0,    0.0, 0.5, 1.0, 1.0,     1.0, 1.0,
121             1.0, -1.0, -1.0,    0.0, 0.5, 1.0, 1.0,     0.0, 1.0,
122
123            -1.0,  1.0, -1.0,    1.0, 0.0, 0.5, 1.0,     0.0, 0.0,
124            -1.0,  1.0,  1.0,    1.0, 0.0, 0.5, 1.0,     1.0, 0.0,
125             1.0,  1.0,  1.0,    1.0, 0.0, 0.5, 1.0,     1.0, 1.0,
126             1.0,  1.0, -1.0,    1.0, 0.0, 0.5, 1.0,     0.0, 1.0
127        ];
128
129        let vertex_buffer = backend.buffer(
130            gfx::BufferKind::Vertex,
131            gfx::BufferUsage::Immutable,
132            gfx::BufferSource::slice(vertices),
133        )?;
134
135        #[rustfmt::skip]
136        let indices: &[u8] = &[
137            0, 1, 2,  0, 2, 3,
138            6, 5, 4,  7, 6, 4,
139            8, 9, 10,  8, 10, 11,
140            14, 13, 12,  15, 14, 12,
141            16, 17, 18,  16, 18, 19,
142            22, 21, 20,  23, 22, 20
143        ];
144
145        let index_buffer = backend.buffer(
146            gfx::BufferKind::Index,
147            gfx::BufferUsage::Immutable,
148            gfx::BufferSource::slice(indices),
149        )?;
150
151        let offscreen_bind = gfx::Bindings {
152            vertex: vec![vertex_buffer],
153            index: Some(index_buffer),
154            textures: vec![],
155        };
156
157        let display_bind = gfx::Bindings {
158            vertex: vec![vertex_buffer],
159            index: Some(index_buffer),
160            textures: vec![color_img],
161        };
162
163        let source = gfx::ShaderSource {
164            vertex: display_shader::VERTEX,
165            fragment: display_shader::FRAGMENT,
166        };
167        let default_shader = backend.shader(source, display_shader::meta()).unwrap();
168
169        let display_pipeline = backend.pipeline(
170            default_shader,
171            gfx::PipelineDescriptor {
172                layout: &[gfx::BufferLayout {
173                    attrs: &[
174                        gfx::VertexAttribute::new("in_pos", gfx::VertexFormat::Float3),
175                        gfx::VertexAttribute::new("in_color", gfx::VertexFormat::Float4),
176                        gfx::VertexAttribute::new("in_uv", gfx::VertexFormat::Float2),
177                    ],
178                    ..gfx::BufferLayout::default()
179                }],
180                depth: Some(gfx::DepthState {
181                    compare: gfx::Comparison::LessOrEqual,
182                    offset: None,
183                }),
184                ..Default::default()
185            },
186        );
187
188        let source = gfx::ShaderSource {
189            vertex: offscreen_shader::VERTEX,
190            fragment: offscreen_shader::FRAGMENT,
191        };
192        let offscreen_shader = backend.shader(source, offscreen_shader::meta()).unwrap();
193        let offscreen_pipeline = backend.pipeline(
194            offscreen_shader,
195            gfx::PipelineDescriptor {
196                layout: &[gfx::BufferLayout {
197                    stride: Some(36),
198                    attrs: &[
199                        gfx::VertexAttribute::new("in_pos", gfx::VertexFormat::Float3),
200                        gfx::VertexAttribute::new("in_color", gfx::VertexFormat::Float4),
201                    ],
202                    ..Default::default()
203                }],
204                depth: Some(gfx::DepthState {
205                    compare: gfx::Comparison::LessOrEqual,
206                    offset: None,
207                }),
208                ..Default::default()
209            },
210        );
211
212        Ok(Self {
213            display_pipeline,
214            display_bind,
215            offscreen_pipeline,
216            offscreen_bind,
217            offscreen_pass,
218            height: 1.,
219            width: 1.,
220            rx: 0.,
221            ry: 0.,
222            backend,
223        })
224    }
225}
226
227fn main() -> Result<(), Box<dyn std::error::Error>> {
228    logger::init(log::Level::Debug)?;
229
230    let (mut win, events) =
231        platform::init("offscreen", 640, 480, &[], platform::GraphicsContext::Gl)?;
232    let ctx = unsafe { glow::Context::from_loader_function(|p| win.get_proc_address(p)) };
233    let backend = gfx::gl::Context::new(ctx, win.size())?;
234    let renderer = Renderer::init(backend)?;
235
236    runtime::run(win, events, renderer)?;
237
238    Ok(())
239}
240
241mod display_shader {
242    use super::*;
243
244    pub const VERTEX: &str = r#"
245    #version 100
246
247    attribute vec3 in_pos;
248    attribute vec4 in_color;
249    attribute vec2 in_uv;
250
251    varying lowp vec4 color;
252    varying lowp vec2 uv;
253
254    uniform mat4 mvp;
255
256    void main() {
257        gl_Position = mvp * vec4(in_pos, 1);
258        color = in_color;
259        uv = in_uv;
260    }
261    "#;
262
263    pub const FRAGMENT: &str = r#"
264    #version 100
265
266    varying lowp vec4 color;
267    varying lowp vec2 uv;
268
269    uniform sampler2D tex;
270
271    void main() {
272        gl_FragColor = color * texture2D(tex, uv);
273    }
274    "#;
275
276    pub fn meta() -> gfx::ShaderDescriptor {
277        gfx::ShaderDescriptor {
278            textures: vec!["tex".to_string()],
279            uniforms: gfx::UniformBlockLayout {
280                uniforms: vec![gfx::UniformDescriptor::new("mvp", gfx::UniformKind::Mat4)],
281            },
282        }
283    }
284
285    #[repr(C)]
286    pub struct Uniforms {
287        pub mvp: Transform3D,
288    }
289    unsafe impl bytes::Packed for Uniforms {}
290}
291
292mod offscreen_shader {
293    use super::*;
294
295    pub const VERTEX: &str = r#"
296    #version 100
297
298    attribute vec3 in_pos;
299    attribute vec4 in_color;
300
301    varying lowp vec4 color;
302
303    uniform mat4 mvp;
304
305    void main() {
306        gl_Position = mvp * vec4(in_pos, 1);
307        color = in_color;
308    }
309    "#;
310
311    pub const FRAGMENT: &str = r#"
312    #version 100
313
314    varying lowp vec4 color;
315
316    void main() {
317        gl_FragColor = color;
318    }
319    "#;
320
321    pub fn meta() -> gfx::ShaderDescriptor {
322        gfx::ShaderDescriptor {
323            textures: vec![],
324            uniforms: gfx::UniformBlockLayout {
325                uniforms: vec![gfx::UniformDescriptor::new("mvp", gfx::UniformKind::Mat4)],
326            },
327        }
328    }
329}