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