Browse code

Add implementation

Robert Cranston authored on 08/03/2026 03:36:49
Showing 2 changed files

... ...
@@ -16,6 +16,18 @@ To build, run `make`. You need the following dependencies on your system:
16 16
 -   [GLEW][]
17 17
 -   [GLFW][]
18 18
 
19
+The code prioritizes conciseness and takes a number of shortcuts:
20
+
21
+-   We don't actually generate the SDFs and winding numbers from contours, we
22
+    just hard code simple circle SDFs with given winding numbers.
23
+-   We don't always take care to explicitly bind OpenGL state at the point of
24
+    use if we happen to know that the currently bound thing is correct or
25
+    harmless, which we often do for this simple program.
26
+-   We abuse the blend mode to add winding numbers, to avoid juggling multiple
27
+    textures. If you're using clever geometry tricks to generate the SDFs this
28
+    might be a problem. You might be able to use some overdraw reducing
29
+    technique, like a depth pre-pass, to mitigate that.
30
+
19 31
 [`sdf-winding`]:  https://git.rcrnstn.net/rcrnstn/sdf-winding
20 32
 [glyph]:          https://en.wikipedia.org/wiki/Glyph
21 33
 [winding number]: https://en.wikipedia.org/wiki/Winding_number
... ...
@@ -11,9 +11,9 @@
11 11
 
12 12
 /// Constants
13 13
 
14
-constexpr auto width  = 640;
15
-constexpr auto height = 480;
16
-constexpr auto title  = "";
14
+constexpr auto width  = 419;
15
+constexpr auto height = 293;
16
+constexpr auto title  = "sdf-winding";
17 17
 
18 18
 /// Helpers
19 19
 
... ...
@@ -125,33 +125,44 @@ int main(int /*argc*/, char * argv[])
125 125
             gl_Position = vec4(tex_coord*2-1, 0, 1);
126 126
         }
127 127
     )";
