Browse code

Add support for writing to file

Robert Cranston authored on 08/03/2026 06:21:18
Showing 1 changed files
... ...
@@ -1,4 +1,5 @@
1 1
 #include <iostream>
2
+#include <fstream>
2 3
 
3 4
 #include <GL/glew.h>
4 5
 #include <GLFW/glfw3.h>
... ...
@@ -20,7 +21,7 @@ static void APIENTRY debug_message_callback(
20 21
     std::cerr << message << std::endl;
21 22
 };
22 23
 
23
-int main()
24
+int main(int argc, char const * argv[])
24 25
 {
25 26
     /// Window/context
26 27
     glfwInit();
... ...
@@ -181,6 +182,26 @@ int main()
181 182
         glfwSwapBuffers(window);
182 183
     }
183 184
 
185
+    /// Optionally write to file
186
+    if (argc >= 2)
187
+    {
188
+        auto ostream = std::ofstream(argv[1]);
189
+        ostream
190
+            << "P6"                   << "\n"
191
+            << width << " " << height << "\n"
192
+            << "255"                  << "\n";
193
+        char data[3*width*height];
194
+        glPixelStorei(GL_PACK_ALIGNMENT, 1);
195
+        for (int y = 0; y < height; ++y)
196
+            glReadPixels(
197
+                0,     y,
198
+                width, 1,
199
+                GL_RGB, GL_UNSIGNED_BYTE,
200
+                &data[3*width*(height-1-y)]
201
+            );
202
+        ostream.write(data, 3*width*height);
203
+    }
204
+
184 205
     /// Cleanup
185 206
     glfwDestroyWindow(window);
186 207
     glfwTerminate();
Browse code

Add implementation

Robert Cranston authored on 08/03/2026 03:36:49
Showing 1 changed files
... ...
@@ -3,8 +3,8 @@
3 3
 #include <GL/glew.h>
4 4
 #include <GLFW/glfw3.h>
5 5
 
6
-constexpr auto width  = 640;
7
-constexpr auto height = 480;
6
+constexpr auto width  = 419;
7
+constexpr auto height = 293;
8 8
 constexpr auto title  = "sdf-winding";
9 9
 
10 10
 static void APIENTRY debug_message_callback(
... ...
@@ -63,27 +63,118 @@ int main()
63 63
         glLinkProgram(program);
64 64
         return program;
65 65
     };
