1/** 2 * Test using a geometry shader to implement wide lines. 3 * 4 * Brian Paul 5 * March 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 18static GLint WinWidth = 500, WinHeight = 500; 19static GLint Win = 0; 20static GLuint VertShader, GeomShader, FragShader, Program; 21static GLboolean Anim = GL_TRUE; 22static GLboolean UseGeomShader = GL_TRUE; 23static GLfloat LineWidth = 10.0; 24static GLfloat MaxLineWidth; 25static GLfloat Xrot = 0, Yrot = 0; 26static int uLineWidth = -1, uInverseViewportSize = -1; 27 28static int NumPoints = 50; 29 30static const GLfloat Red[4] = {1, 0, 0, 1}; 31static const GLfloat Green[4] = {0, 1, 0, 0}; 32 33 34static void 35CheckError(int line) 36{ 37 GLenum err = glGetError(); 38 if (err) { 39 printf("GL Error %s (0x%x) at line %d\n", 40 gluErrorString(err), (int) err, line); 41 } 42} 43 44 45static void 46Redisplay(void) 47{ 48 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 49 50 glPushMatrix(); 51 glRotatef(Xrot, 1, 0, 0); 52 glRotatef(Yrot, 0, 0, 1); 53 54 if (UseGeomShader) { 55 glUseProgram(Program); 56 glUniform1f(uLineWidth, LineWidth); 57 } 58 else { 59 glUseProgram(0); 60 glLineWidth(LineWidth); 61 } 62 63 glDrawArrays(GL_LINES, 0, NumPoints / 2); 64 65 glPopMatrix(); 66 67 glutSwapBuffers(); 68} 69 70 71static void 72Idle(void) 73{ 74 int curTime = glutGet(GLUT_ELAPSED_TIME); 75 Xrot = curTime * 0.02; 76 Yrot = curTime * 0.05; 77 glutPostRedisplay(); 78} 79 80 81static void 82Reshape(int width, int height) 83{ 84 float ar = (float) width / height; 85 glViewport(0, 0, width, height); 86 glMatrixMode(GL_PROJECTION); 87 glLoadIdentity(); 88 glFrustum(-ar, ar, -1, 1, 3, 25); 89 glMatrixMode(GL_MODELVIEW); 90 glLoadIdentity(); 91 glTranslatef(0, 0, -10); 92 93 { 94 GLfloat viewport[4]; 95 glGetFloatv(GL_VIEWPORT, viewport); 96 glUniform2f(uInverseViewportSize, 1.0F / viewport[2], 1.0F / viewport[3]); 97 } 98} 99 100 101static void 102CleanUp(void) 103{ 104 glDeleteShader(FragShader); 105 glDeleteShader(VertShader); 106 glDeleteShader(GeomShader); 107 glDeleteProgram(Program); 108 glutDestroyWindow(Win); 109} 110 111 112static void 113Key(unsigned char key, int x, int y) 114{ 115 (void) x; 116 (void) y; 117 118 switch(key) { 119 case ' ': 120 case 'a': 121 Anim = !Anim; 122 if (Anim) { 123 glutIdleFunc(Idle); 124 } 125 else 126 glutIdleFunc(NULL); 127 break; 128 case 'g': 129 UseGeomShader = !UseGeomShader; 130 printf("Use geometry shader? %d\n", UseGeomShader); 131 break; 132 case 'w': 133 LineWidth -= 0.5; 134 if (LineWidth < 1.0) 135 LineWidth = 1.0; 136 printf("Line width: %f\n", LineWidth); 137 break; 138 case 'W': 139 LineWidth += 0.5; 140 if (LineWidth > MaxLineWidth) 141 LineWidth = MaxLineWidth; 142 printf("Line width: %f\n", LineWidth); 143 break; 144 145 case 27: 146 CleanUp(); 147 exit(0); 148 break; 149 } 150 glutPostRedisplay(); 151} 152 153 154static void 155MakePointsVBO(void) 156{ 157 struct vert { 158 GLfloat pos[3]; 159 GLfloat color[3]; 160 }; 161 struct vert *v; 162 GLuint vbo; 163 int i; 164 165 glGenBuffers(1, &vbo); 166 glBindBuffer(GL_ARRAY_BUFFER, vbo); 167 glBufferData(GL_ARRAY_BUFFER, NumPoints * sizeof(struct vert), 168 NULL, GL_STATIC_DRAW); 169 170 v = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); 171 for (i = 0; i < NumPoints; i++) { 172 v[i].color[0] = (rand() % 1000) / 1000.0; 173 v[i].color[1] = (rand() % 1000) / 1000.0; 174 v[i].color[2] = (rand() % 1000) / 1000.0; 175 v[i].pos[0] = ((rand() % 2000) - 1000.0) / 500.0; 176 v[i].pos[1] = ((rand() % 2000) - 1000.0) / 500.0; 177 v[i].pos[2] = ((rand() % 2000) - 1000.0) / 500.0; 178 } 179 glUnmapBuffer(GL_ARRAY_BUFFER); 180 181 glVertexPointer(3, GL_FLOAT, sizeof(struct vert), (void *) 0); 182 glEnable(GL_VERTEX_ARRAY); 183 glColorPointer(3, GL_FLOAT, sizeof(struct vert), (void *) sizeof(float[3])); 184 glEnable(GL_COLOR_ARRAY); 185} 186 187 188static void 189Init(void) 190{ 191 static const char *fragShaderText = 192 "void main() \n" 193 "{ \n" 194 " gl_FragColor = gl_Color; \n" 195 "} \n"; 196 static const char *vertShaderText = 197 "void main() \n" 198 "{ \n" 199 " gl_FrontColor = gl_Color; \n" 200 " gl_Position = ftransform(); \n" 201 "} \n"; 202 static const char *geomShaderText = 203 "#version 120 \n" 204 "#extension GL_ARB_geometry_shader4: enable \n" 205 "uniform vec2 InverseViewportSize; \n" 206 "uniform float LineWidth; \n" 207 "void main() \n" 208 "{ \n" 209 " vec4 pos0 = gl_PositionIn[0]; \n" 210 " vec4 pos1 = gl_PositionIn[1]; \n" 211 " vec4 dir = abs(pos1 - pos0); \n" 212 " vec2 d0 = vec2(LineWidth * pos0.w) * InverseViewportSize; \n" 213 " vec2 d1 = vec2(LineWidth * pos1.w) * InverseViewportSize; \n" 214 " // this conditional could be avoided \n" 215 " if (dir.x > dir.y) { \n" 216 " // X-major line \n" 217 " d0.x = 0.0; \n" 218 " d1.x = 0.0; \n" 219 " } \n" 220 " else { \n" 221 " // Y-major line \n" 222 " d0.y = 0.0; \n" 223 " d1.y = 0.0; \n" 224 " } \n" 225 " gl_FrontColor = gl_FrontColorIn[0]; \n" 226 " gl_TexCoord[0] = vec4(0, 0, 0, 1); \n" 227 " gl_Position = pos0 + vec4( d0.x, -d0.y, 0, 0); \n" 228 " EmitVertex(); \n" 229 " gl_FrontColor = gl_FrontColorIn[1]; \n" 230 " gl_TexCoord[0] = vec4(1, 0, 0, 1); \n" 231 " gl_Position = pos1 + vec4( d1.x, -d1.y, 0, 0); \n" 232 " EmitVertex(); \n" 233 " gl_FrontColor = gl_FrontColorIn[0]; \n" 234 " gl_TexCoord[0] = vec4(0, 1, 0, 1); \n" 235 " gl_Position = pos0 + vec4(-d0.x, d0.y, 0, 0); \n" 236 " EmitVertex(); \n" 237 " gl_FrontColor = gl_FrontColorIn[1]; \n" 238 " gl_TexCoord[0] = vec4(1, 1, 0, 1); \n" 239 " gl_Position = pos1 + vec4(-d1.x, d1.y, 0, 0); \n" 240 " EmitVertex(); \n" 241 "} \n"; 242 243 if (!ShadersSupported()) 244 exit(1); 245 246 if (!glutExtensionSupported("GL_ARB_geometry_shader4")) { 247 fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n"); 248 exit(1); 249 } 250 251 VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText); 252 FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText); 253 GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText); 254 assert(GeomShader); 255 256 Program = LinkShaders3(VertShader, GeomShader, FragShader); 257 assert(Program); 258 CheckError(__LINE__); 259 260 /* 261 * The geometry shader will convert incoming lines to quads (4-vertex 262 * triangle strips). 263 */ 264 glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB, 265 GL_LINES); 266 glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB, 267 GL_TRIANGLE_STRIP); 268 glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 4); 269 CheckError(__LINE__); 270 271 glLinkProgramARB(Program); 272 273 /* check link */ 274 { 275 GLint stat; 276 GetProgramiv(Program, GL_LINK_STATUS, &stat); 277 if (!stat) { 278 GLchar log[1000]; 279 GLsizei len; 280 GetProgramInfoLog(Program, 1000, &len, log); 281 fprintf(stderr, "Shader link error:\n%s\n", log); 282 } 283 } 284 285 CheckError(__LINE__); 286 287 glUseProgram(Program); 288 CheckError(__LINE__); 289 290 uInverseViewportSize = glGetUniformLocation(Program, "InverseViewportSize"); 291 uLineWidth = glGetUniformLocation(Program, "LineWidth"); 292 293 glClearColor(0.3f, 0.3f, 0.3f, 0.0f); 294 295 printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER)); 296 297 assert(glIsProgram(Program)); 298 assert(glIsShader(FragShader)); 299 assert(glIsShader(VertShader)); 300 assert(glIsShader(GeomShader)); 301 302 glEnable(GL_DEPTH_TEST); 303 304 { 305 GLfloat r[2]; 306 glGetFloatv(GL_LINE_WIDTH_RANGE, r); 307 MaxLineWidth = r[1]; 308 } 309 310 MakePointsVBO(); 311} 312 313 314int 315main(int argc, char *argv[]) 316{ 317 glutInit(&argc, argv); 318 319 if (argc > 1) { 320 int n = atoi(argv[1]); 321 if (n > 0) { 322 NumPoints = n; 323 } 324 else { 325 printf("Invalid number of points\n"); 326 return 1; 327 } 328 } 329 330 glutInitWindowSize(WinWidth, WinHeight); 331 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); 332 Win = glutCreateWindow(argv[0]); 333 glewInit(); 334 glutReshapeFunc(Reshape); 335 glutKeyboardFunc(Key); 336 glutDisplayFunc(Redisplay); 337 if (Anim) 338 glutIdleFunc(Idle); 339 340 Init(); 341 glutMainLoop(); 342 return 0; 343} 344