use geng::prelude::*; const SHADER_SOURCE: &str = " varying vec2 v_uv; #ifdef VERTEX_SHADER attribute vec2 a_pos; uniform mat3 u_projection_matrix; uniform mat3 u_view_matrix; uniform mat3 u_model_matrix; void main() { v_uv = a_pos; vec3 pos = u_projection_matrix * u_view_matrix * u_model_matrix * vec3(a_pos, 1.0); gl_Position = vec4(pos.xy, 0.0, pos.z); } #endif #ifdef FRAGMENT_SHADER uniform sampler2D u_texture; uniform vec4 u_color; uniform vec4 u_border_color; uniform float u_outline_distance; float aa(float x) { float w = length(vec2(dFdx(x), dFdy(x))); return 1.0 - smoothstep(-w, w, x); } float read_sdf(sampler2D texture, vec2 uv) { return 1.0 - 2.0 * texture2D(texture, uv).x; } void main() { float dist = read_sdf(u_texture, v_uv); float inside = aa(dist); float inside_border = aa(dist - u_outline_distance); vec4 outside_color = vec4(u_border_color.xyz, 0.0); gl_FragColor = u_color * inside + (1.0 - inside) * ( u_border_color * inside_border + outside_color * (1.0 - inside_border) ); } #endif "; struct State { geng: Geng, program: ugli::Program, texture: ugli::Texture, font: geng::Font, } impl State { fn new(geng: &Geng) -> Self { let font = geng::Font::new( geng.ugli(), include_bytes!("../crates/geng-font/src/default.ttf"), &geng::font::Options { pixel_size: 64.0, max_distance: 0.25, antialias: true, ..default() }, ) .unwrap(); Self { geng: geng.clone(), program: geng.shader_lib().compile(SHADER_SOURCE).unwrap(), texture: font .create_text_sdf("Hello, Crabs", geng::TextAlign::CENTER, 64.0) .unwrap(), font, } } } impl geng::State for State { fn draw(&mut self, framebuffer: &mut ugli::Framebuffer) { ugli::clear(framebuffer, Some(Rgba::BLACK), None, None); let size = vec2(self.texture.size().map(|x| x as f32).aspect(), 1.0); ugli::draw( framebuffer, &self.program, ugli::DrawMode::TriangleFan, &ugli::VertexBuffer::new_dynamic( self.geng.ugli(), vec![ draw2d::Vertex { a_pos: vec2(0.0, 0.0), }, draw2d::Vertex { a_pos: vec2(1.0, 0.0), }, draw2d::Vertex { a_pos: vec2(1.0, 1.0), }, draw2d::Vertex { a_pos: vec2(0.0, 1.0), }, ], ), ( ugli::uniforms! { u_model_matrix: mat3::scale(size), u_texture: &self.texture, u_color: Rgba::WHITE, u_border_color: Rgba::GRAY, u_outline_distance: self.font.max_distance() / 2.0, }, geng::Camera2d { center: size / 2.0, rotation: Angle::ZERO, fov: 3.0, } .uniforms(framebuffer.size().map(|x| x as f32)), ), ugli::DrawParameters { blend_mode: Some(ugli::BlendMode::straight_alpha()), ..default() }, ); } } fn main() { logger::init(); geng::setup_panic_handler(); Geng::run("Font", |geng| async move { geng.run_state(State::new(&geng)).await; }); }