Browse code

Add implementation

Robert Cranston authored on 08/03/2026 03:36:49
Showing 1 changed files
... ...
@@ -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
Browse code

Add OpenGL boilerplate

Robert Cranston authored on 08/03/2026 00:46:06
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,199 @@
1
+/// Includes
2
+
3
+#include <ctime>
4
+#include <sstream>
5
+#include <fstream>
6
+#include <iostream>
7
+#include <iomanip>
8
+
9
+#include <GL/glew.h>
10
+#include <GLFW/glfw3.h>
11
+
12
+/// Constants
13
+
14
+constexpr auto width  = 640;
15
+constexpr auto height = 480;
16
+constexpr auto title  = "";
17
+
18
+/// Helpers
19
+
20
+//// Debug
21
+void GLAPIENTRY debug_message_callback(
22
+    GLenum          /* source   */,
23
+    GLenum          /* type     */,
24
+    GLuint          /* id       */,
25
+    GLenum          /* severity */,
26
+    GLsizei         /* length   */,
27
+    GLchar  const *    message,
28
+    void    const * /* param    */
29
+)
30
+{
31
+    std::cerr << message << std::endl;
32
+};
33
+
34
+//// Uniform
35
+void uniform(GLint location, int            value    ) { glUniform1i (location,    value); }
36
+void uniform(GLint location, bool           value    ) { glUniform1i (location,    value); }
37
+void uniform(GLint location, float          value    ) { glUniform1f (location,    value); }
38
+void uniform(GLint location, float const (& value)[1]) { glUniform1fv(location, 1, value); }
39
+void uniform(GLint location, float const (& value)[2]) { glUniform2fv(location, 1, value); }
40
+void uniform(GLint location, float const (& value)[3]) { glUniform3fv(location, 1, value); }
41
+void uniform(GLint location, float const (& value)[4]) { glUniform4fv(location, 1, value); }
42
+template<typename T>
43
+void uniform(char const * name, T const & value)
44
+{
45
+    GLuint program;   glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&program);
46
+    GLint  location = glGetUniformLocation(program, name);
47
+    uniform(location, value);
48
+}
49
+
50
+//// Write
51
+void write_tga(char const * base)
52
+{
53
+    char header[] = {
54
+        0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55
+        (char)(width  >> 0U),
56
+        (char)(width  >> 8U),
57
+        (char)(height >> 0U),
58
+        (char)(height >> 8U),
59
+        4*8, 0,
60
+    };
61
+    char data[4*width*height];
62
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
63
+    glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, data);
64
+    auto t  = std::time(nullptr);
65
+    auto pt = std::put_time(std::localtime(&t), "%Y-%m-%d_%H-%M-%S");
66
+    std::ofstream((std::ostringstream{} << base << "_" << pt << ".tga").str())
67
+        .write(header, sizeof(header))
68
+        .write(data,   sizeof(data));
69
+}
70
+
71
+/// Main
72
+
73
+int main(int /*argc*/, char * argv[])
74
+{
75
+    //// Window/context
76
+    glfwInit();
77
+    auto window = glfwCreateWindow(width, height, title, nullptr, nullptr);
78
+    glfwMakeContextCurrent(window);
79
+    glewInit();
80
+
81
+    //// Callbacks
82
+    glfwSetWindowUserPointer(window, argv[0]);
83
+    glfwSetKeyCallback(window,
84
+        [](GLFWwindow* w, int key, int /*scancode*/, int action, int /*mods*/)
85
+        {
86
+            auto ptr = glfwGetWindowUserPointer(w);
87
+            if (action != GLFW_RELEASE) return;
88
+            if (key == GLFW_KEY_Q) glfwSetWindowShouldClose(w, GLFW_TRUE);
89
+            if (key == GLFW_KEY_W) write_tga((char *)ptr);
90
+        }
91
+    );
92
+
93
+    //// Debug
94
+    glEnable(GL_DEBUG_OUTPUT);
95
+    glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
96
+    glDebugMessageCallback(debug_message_callback, nullptr);
97
+
98
+    //// Shader
99
+    auto make_program = [](char const * vert_source, char const * frag_source)
100
+    {
101
+        auto program = glCreateProgram();
102
+        auto vert    = glCreateShader(GL_VERTEX_SHADER);
103
+        auto frag    = glCreateShader(GL_FRAGMENT_SHADER);
104
+        glShaderSource(vert, 1, &vert_source, nullptr);
105
+        glShaderSource(frag, 1, &frag_source, nullptr);
106
+        glCompileShader(vert);
107
+        glCompileShader(frag);
108
+        glAttachShader(program, vert);
109
+        glAttachShader(program, frag);
110
+        glLinkProgram(program);
111
+        return program;
112
+    };
113
+    auto vert_source = R"(
114
+        #version 140
115
+
116
+        out vec2 tex_coord;
117
+
118
+        void main()
119
+        {
120
+            tex_coord = vec2[](
121
+                vec2(0, 0),
122
+                vec2(2, 0),
123
+                vec2(0, 2)
124
+            )[gl_VertexID];
125
+            gl_Position = vec4(tex_coord*2-1, 0, 1);
126
+        }
127
+    )";
128
+    auto tex_coord_program = make_program(vert_source, R"(
129
+        #version 140
130
+
131
+        uniform int   view;
132
+        uniform bool  debug;
133
+        uniform float time;
134
+
135
+        in vec2 tex_coord;
136
+
137
+        void main()
138
+        {
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
+            }
155
+
156
+            bvec3 debug_color = bvec3(
157
+                any(lessThan   (gl_FragColor, vec4(0))),
158
+                any(greaterThan(gl_FragColor, vec4(1))),
159
+                any(isinf      (gl_FragColor)) ||
160
+                any(isnan      (gl_FragColor))
161
+            );
162
+            if (debug && any(debug_color))
163
+                gl_FragColor = vec4(debug_color, 1);
164
+        }
165
+    )");
166
+
167
+    //// Main loop
168
+    int   view  = 1;
169
+    bool  debug = true;
170
+    float time  = (float)glfwGetTime();
171
+    while (glfwPollEvents(), !glfwWindowShouldClose(window))
172
+    {
173
+        ///// Time
174
+        float pt = time;
175
+        float dt = (time = (float)glfwGetTime()) - pt;
176
+
177
+        ///// Input
178
+        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;
182
+
183
+        ///// Draw
184
+        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);
190
+        glDrawArrays(GL_TRIANGLES, 0, 3);
191
+
192
+        ///// Swap
193
+        glfwSwapBuffers(window);
194
+    }
195
+
196
+    //// Cleanup
197
+    glfwDestroyWindow(window);
198
+    glfwTerminate();
199
+}