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 17static GLint WinWidth = 500, WinHeight = 500; 18static GLint Win = 0; 19static GLuint VertShader, GeomShader, FragShader, Program; 20static GLboolean Anim = GL_TRUE; 21static GLboolean UseGeomShader = GL_TRUE; 22static GLfloat Xrot = 0, Yrot = 0; 23static int uViewportSize = -1, uStippleFactor = -1, uStipplePattern = -1; 24static int NumPoints = 50; 25 26static const GLushort StipplePattern = 0x10ff; 27static GLuint StippleFactor = 2; 28 29 30static void 31CheckError(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 */ 52static void 53SetStippleUniform(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 65static void 66Redisplay(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 91static void 92Idle(void) 93{ 94 int curTime = glutGet(GLUT_ELAPSED_TIME); 95 Xrot = curTime * 0.02; 96 Yrot = curTime * 0.05; 97 glutPostRedisplay(); 98} 99 100 101static void 102Reshape(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 125static void 126CleanUp(void) 127{ 128 glDeleteShader(FragShader); 129 glDeleteShader(VertShader); 130 glDeleteShader(GeomShader); 131 glDeleteProgram(Program); 132 glutDestroyWindow(Win); 133} 134 135 136static void 137Key(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 168static void 169MakePointsVBO(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 202static void 203Init(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 317int 318main(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