grafix_toolbox/kit/opengl/utility/
sdf.rs1use crate::GL::{gl, *};
2use crate::{lib::*, math::*};
3
4pub struct SdfGenerator {
5 dst_t: Shader,
6 dt_h: Shader,
7 sampl: Sampler,
8}
9impl SdfGenerator {
10 pub fn new(sampl: &[(GLenum, GLenum)]) -> Self {
11 let dst_t = Shader::pure([vs_mesh__2d_screen, ps_sdf__distance_transform_v]);
12 let dt_h = Shader::pure([vs_mesh__2d_screen, ps_sdf__distance_transform]);
13 let sampl = sampl
14 .iter()
15 .chain(&[(gl::TEXTURE_MAG_FILTER, gl::NEAREST), (gl::TEXTURE_MIN_FILTER, gl::NEAREST)])
16 .copied()
17 .collect_vec()
18 .into();
19 Self { dst_t, dt_h, sampl }
20 }
21 pub fn generate<S: TexSize, F: TexFmt, FROM: TexSize>(&mut self, tex: &Tex2d<S, F>, border: i32) -> Tex2d<RED, f16> {
22 ASSERT!(FROM::SIZE <= S::SIZE, "Wrong sdf source channel");
23 let s = tex.whdl().xy();
24 GLSave!(BLEND, DEPTH_TEST);
25 GLDisable!(BLEND, DEPTH_TEST);
26 let Self { dst_t, dt_h, sampl } = self;
27 let surf_out = Fbo::<RED, f16>::new(s);
28 let surf_in = Fbo::<RED, f16>::new(s);
29 {
30 let t = tex.Bind(sampl);
31 let s = Uniforms!(dst_t, ("iTex", t), ("iChannel", i32(FROM::SIZE)), ("iBorder", border), ("iStep", Vec2((0, 1)).div(s)));
32
33 let s = Uniform!(s, ("iSide", 1.));
34 surf_out.bind();
35 Screen::Draw();
36
37 let _ = Uniform!(s, ("iSide", -1.));
38 surf_in.bind();
39 Screen::Draw();
40 }
41 let out = Fbo::<RED, f16>::new(s);
42 {
43 let to = surf_out.tex.Bind(sampl);
44 let ti = surf_in.tex.Bind(sampl);
45 let _ = Uniforms!(dt_h, ("iOut", to), ("iIn", ti), ("iBorder", border), ("iStep", Vec2((1, 0)).div(s)));
46 out.bind();
47 Screen::Draw();
48 }
49 GLRestore!(BLEND, DEPTH_TEST);
50
51 out.tex
52 }
53}
54impl Default for SdfGenerator {
55 fn default() -> Self {
56 Self::new(&[(gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE), (gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE)])
57 }
58}
59
60SHADER!(
61 ps_sdf__distance_transform_v,
62 r"in vec2 glUV;
63 layout(location = 0) out float glFragColor;
64 uniform sampler2D iTex;
65 uniform int iChannel, iBorder;
66 uniform vec2 iStep;
67 uniform float iSide;
68
69 float get_tex(in vec2 p) {
70 if (1 == iChannel) return texture(iTex, glUV + p).r;
71 if (2 == iChannel) return texture(iTex, glUV + p).g;
72 if (3 == iChannel) return texture(iTex, glUV + p).b;
73 return texture(iTex, glUV + p).a;
74 }
75
76 void main() {
77 const float t = iSide * .5;
78 for (int _i = 0; _i < iBorder; ++_i) {
79 float i = float(_i);
80 vec2 o = iStep * i;
81 if (iSide * get_tex(o) > t || iSide * get_tex(-o) > t) {
82 glFragColor = i / iBorder;
83 return;
84 }
85 }
86
87 glFragColor = 1;
88 }"
89);
90
91SHADER!(
92 ps_sdf__distance_transform,
93 r"in vec2 glUV;
94 layout(location = 0) out float glFragColor;
95 uniform sampler2D iIn, iOut;
96 uniform int iBorder;
97 uniform vec2 iStep;
98
99 void main() {
100 float d_i = texture(iIn, glUV).r;
101 float d_o = texture(iOut, glUV).r;
102
103 for (int _i = 1; _i < iBorder; ++_i) {
104 float i = float(_i);
105 vec2 o = i * iStep;
106 vec2 o_p = glUV + o;
107 vec2 o_m = glUV - o;
108 i /= iBorder;
109 d_o = min(d_o, min(length(vec2(i, texture(iOut, o_p).r)), length(vec2(i, texture(iOut, o_m).r))));
110 d_i = min(d_i, min(length(vec2(i, texture(iIn, o_p).r)), length(vec2(i, texture(iIn, o_m).r))));
111 }
112
113 d_o = .5 - d_o * .5;
114 d_i = .5 + d_i * .5;
115
116 glFragColor = mix(d_o, d_i, float(d_i > .5));
117 }"
118);