128
-    auto tex_coord_program = make_program(vert_source, R"(
128
+    auto circle_program = make_program(vert_source, R"(
129 129
         #version 140
130 130
 
131
-        uniform int   view;
132
-        uniform bool  debug;
133
-        uniform float time;
131
+        uniform float sharpness;
132
+        uniform vec2  center;
133
+        uniform float radius;
134
+        uniform int   winding;
135
+
136
+        out float windings;
137
+
138
+        void main()
139
+        {
140
+            vec2  pos = gl_FragCoord.xy;
141
+            float sdf = length(pos - center) - radius;
142
+            windings = winding * 1 / (1 + exp2(sharpness*sdf));
143
+        }
144
+    )");
145
+    auto resolve_program = make_program(vert_source, R"(
146
+        #version 140
147
+
148
+        uniform int       view;
149
+        uniform bool      debug;
150
+        uniform float     sharpness;
151
+        uniform float     smoothing;
152
+        uniform sampler2D windings;
134 153
 
135 154
         in vec2 tex_coord;
136 155
 
137 156
         void main()
138 157
         {
139
-            gl_FragColor = vec4(fract(tex_coord - float(!debug)*time), 0, 1);
140
-
141
-            // Exercise the debugging colors.
142
-            // <https://www.desmos.com/calculator/uijwefecqs>
143
-            if (view >= 2)
144
-            {
145
-                float x = tex_coord.x;
146
-                x -= 0.5;
147
-                x *= 10;
148
-                x  = clamp(x, -3, +3);
149
-                x /= (3 + x) * (3 - x);
150
-                x *= (3 + 1) * (3 - 1);
151
-                x *= 0.5;
152
-                x += 0.5;
153
-                gl_FragColor = vec4(x / (1 / float(tex_coord.y > 0.5)));
154
-            }
158
+            float winding = texture(windings, tex_coord).r;
159
+            if (view == 1) winding = 0.5 + 0.25 * winding;                        // view packed, remapped
160
+            if (view >= 2) winding = clamp(abs(winding), 0, 1);                   // discard sign/sum
161
+            if (view == 3) winding = 0.5 + 0.25 * winding;                        // view flattened, remapped
162
+            if (view >= 4) winding = log2(1 / winding - 1) / sharpness;           // unpack (reversible)
163
+            if (view == 5) winding = winding / 100;                               // view unpacked, expanded
164
+            if (view >= 6) winding = smoothstep(-smoothing, +smoothing, winding); // smooth
165
+            gl_FragColor = vec4(vec3(winding), 1);
155 166
 
156 167
             bvec3 debug_color = bvec3(
157 168
                 any(lessThan   (gl_FragColor, vec4(0))),
... ...
@@ -164,10 +175,53 @@ int main(int /*argc*/, char * argv[])
164 175
         }
165 176
     )");
166 177
 
178
+    /// Texture
179
+    GLuint windings;
180
+    glGenTextures(1, &windings);
181
+    glBindTexture(GL_TEXTURE_2D, windings);
182
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
183
+    glTexImage2D(
184
+        GL_TEXTURE_2D, 0,
185
+        GL_R32F,
186
+        width, height, 0,
187
+        GL_RED, GL_FLOAT, nullptr
188
+    );
189
+
190
+    /// Framebuffer
191
+    GLuint framebuffer;
192
+    glGenFramebuffers(1, &framebuffer);
193
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
194
+    glFramebufferTexture(
195
+        GL_FRAMEBUFFER,
196
+        GL_COLOR_ATTACHMENT0,
197
+        windings,
198
+        0
199
+    );
200
+
201
+    //// Data
202
+    struct Circle
203
+    {
204
+        GLfloat center[2];
205
+        GLfloat radius;
206
+        GLint   winding;
207
+    }
208
+    circles[] =
209
+    {
210
+        {{(249+ 69)/2.0+0.5, height-1-(251+ 71)/2.0+0.5}, (251- 71)/2.0, +1},
211
+        {{(192+133)/2.0+0.5, height-1-(241+182)/2.0+0.5}, (241-182)/2.0, +1},
212
+        {{(138+ 79)/2.0+0.5, height-1-(199+140)/2.0+0.5}, (199-140)/2.0, -1},
213
+        {{(274+173)/2.0+0.5, height-1-(134+ 33)/2.0+0.5}, (134- 33)/2.0, -1},
214
+    };
215
+
167 216
     //// Main loop
168
-    int   view  = 1;
169
-    bool  debug = true;
170
-    float time  = (float)glfwGetTime();
217
+    int     view      = 1;
218
+    bool    debug     = true;
219
+    float   time      = (float)glfwGetTime();
220
+    float   sharpness = 1;
221
+    float   smoothing = 5;
222
+    glEnable(GL_BLEND);
223
+    glBlendFunc(GL_ONE, GL_ONE);
224
+    glBlendEquation(GL_FUNC_ADD);
171 225
     while (glfwPollEvents(), !glfwWindowShouldClose(window))
172 226
     {
173 227
         ///// Time
... ...
@@ -176,17 +230,37 @@ int main(int /*argc*/, char * argv[])
176 230
 
177 231
         ///// Input
178 232
         for (int num = 0; num <= 9; ++num)
179
-        if (glfwGetKey(window, GLFW_KEY_0 + num)) view  = num;
180
-        if (glfwGetKey(window, GLFW_KEY_R))       debug = false;
181
-        if (glfwGetKey(window, GLFW_KEY_E))       debug = true;
233
+        if (glfwGetKey(window, GLFW_KEY_0 + num)) view       = num;
234
+        if (glfwGetKey(window, GLFW_KEY_E))       debug      = true;
235
+        if (glfwGetKey(window, GLFW_KEY_R))       debug      = false;
236
+        if (glfwGetKey(window, GLFW_KEY_H))       sharpness -= 0.5F * dt;
237
+        if (glfwGetKey(window, GLFW_KEY_L))       sharpness += 0.5F * dt;
238
+        if (glfwGetKey(window, GLFW_KEY_J))       smoothing -= 5.0F * dt;
239
+        if (glfwGetKey(window, GLFW_KEY_K))       smoothing += 5.0F * dt;
240
+        sharpness = std::max(0.1F, sharpness);
241
+        smoothing = std::max(0.0F, smoothing);
242
+
243
+        ///// Generate smoothed windings
244
+        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
245
+        glClear(GL_COLOR_BUFFER_BIT);
246
+        glUseProgram(circle_program);
247
+        for (auto const & circle : circles)
248
+        {
249
+            uniform("sharpness", sharpness);
250
+            uniform("center",    circle.center);
251
+            uniform("radius",    circle.radius);
252
+            uniform("winding",   circle.winding);
253
+            glDrawArrays(GL_TRIANGLES, 0, 3);
254
+        }
182 255
 
183
-        ///// Draw
256
+        ///// Resolve
257
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
184 258
         glClear(GL_COLOR_BUFFER_BIT);
185
-        glUseProgram(tex_coord_program);
186
-        uniform("view",  view);
187
-        uniform("debug", debug);
188
-        uniform("time",  time);
189
-        uniform("dt",    dt);
259
+        glUseProgram(resolve_program);
260
+        uniform("view",      view);
261
+        uniform("debug",     debug);
262
+        uniform("sharpness", sharpness);
263
+        uniform("smoothing", smoothing);
190 264
         glDrawArrays(GL_TRIANGLES, 0, 3);
191 265
 
192 266
         ///// Swap