1/** 2 * Test using a geometry shader to implement polygon outlining. 3 * 4 * Based on the technique "Single-Pass Wireframe Rendering" by Andreas 5 * Bærentzen, Steen Lund Nielsen, Mikkel Gjael, Bent D. Larsen & Niels 6 * Jaergen Christensen, SIGGRAPH 2006 7 * 8 * Brian Paul 9 * May 2012 10 */ 11 12#include <assert.h> 13#include <string.h> 14#include <stdio.h> 15#include <stdlib.h> 16#include <math.h> 17#include <GL/glew.h> 18#include "glut_wrap.h" 19#include "shaderutil.h" 20#include "trackball.h" 21 22static GLint WinWidth = 500, WinHeight = 500; 23static GLint Win = 0; 24static GLuint VertShader, GeomShader, FragShader, Program; 25static GLboolean Anim = GL_TRUE; 26static int uViewportSize = -1; 27 28static const GLfloat Orange[4] = {1.0, 0.6, 0.0, 1}; 29 30static float CurQuat[4] = { 0, 0, 0, 1 }; 31static GLboolean ButtonDown = GL_FALSE; 32static GLint ButtonX, ButtonY; 33 34 35static void 36CheckError(int line) 37{ 38 GLenum err = glGetError(); 39 if (err) { 40 printf("GL Error %s (0x%x) at line %d\n", 41 gluErrorString(err), (int) err, line); 42 } 43} 44 45 46static void 47Redisplay(void) 48{ 49 GLfloat rot[4][4]; 50 51 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 52 53 glColor4fv(Orange); 54 55 glPushMatrix(); 56 build_rotmatrix(rot, CurQuat); 57 glMultMatrixf(&rot[0][0]); 58 59 if (0) 60 glutSolidDodecahedron(); 61 else 62 glutSolidSphere(2, 30, 20); 63 64 glPopMatrix(); 65 66 glutSwapBuffers(); 67} 68 69 70static void 71Idle(void) 72{ 73 static const float yAxis[3] = {0, 1, 0}; 74 static double t0 = -1.; 75 float quat[4]; 76 double dt, t = glutGet(GLUT_ELAPSED_TIME) / 2000.0; 77 if (t0 < 0.0) 78 t0 = t; 79 dt = t - t0; 80 t0 = t; 81 82 axis_to_quat(yAxis, 2.0 * dt, quat); 83 add_quats(quat, CurQuat, CurQuat); 84 85 glutPostRedisplay(); 86} 87 88 89static void 90Reshape(int width, int height) 91{ 92 float ar = (float) width / height; 93 WinWidth = width; 94 WinHeight = height; 95 glViewport(0, 0, width, height); 96 glMatrixMode(GL_PROJECTION); 97 glLoadIdentity(); 98 glFrustum(-ar, ar, -1, 1, 3, 25); 99 glMatrixMode(GL_MODELVIEW); 100 glLoadIdentity(); 101 glTranslatef(0, 0, -10); 102 103 /* pass viewport dims to the shader */ 104 { 105 GLfloat viewport[4]; 106 glGetFloatv(GL_VIEWPORT, viewport); 107 glUniform2f(uViewportSize, viewport[2], viewport[3]); 108 } 109} 110 111 112static void 113MouseMotion(int x, int y) 114{ 115 if (ButtonDown) { 116 float x0 = (2.0 * ButtonX - WinWidth) / WinWidth; 117 float y0 = (WinHeight - 2.0 * ButtonY) / WinHeight; 118 float x1 = (2.0 * x - WinWidth) / WinWidth; 119 float y1 = (WinHeight - 2.0 * y) / WinHeight; 120 float q[4]; 121 122 trackball(q, x0, y0, x1, y1); 123 ButtonX = x; 124 ButtonY = y; 125 add_quats(q, CurQuat, CurQuat); 126 127 glutPostRedisplay(); 128 } 129} 130 131 132static void 133MouseButton(int button, int state, int x, int y) 134{ 135 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { 136 ButtonDown = GL_TRUE; 137 ButtonX = x; 138 ButtonY = y; 139 } 140 else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { 141 ButtonDown = GL_FALSE; 142 } 143} 144 145 146static void 147CleanUp(void) 148{ 149 glDeleteShader(FragShader); 150 glDeleteShader(VertShader); 151 glDeleteShader(GeomShader); 152 glDeleteProgram(Program); 153 glutDestroyWindow(Win); 154} 155 156 157static void 158Key(unsigned char key, int x, int y) 159{ 160 (void) x; 161 (void) y; 162 163 switch(key) { 164 case ' ': 165 case 'a': 166 Anim = !Anim; 167 if (Anim) { 168 glutIdleFunc(Idle); 169 } 170 else 171 glutIdleFunc(NULL); 172 break; 173 case 27: 174 CleanUp(); 175 exit(0); 176 break; 177 } 178 glutPostRedisplay(); 179} 180 181 182static void 183Init(void) 184{ 185 static const char *vertShaderText = 186 "#version 120 \n" 187 "void main() \n" 188 "{ \n" 189 " gl_FrontColor = gl_Color; \n" 190 " gl_Position = ftransform(); \n" 191 "} \n"; 192 static const char *geomShaderText = 193 "#version 120 \n" 194 "#extension GL_ARB_geometry_shader4: enable \n" 195 "uniform vec2 ViewportSize; \n" 196 "varying vec2 Vert0, Vert1, Vert2; \n" 197 "\n" 198 "// Transform NDC coord to window coord \n" 199 "vec2 vpxform(vec4 p) \n" 200 "{ \n" 201 " return (p.xy / p.w + 1.0) * 0.5 * ViewportSize; \n" 202 "} \n" 203 "\n" 204 "void main() \n" 205 "{ \n" 206 " gl_FrontColor = gl_FrontColorIn[0]; \n" 207 " Vert0 = vpxform(gl_PositionIn[0]); \n" 208 " Vert1 = vpxform(gl_PositionIn[1]); \n" 209 " Vert2 = vpxform(gl_PositionIn[2]); \n" 210 " gl_Position = gl_PositionIn[0]; \n" 211 " EmitVertex(); \n" 212 " gl_Position = gl_PositionIn[1]; \n" 213 " EmitVertex(); \n" 214 " gl_Position = gl_PositionIn[2]; \n" 215 " EmitVertex(); \n" 216 "} \n"; 217 static const char *fragShaderText = 218 "#version 120 \n" 219 "#define LINE_WIDTH 2.5 \n" 220 "varying vec2 Vert0, Vert1, Vert2; \n" 221 "// Compute distance from a point to a line \n" 222 "float point_line_dist(vec2 p, vec2 v1, vec2 v2) \n" 223 "{ \n" 224 " float s = (v2.x - v1.x) * (v1.y - p.y) - (v1.x - p.x) * (v2.y - v1.y); \n" 225 " float t = length(v2 - v1); \n" 226 " return abs(s) / t; \n" 227 "} \n" 228 "\n" 229 "void main() \n" 230 "{ \n" 231 " float d0 = point_line_dist(gl_FragCoord.xy, Vert0, Vert1); \n" 232 " float d1 = point_line_dist(gl_FragCoord.xy, Vert1, Vert2); \n" 233 " float d2 = point_line_dist(gl_FragCoord.xy, Vert2, Vert0); \n" 234 " float m = min(d0, min(d1, d2)); \n" 235 " gl_FragColor = gl_Color * smoothstep(0.0, LINE_WIDTH, m); \n" 236 "} \n"; 237 238 if (!ShadersSupported()) 239 exit(1); 240 241 if (!glutExtensionSupported("GL_ARB_geometry_shader4")) { 242 fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n"); 243 exit(1); 244 } 245 246 VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText); 247 FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText); 248 GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText); 249 250 Program = LinkShaders3(VertShader, GeomShader, FragShader); 251 assert(Program); 252 CheckError(__LINE__); 253 254 /* 255 * The geometry shader will receive and emit triangles. 256 */ 257 glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB, 258 GL_TRIANGLES); 259 glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB, 260 GL_TRIANGLE_STRIP); 261 glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 3); 262 CheckError(__LINE__); 263 264 /* relink */ 265 glLinkProgramARB(Program); 266 267 assert(glIsProgram(Program)); 268 assert(glIsShader(FragShader)); 269 assert(glIsShader(VertShader)); 270 assert(glIsShader(GeomShader)); 271 272 glUseProgram(Program); 273 274 uViewportSize = glGetUniformLocation(Program, "ViewportSize"); 275 276 glClearColor(0.3f, 0.3f, 0.3f, 0.0f); 277 glEnable(GL_DEPTH_TEST); 278 279 printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER)); 280} 281 282 283int 284main(int argc, char *argv[]) 285{ 286 glutInit(&argc, argv); 287 glutInitWindowSize(WinWidth, WinHeight); 288 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); 289 Win = glutCreateWindow(argv[0]); 290 glewInit(); 291 glutReshapeFunc(Reshape); 292 glutKeyboardFunc(Key); 293 glutDisplayFunc(Redisplay); 294 glutMotionFunc(MouseMotion); 295 glutMouseFunc(MouseButton); 296 if (Anim) 297 glutIdleFunc(Idle); 298 299 Init(); 300 glutMainLoop(); 301 return 0; 302} 303