linehacks.c revision 32001f49
1/**
2 * Test some hacks to approximate wide lines, AA lines and stippled lines
3 * by drawing the lines multiple times at offsets and stippling with a fragment
4 * shader.
5 *
6 * Brian Paul
7 * June 2011
8 */
9
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <math.h>
14#include <GL/glew.h>
15#include "glut_wrap.h"
16#include "shaderutil.h"
17
18
19static int Win;
20static int WinWidth = 1000, WinHeight = 500;
21static GLboolean Anti = GL_FALSE, Stipple = GL_FALSE;
22static GLfloat LineWidth = 1.0, MaxLineWidth = 3.0;
23static GLuint StippleProgram;
24
25
26/**
27 * Generate a 2D texture stipple pattern from 1D line stipple pattern.
28 * Note: the stipple repeat factor could be implemented by scaling the
29 * texcoords in the frag shader.
30 */
31static GLuint
32LineStippleToTexture(GLushort pattern)
33{
34   GLuint tex, i, j;
35   GLubyte pattern2d[16][16];
36
37   glGenTextures(1, &tex);
38   glBindTexture(GL_TEXTURE_2D, tex);
39
40   for (i = 0; i < 16; i++) {
41      for (j = 0; j < 16; j++) {
42         GLuint ibit = (pattern >> i) & 1;
43         GLuint jbit = (pattern >> j) & 1;
44         pattern2d[i][j] = (ibit | jbit) * 255;
45      }
46   }
47
48   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
49   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
50   glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 16, 16, 0,
51                GL_LUMINANCE, GL_UNSIGNED_BYTE, pattern2d);
52
53   return tex;
54}
55
56
57/** Draw some lines */
58static void
59DrawLines(void)
60{
61   const float r0 = 0.2, r1 = 0.9;
62   float a;
63
64   glBegin(GL_LINES);
65   for (a = 0.0; a < 2.0*M_PI; a += 0.1) {
66      float x0 = r0 * cos(a), y0 = r0 * sin(a);
67      float x1 = r1 * cos(a), y1 = r1 * sin(a);
68      glVertex2f(x0, y0);
69      glVertex2f(x1, y1);
70   }
71   glEnd();
72   glBegin(GL_LINE_LOOP);
73   for (a = 0.0; a <= 2.0*M_PI; a += 0.1) {
74      float x1 = r1 * cos(a), y1 = r1 * sin(a);
75      glVertex2f(x1, y1);
76   }
77   glEnd();
78   glBegin(GL_LINE_LOOP);
79   for (a = 0.0; a <= 2.0*M_PI; a += 0.1) {
80      float x0 = r0 * cos(a), y0 = r0 * sin(a);
81      glVertex2f(x0, y0);
82   }
83   glEnd();
84   glBegin(GL_LINE_LOOP);
85   glVertex2f(-r1, -r1);
86   glVertex2f(+r1, -r1);
87   glVertex2f(+r1, +r1);
88   glVertex2f(-r1, +r1);
89   glEnd();
90}
91
92
93static void
94Draw(void)
95{
96   GLint vpWidth = WinWidth / 2;
97   GLint vpHeight = WinHeight;
98
99   glClear(GL_COLOR_BUFFER_BIT);
100
101   /* draw regular lines */
102   {
103      if (Anti) {
104         glEnable(GL_LINE_SMOOTH);
105         glEnable(GL_BLEND);
106      }
107      if (Stipple)
108         glEnable(GL_LINE_STIPPLE);
109      glLineWidth(LineWidth);
110
111      glViewport(0, 0, vpWidth, vpHeight);
112      glColor3f(1, 1, 1);
113      DrawLines();
114
115      glDisable(GL_LINE_SMOOTH);
116      glDisable(GL_LINE_STIPPLE);
117      glDisable(GL_BLEND);
118      glLineWidth(1.0);
119   }
120
121   /* draw hacked lines */
122   {
123#define ALPHA 0.5
124      static const float offsets_alpha[5][3] = {
125         { 0.0, 0.0, 1.0 },
126#if 0
127         { 1.0, 0.5, ALPHA },
128         { -1.0, -0.5, ALPHA },
129         { -0.5, 1.0, ALPHA },
130         { 0.5, -1.0, ALPHA }
131#else
132         { 1.0, 0.0, ALPHA },
133         { -1.0, 0.0, ALPHA },
134         { 0.0, 1.0, ALPHA },
135         { 0.0, -1.0, ALPHA }
136#endif
137      };
138
139      int passes, pass;
140      float s;
141
142      glViewport(vpWidth, 0, vpWidth, vpHeight);
143
144      if (Anti || LineWidth > 1.5) {
145         passes = 5;
146      }
147      else {
148         passes = 1;
149      }
150      if (Anti)
151         s = 2.0 * LineWidth / MaxLineWidth;
152      else
153         s = 1.5 * LineWidth / MaxLineWidth;
154
155      if (Stipple) {
156         glUseProgram(StippleProgram);
157      }
158      if (Anti) {
159         glEnable(GL_BLEND);
160      }
161
162      for (pass = 0; pass < passes; pass++) {
163         /* Translate all vertices by small x/y offset */
164         float tx = offsets_alpha[pass][0] / vpWidth * s;
165         float ty = offsets_alpha[pass][1] / vpHeight * s;
166         /* adjust fragment alpha (this could be done in many ways) */
167         glColor4f(1, 1, 1, offsets_alpha[pass][2]);
168
169         glPushMatrix();
170         glTranslatef(tx, ty, 0.0);
171            DrawLines();
172         glPopMatrix();
173      }
174
175      if (1) {
176         /* debug: show the 2D texture / stipple pattern */
177         GLfloat w = 32.0 / (vpWidth);
178         GLfloat h = 32.0 / (vpHeight);
179
180         glColor3f(1, 1, 1);
181         glBegin(GL_QUADS);
182         glVertex2f(0.0, 0.0);
183         glVertex2f(w, 0.0);
184         glVertex2f(w, h);
185         glVertex2f(0.0, h);
186         glEnd();
187      }
188
189      glUseProgram(0);
190      glDisable(GL_BLEND);
191
192   }
193
194   glutSwapBuffers();
195}
196
197
198static void
199Reshape(int width, int height)
200{
201   WinWidth = width;
202   WinHeight = height;
203   glViewport(0, 0, width, height);
204   glMatrixMode(GL_PROJECTION);
205   glLoadIdentity();
206   glMatrixMode(GL_MODELVIEW);
207   glLoadIdentity();
208}
209
210
211static void
212Key(unsigned char key, int x, int y)
213{
214   (void) x;
215   (void) y;
216   switch (key) {
217   case 'a':
218      Anti = !Anti;
219      break;
220   case 's':
221      Stipple = !Stipple;
222      break;
223   case 'w':
224      LineWidth -= 0.25;
225      if (LineWidth < 1.0)
226         LineWidth = 1.0;
227      break;
228   case 'W':
229      LineWidth += 0.25;
230      if (LineWidth > MaxLineWidth)
231         LineWidth = MaxLineWidth;
232      break;
233   case 27:
234      glutDestroyWindow(Win);
235      exit(0);
236      break;
237   }
238   printf("Stipple: %d  Antialias: %d  LineWidth: %f\n",
239          Stipple, Anti, LineWidth);
240
241   glutPostRedisplay();
242}
243
244
245static void
246SpecialKey(int key, int x, int y)
247{
248   glutPostRedisplay();
249}
250
251
252static GLuint
253MakeFragmentShader(void)
254{
255   static const char *fragShaderText =
256      "uniform sampler2D stippleTex; \n"
257      "uniform vec2 viewportSize; \n"
258      "float scale = 1.0 / 16.0; \n"
259      "void main() \n"
260      "{ \n"
261      "   vec2 t = gl_FragCoord.xy * scale; \n"
262      "   gl_FragColor = gl_Color * texture2D(stippleTex, t); \n"
263      "} \n";
264
265   GLuint fragShader, program;
266   GLint stippleTex, viewportSize;
267
268   fragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
269   program = LinkShaders(0, fragShader);
270   glUseProgram(program);
271
272   stippleTex = glGetUniformLocation(program, "stippleTex");
273   glUniform1i(stippleTex, 0); /* unit 0 */
274
275   viewportSize = glGetUniformLocation(program, "viewportSize");
276   glUniform2f(viewportSize, WinWidth / 2.0, WinHeight);
277
278   glUseProgram(0);
279
280   return program;
281}
282
283
284static void
285Usage(void)
286{
287   printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
288   printf("Keys:\n");
289   printf("  a - toggle antialiasing\n");
290   printf("  s - toogle stippling\n");
291   printf("  w/W - decrease/increase line width\n");
292}
293
294
295static void
296Init(void)
297{
298   GLushort pattern = 0xf2;
299   GLuint tex;
300
301   if (!ShadersSupported())
302      exit(1);
303
304   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
305   glLineStipple(1, pattern);
306
307   StippleProgram = MakeFragmentShader();
308
309   tex = LineStippleToTexture(pattern);
310   glBindTexture(GL_TEXTURE_2D, tex);
311}
312
313
314int
315main(int argc, char *argv[])
316{
317   glutInit(&argc, argv);
318   glutInitWindowSize(WinWidth, WinHeight);
319   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
320   Win = glutCreateWindow(argv[0]);
321   glutReshapeFunc(Reshape);
322   glutKeyboardFunc(Key);
323   glutSpecialFunc(SpecialKey);
324   glutDisplayFunc(Draw);
325   glewInit();
326   Usage();
327   Init();
328   glutMainLoop();
329   return 0;
330}
331