66
-    auto tex_coord_program = make_program(R"(
66
+    auto circle_program = make_program(R"(
67 67
         #version 140
68 68
 
69
+        uniform float smoothing;
70
+        uniform vec2  center;
71
+        uniform float radius;
72
+        uniform int   winding;
73
+
74
+        out float windings;
75
+
76
+        void main()
77
+        {
78
+            vec2  pos = gl_FragCoord.xy;
79
+            float sdf = length(pos - center) - radius;
80
+            windings = winding * smoothstep(
81
+                -smoothing/2,
82
+                +smoothing/2,
83
+                -sdf
84
+            );
85
+        }
86
+    )");
87
+    auto resolve_program = make_program(R"(
88
+        #version 140
89
+
90
+        uniform int       view;
91
+        uniform sampler2D windings;
92
+
69 93
         in vec2 tex_coord;
70 94
 
71 95
         void main()
72 96
         {
73
-            gl_FragColor = vec4(tex_coord, 0, 1);
97
+            float winding = texture(windings, tex_coord).r;
98
+            switch (view)
99
+            {
100
+                case 0: gl_FragColor = vec4(0.5 + 0.25 * winding); break;
101
+                case 1: gl_FragColor = vec4(abs(winding));         break;
102
+            }
74 103
         }
75 104
     )");
76 105
 
106
+    /// Texture
107
+    GLuint windings;
108
+    glGenTextures(1, &windings);
109
+    glBindTexture(GL_TEXTURE_2D, windings);
110
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
111
+    glTexImage2D(
112
+        GL_TEXTURE_2D, 0,
113
+        GL_R32F,
114
+        width, height, 0,
115
+        GL_RED, GL_FLOAT, nullptr
116
+    );
117
+
118
+    /// Framebuffer
119
+    GLuint framebuffer;
120
+    glGenFramebuffers(1, &framebuffer);
121
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
122
+    glFramebufferTexture(
123
+        GL_FRAMEBUFFER,
124
+        GL_COLOR_ATTACHMENT0,
125
+        windings,
126
+        0
127
+    );
128
+
77 129
     /// Draw loop
130
+    float smoothing = 10;
131
+    int   view      = 0;
132
+    struct Circle
133
+    {
134
+        float center[2];
135
+        float radius;
136
+        int   winding;
137
+    };
138
+    glEnable(GL_BLEND);
139
+    glBlendFunc(GL_ONE, GL_ONE);
140
+    glBlendEquation(GL_FUNC_ADD);
78 141
     while (glfwPollEvents(), !glfwWindowShouldClose(window))
79 142
     {
80 143
         //// Input
81 144
         if (glfwGetKey(window, GLFW_KEY_Q))
82 145
             glfwSetWindowShouldClose(window, GLFW_TRUE);
146
+        if (glfwGetKey(window, GLFW_KEY_J))
147
+            smoothing += 1;
148
+        if (glfwGetKey(window, GLFW_KEY_K))
149
+            smoothing -= smoothing < 1 ? smoothing : 1;
150
+        if (glfwGetKey(window, GLFW_KEY_H))
151
+            view = 0;
152
+        if (glfwGetKey(window, GLFW_KEY_L))
153
+            view = 1;
154
+
155
+        //// Generate smoothed windings
156
+        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
157
+        glClear(GL_COLOR_BUFFER_BIT);
158
+        glUseProgram(circle_program);
159
+        for (auto const & circle : {
160
+            Circle{{(249+ 69)/2.0+0.5, height-1-(251+ 71)/2.0+0.5}, (251- 71)/2.0, +1},
161
+            Circle{{(192+133)/2.0+0.5, height-1-(241+182)/2.0+0.5}, (241-182)/2.0, +1},
162
+            Circle{{(138+ 79)/2.0+0.5, height-1-(199+140)/2.0+0.5}, (199-140)/2.0, -1},
163
+            Circle{{(274+173)/2.0+0.5, height-1-(134+ 33)/2.0+0.5}, (134- 33)/2.0, -1},
164
+        })
165
+        {
166
+            glUniform1fv(glGetUniformLocation(circle_program, "smoothing"), 1, &smoothing);
167
+            glUniform2fv(glGetUniformLocation(circle_program, "center"),    1,  circle.center);
168
+            glUniform1fv(glGetUniformLocation(circle_program, "radius"),    1, &circle.radius);
169
+            glUniform1iv(glGetUniformLocation(circle_program, "winding"),   1, &circle.winding);
170
+            glDrawArrays(GL_TRIANGLES, 0, 3);
171
+        }
83 172
 
84
-        //// Draw
173
+        //// Resolve
174
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
85 175
         glClear(GL_COLOR_BUFFER_BIT);
86
-        glUseProgram(tex_coord_program);
176
+        glUseProgram(resolve_program);
177
+        glUniform1iv(glGetUniformLocation(resolve_program, "view"), 1, &view);
87 178
         glDrawArrays(GL_TRIANGLES, 0, 3);
88 179
 
89 180
         //// 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,96 @@
1
+#include <iostream>
2
+
3
+#include <GL/glew.h>
4
+#include <GLFW/glfw3.h>
5
+
6
+constexpr auto width  = 640;
7
+constexpr auto height = 480;
8
+constexpr auto title  = "sdf-winding";
9
+
10
+static void APIENTRY debug_message_callback(
11
+    GLenum          /* source   */,
12
+    GLenum          /* type     */,
13
+    GLuint          /* id       */,
14
+    GLenum          /* severity */,
15
+    GLsizei         /* length   */,
16
+    GLchar  const *    message,
17
+    void    const * /* param    */
18
+)
19
+{
20
+    std::cerr << message << std::endl;
21
+};
22
+
23
+int main()
24
+{
25
+    /// Window/context
26
+    glfwInit();
27
+    auto window = glfwCreateWindow(width, height, title, nullptr, nullptr);
28
+    glfwMakeContextCurrent(window);
29
+    glewInit();
30
+
31
+    /// Debug
32
+    glEnable(GL_DEBUG_OUTPUT);
33
+    glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
34
+    glDebugMessageCallback(debug_message_callback, nullptr);
35
+
36
+    /// Shader
37
+    auto make_program = [](char const * fragment_source)
38
+    {
39
+        auto program = glCreateProgram();
40
+        auto attach  = [&](GLenum type, char const * source)
41
+        {
42
+            auto shader = glCreateShader(type);
43
+            glShaderSource(shader, 1, &source, nullptr);
44
+            glCompileShader(shader);
45
+            glAttachShader(program, shader);
46
+        };
47
+        attach(GL_VERTEX_SHADER, R"(
48
+            #version 140
49
+
50
+            out vec2 tex_coord;
51
+
52
+            void main()
53
+            {
54
+                tex_coord = vec2[](
55
+                    vec2(0, 0),
56
+                    vec2(2, 0),
57
+                    vec2(0, 2)
58
+                )[gl_VertexID];
59
+                gl_Position = vec4(tex_coord*2-1, 0, 1);
60
+            }
61
+        )");
62
+        attach(GL_FRAGMENT_SHADER, fragment_source);
63
+        glLinkProgram(program);
64
+        return program;
65
+    };
66
+    auto tex_coord_program = make_program(R"(
67
+        #version 140
68
+
69
+        in vec2 tex_coord;
70
+
71
+        void main()
72
+        {
73
+            gl_FragColor = vec4(tex_coord, 0, 1);
74
+        }
75
+    )");
76
+
77
+    /// Draw loop
78
+    while (glfwPollEvents(), !glfwWindowShouldClose(window))
79
+    {
80
+        //// Input
81
+        if (glfwGetKey(window, GLFW_KEY_Q))
82
+            glfwSetWindowShouldClose(window, GLFW_TRUE);
83
+
84
+        //// Draw
85
+        glClear(GL_COLOR_BUFFER_BIT);
86
+        glUseProgram(tex_coord_program);
87
+        glDrawArrays(GL_TRIANGLES, 0, 3);
88
+
89
+        //// Swap
90
+        glfwSwapBuffers(window);
91
+    }
92
+
93
+    /// Cleanup
94
+    glfwDestroyWindow(window);
95
+    glfwTerminate();
96
+}