1 /**
2  * Test using a geometry and fragment shaders to implement stippled lines.
3  *
4  * Brian Paul
5  * April 2011
6  */
7 
8 #include <assert.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <math.h>
13 #include <GL/glew.h>
14 #include "glut_wrap.h"
15 #include "shaderutil.h"
16 
17 static GLint WinWidth = 500, WinHeight = 500;
18 static GLint Win = 0;
19 static GLuint VertShader, GeomShader, FragShader, Program;
20 static GLboolean Anim = GL_TRUE;
21 static GLboolean UseGeomShader = GL_TRUE;
22 static GLfloat Xrot = 0, Yrot = 0;
23 static int uViewportSize = -1, uStippleFactor = -1, uStipplePattern = -1;
24 static int NumPoints = 50;
25 
26 static const GLushort StipplePattern = 0x10ff;
27 static GLuint StippleFactor = 2;
28 
29 
30 static void
CheckError(int line)31 CheckError(int line)
32 {
33    GLenum err = glGetError();
34    if (err) {
35       printf("GL Error %s (0x%x) at line %d\n",
36              gluErrorString(err), (int) err, line);
37    }
38 }
39 
40 
41 /**
42  * Set stipple factor and pattern for geometry shader.
43  *
44  * We convert the 16-bit stipple pattern into an array of 16 float values
45  * then pass the array as a uniform variable.
46  *
47  * Note: With GLSL 1.30 or later the stipple pattern could be implemented
48  * as an ordinary integer since GLSL 1.30 has true integer types and bit
49  * shifts and bit masks.
50  *
51  */
52 static void
SetStippleUniform(GLint factor,GLushort pattern)53 SetStippleUniform(GLint factor, GLushort pattern)
54 {
55    GLfloat p[16];
56    int i;
57    for (i = 0; i < 16; i++) {
58       p[i] = (pattern & (1 << i)) ? 1.0f : 0.0f;
59    }
60    glUniform1fv(uStipplePattern, 16, p);
61    glUniform1f(uStippleFactor, factor);
62 }
63 
64 
65 static void
Redisplay(void)66 Redisplay(void)
67 {
68    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
69 
70    glPushMatrix();
71    glRotatef(Xrot, 1, 0, 0);
72    glRotatef(Yrot, 0, 0, 1);
73 
74    if (UseGeomShader) {
75       glUseProgram(Program);
76       glDisable(GL_LINE_STIPPLE);
77    }
78    else {
79       glUseProgram(0);
80       glEnable(GL_LINE_STIPPLE);
81    }
82 
83    glDrawArrays(GL_LINES, 0, NumPoints / 2);
84 
85    glPopMatrix();
86 
87    glutSwapBuffers();
88 }
89 
90 
91 static void
Idle(void)92 Idle(void)
93 {
94    int curTime = glutGet(GLUT_ELAPSED_TIME);
95    Xrot = curTime * 0.02;
96    Yrot = curTime * 0.05;
97    glutPostRedisplay();
98 }
99 
100 
101 static void
Reshape(int width,int height)102 Reshape(int width, int height)
103 {
104    float ar = (float) width / height;
105    glViewport(0, 0, width, height);
106    glMatrixMode(GL_PROJECTION);
107    glLoadIdentity();
108 #if 1
109    glFrustum(-ar, ar, -1, 1, 3, 25);
110 #else
111    glOrtho(-3.0*ar, 3.0*ar, -3.0, 3.0, 3, 25);
112 #endif
113    glMatrixMode(GL_MODELVIEW);
114    glLoadIdentity();
115    glTranslatef(0, 0, -10);
116 
117    {
118       GLfloat viewport[4];
119       glGetFloatv(GL_VIEWPORT, viewport);
120       glUniform2f(uViewportSize, viewport[2], viewport[3]);
121    }
122 }
123 
124 
125 static void
CleanUp(void)126 CleanUp(void)
127 {
128    glDeleteShader(FragShader);
129    glDeleteShader(VertShader);
130    glDeleteShader(GeomShader);
131    glDeleteProgram(Program);
132    glutDestroyWindow(Win);
133 }
134 
135 
136 static void
Key(unsigned char key,int x,int y)137 Key(unsigned char key, int x, int y)
138 {
139   (void) x;
140   (void) y;
141 
142    switch(key) {
143    case ' ':
144    case 'a':
145       Anim = !Anim;
146       if (Anim) {
147          glutIdleFunc(Idle);
148       }
149       else
150          glutIdleFunc(NULL);
151       break;
152    case 'g':
153       UseGeomShader = !UseGeomShader;
154       printf("Use geometry shader? %d\n", UseGeomShader);
155       break;
156    case 'x':
157       Xrot ++;
158       break;
159    case 27:
160       CleanUp();
161       exit(0);
162       break;
163    }
164    glutPostRedisplay();
165 }
166 
167 
168 static void
MakePointsVBO(void)169 MakePointsVBO(void)
170 {
171    struct vert {
172       GLfloat pos[3];
173       GLfloat color[3];
174    };
175    struct vert *v;
176    GLuint vbo;
177    int i;
178 
179    glGenBuffers(1, &vbo);
180    glBindBuffer(GL_ARRAY_BUFFER, vbo);
181    glBufferData(GL_ARRAY_BUFFER, NumPoints * sizeof(struct vert),
182                 NULL, GL_STATIC_DRAW);
183 
184    v = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
185    for (i = 0; i < NumPoints; i++) {
186       v[i].color[0] = (rand() % 1000) / 1000.0;
187       v[i].color[1] = (rand() % 1000) / 1000.0;
188       v[i].color[2] = (rand() % 1000) / 1000.0;
189       v[i].pos[0] = ((rand() % 2000) - 1000.0) / 500.0;
190       v[i].pos[1] = ((rand() % 2000) - 1000.0) / 500.0;
191       v[i].pos[2] = ((rand() % 2000) - 1000.0) / 500.0;
192    }
193    glUnmapBuffer(GL_ARRAY_BUFFER);
194 
195    glVertexPointer(3, GL_FLOAT, sizeof(struct vert), (void *) 0);
196    glEnable(GL_VERTEX_ARRAY);
197    glColorPointer(3, GL_FLOAT, sizeof(struct vert), (void *) sizeof(float[3]));
198    glEnable(GL_COLOR_ARRAY);
199 }
200 
201 
202 static void
Init(void)203 Init(void)
204 {
205    static const char *fragShaderText =
206       "uniform float StipplePattern[16]; \n"
207       "varying float stippleCoord; \n"
208       "void main() \n"
209       "{ \n"
210       "   // check the stipple pattern and discard if value is zero \n"
211       "   // TODO: we should really undo the perspective interpolation here \n"
212       "   // so that it's linear. \n"
213       "   float stip = StipplePattern[int(fract(stippleCoord) * 16.0)]; \n"
214       "   if (stip == 0.0) \n"
215       "      discard; \n"
216       "   gl_FragColor = gl_Color; \n"
217       "} \n";
218    static const char *vertShaderText =
219       "void main() \n"
220       "{ \n"
221       "   gl_FrontColor = gl_Color; \n"
222       "   gl_Position = ftransform(); \n"
223       "} \n";
224    static const char *geomShaderText =
225       "#version 120 \n"
226       "#extension GL_ARB_geometry_shader4: enable \n"
227       "uniform vec2 ViewportSize; \n"
228       "uniform float StippleFactor; \n"
229       "varying float stippleCoord; \n"
230       "void main() \n"
231       "{ \n"
232       "   vec4 pos0 = gl_PositionIn[0]; \n"
233       "   vec4 pos1 = gl_PositionIn[1]; \n"
234       "   // Convert eye coords to window coords \n"
235       "   // Note: we're off by a factor of two here, make up for that below \n"
236       "   vec2 p0 = pos0.xy / pos0.w * ViewportSize; \n"
237       "   vec2 p1 = pos1.xy / pos1.w * ViewportSize; \n"
238       "   float len = length(p0.xy - p1.xy); \n"
239       "   // Emit first vertex \n"
240       "   gl_FrontColor = gl_FrontColorIn[0]; \n"
241       "   gl_Position = pos0; \n"
242       "   stippleCoord = 0.0; \n"
243       "   EmitVertex(); \n"
244       "   // Emit second vertex \n"
245       "   gl_FrontColor = gl_FrontColorIn[1]; \n"
246       "   gl_Position = pos1; \n"
247       "   stippleCoord = len / StippleFactor / 32.0; // Note: not 16, see above \n"
248       "   EmitVertex(); \n"
249       "} \n";
250 
251    if (!ShadersSupported())
252       exit(1);
253 
254    if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
255       fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
256       exit(1);
257    }
258 
259    VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
260    FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
261    GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
262    assert(GeomShader);
263 
264    Program = LinkShaders3(VertShader, GeomShader, FragShader);
265    assert(Program);
266    CheckError(__LINE__);
267 
268    /*
269     * The geometry shader accepts lines and produces lines.
270     */
271    glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
272                           GL_LINES);
273    glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
274                           GL_LINE_STRIP);
275    glProgramParameteriARB(Program, GL_GEOMETRY_VERTICES_OUT_ARB, 4);
276    CheckError(__LINE__);
277 
278    glLinkProgramARB(Program);
279 
280    /* check link */
281    {
282       GLint stat;
283       GetProgramiv(Program, GL_LINK_STATUS, &stat);
284       if (!stat) {
285          GLchar log[1000];
286          GLsizei len;
287          GetProgramInfoLog(Program, 1000, &len, log);
288          fprintf(stderr, "Shader link error:\n%s\n", log);
289       }
290    }
291 
292    glUseProgram(Program);
293 
294    uViewportSize = glGetUniformLocation(Program, "ViewportSize");
295    uStippleFactor = glGetUniformLocation(Program, "StippleFactor");
296    uStipplePattern = glGetUniformLocation(Program, "StipplePattern");
297 
298    glUniform1f(uStippleFactor, StippleFactor);
299 
300    glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
301 
302    printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
303 
304    assert(glIsProgram(Program));
305    assert(glIsShader(FragShader));
306    assert(glIsShader(VertShader));
307    assert(glIsShader(GeomShader));
308 
309 
310    glLineStipple(StippleFactor, StipplePattern);
311    SetStippleUniform(StippleFactor, StipplePattern);
312 
313    MakePointsVBO();
314 }
315 
316 
317 int
main(int argc,char * argv[])318 main(int argc, char *argv[])
319 {
320    glutInit(&argc, argv);
321 
322    if (argc > 1) {
323       int n = atoi(argv[1]);
324       if (n > 0) {
325          NumPoints = n;
326       }
327       else {
328          printf("Invalid number of points\n");
329          return 1;
330       }
331    }
332 
333    glutInitWindowSize(WinWidth, WinHeight);
334    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
335    Win = glutCreateWindow(argv[0]);
336    glewInit();
337    glutReshapeFunc(Reshape);
338    glutKeyboardFunc(Key);
339    glutDisplayFunc(Redisplay);
340    if (Anim)
341       glutIdleFunc(Idle);
342 
343    Init();
344    glutMainLoop();
345    return 0;
346 }
347