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