Skip to main content

grafix_toolbox/kit/opengl/utility/
sdf.rs

1use 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);