132001f49Smrg/** 232001f49Smrg * Test using a geometry and fragment shaders to implement stippled lines. 332001f49Smrg * 432001f49Smrg * Brian Paul 532001f49Smrg * April 2011 632001f49Smrg */ 732001f49Smrg 832001f49Smrg#include <assert.h> 932001f49Smrg#include <string.h> 1032001f49Smrg#include <stdio.h> 1132001f49Smrg#include <stdlib.h> 1232001f49Smrg#include <math.h> 1332001f49Smrg#include <GL/glew.h> 1432001f49Smrg#include "glut_wrap.h" 1532001f49Smrg#include "shaderutil.h" 1632001f49Smrg 1732001f49Smrgstatic GLint WinWidth = 500, WinHeight = 500; 1832001f49Smrgstatic GLint Win = 0; 1932001f49Smrgstatic GLuint VertShader, GeomShader, FragShader, Program; 2032001f49Smrgstatic GLboolean Anim = GL_TRUE; 2132001f49Smrgstatic GLboolean UseGeomShader = GL_TRUE; 2232001f49Smrgstatic GLfloat Xrot = 0, Yrot = 0; 2332001f49Smrgstatic int uViewportSize = -1, uStippleFactor = -1, uStipplePattern = -1; 247ec3b29aSmrgstatic int NumPoints = 50; 2532001f49Smrg 2632001f49Smrgstatic const GLushort StipplePattern = 0x10ff; 2732001f49Smrgstatic GLuint StippleFactor = 2; 2832001f49Smrg 2932001f49Smrg 3032001f49Smrgstatic void 3132001f49SmrgCheckError(int line) 3232001f49Smrg{ 3332001f49Smrg GLenum err = glGetError(); 3432001f49Smrg if (err) { 3532001f49Smrg printf("GL Error %s (0x%x) at line %d\n", 3632001f49Smrg gluErrorString(err), (int) err, line); 3732001f49Smrg } 3832001f49Smrg} 3932001f49Smrg 4032001f49Smrg 4132001f49Smrg/** 4232001f49Smrg * Set stipple factor and pattern for geometry shader. 4332001f49Smrg * 4432001f49Smrg * We convert the 16-bit stipple pattern into an array of 16 float values 4532001f49Smrg * then pass the array as a uniform variable. 4632001f49Smrg * 4732001f49Smrg * Note: With GLSL 1.30 or later the stipple pattern could be implemented 4832001f49Smrg * as an ordinary integer since GLSL 1.30 has true integer types and bit 4932001f49Smrg * shifts and bit masks. 5032001f49Smrg * 5132001f49Smrg */ 5232001f49Smrgstatic void 5332001f49SmrgSetStippleUniform(GLint factor, GLushort pattern) 5432001f49Smrg{ 5532001f49Smrg GLfloat p[16]; 5632001f49Smrg int i; 5732001f49Smrg for (i = 0; i < 16; i++) { 5832001f49Smrg p[i] = (pattern & (1 << i)) ? 1.0f : 0.0f; 5932001f49Smrg } 6032001f49Smrg glUniform1fv(uStipplePattern, 16, p); 6132001f49Smrg glUniform1f(uStippleFactor, factor); 6232001f49Smrg} 6332001f49Smrg 6432001f49Smrg 6532001f49Smrgstatic void 6632001f49SmrgRedisplay(void) 6732001f49Smrg{ 6832001f49Smrg glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 6932001f49Smrg 7032001f49Smrg glPushMatrix(); 7132001f49Smrg glRotatef(Xrot, 1, 0, 0); 7232001f49Smrg glRotatef(Yrot, 0, 0, 1); 7332001f49Smrg 7432001f49Smrg if (UseGeomShader) { 7532001f49Smrg glUseProgram(Program); 7632001f49Smrg glDisable(GL_LINE_STIPPLE); 7732001f49Smrg } 7832001f49Smrg else { 7932001f49Smrg glUseProgram(0); 8032001f49Smrg glEnable(GL_LINE_STIPPLE); 8132001f49Smrg } 8232001f49Smrg 837ec3b29aSmrg glDrawArrays(GL_LINES, 0, NumPoints / 2); 8432001f49Smrg 8532001f49Smrg glPopMatrix(); 8632001f49Smrg 8732001f49Smrg glutSwapBuffers(); 8832001f49Smrg} 8932001f49Smrg 9032001f49Smrg 9132001f49Smrgstatic void 9232001f49SmrgIdle(void) 9332001f49Smrg{ 9432001f49Smrg int curTime = glutGet(GLUT_ELAPSED_TIME); 9532001f49Smrg Xrot = curTime * 0.02; 9632001f49Smrg Yrot = curTime * 0.05; 9732001f49Smrg glutPostRedisplay(); 9832001f49Smrg} 9932001f49Smrg 10032001f49Smrg 10132001f49Smrgstatic void 10232001f49SmrgReshape(int width, int height) 10332001f49Smrg{ 10432001f49Smrg float ar = (float) width / height; 10532001f49Smrg glViewport(0, 0, width, height); 10632001f49Smrg glMatrixMode(GL_PROJECTION); 10732001f49Smrg glLoadIdentity(); 10832001f49Smrg#if 1 10932001f49Smrg glFrustum(-ar, ar, -1, 1, 3, 25); 11032001f49Smrg#else 11132001f49Smrg glOrtho(-3.0*ar, 3.0*ar, -3.0, 3.0, 3, 25); 11232001f49Smrg#endif 11332001f49Smrg glMatrixMode(GL_MODELVIEW); 11432001f49Smrg glLoadIdentity(); 11532001f49Smrg glTranslatef(0, 0, -10); 11632001f49Smrg 11732001f49Smrg { 11832001f49Smrg GLfloat viewport[4]; 11932001f49Smrg glGetFloatv(GL_VIEWPORT, viewport); 12032001f49Smrg glUniform2f(uViewportSize, viewport[2], viewport[3]); 12132001f49Smrg } 12232001f49Smrg} 12332001f49Smrg 12432001f49Smrg 12532001f49Smrgstatic void 12632001f49SmrgCleanUp(void) 12732001f49Smrg{ 12832001f49Smrg glDeleteShader(FragShader); 12932001f49Smrg glDeleteShader(VertShader); 13032001f49Smrg glDeleteShader(GeomShader); 13132001f49Smrg glDeleteProgram(Program); 13232001f49Smrg glutDestroyWindow(Win); 13332001f49Smrg} 13432001f49Smrg 13532001f49Smrg 13632001f49Smrgstatic void 13732001f49SmrgKey(unsigned char key, int x, int y) 13832001f49Smrg{ 13932001f49Smrg (void) x; 14032001f49Smrg (void) y; 14132001f49Smrg 14232001f49Smrg switch(key) { 14332001f49Smrg case ' ': 14432001f49Smrg case 'a': 14532001f49Smrg Anim = !Anim; 14632001f49Smrg if (Anim) { 14732001f49Smrg glutIdleFunc(Idle); 14832001f49Smrg } 14932001f49Smrg else 15032001f49Smrg glutIdleFunc(NULL); 15132001f49Smrg break; 15232001f49Smrg case 'g': 15332001f49Smrg UseGeomShader = !UseGeomShader; 15432001f49Smrg printf("Use geometry shader? %d\n", UseGeomShader); 15532001f49Smrg break; 15632001f49Smrg case 'x': 15732001f49Smrg Xrot ++; 15832001f49Smrg break; 15932001f49Smrg case 27: 16032001f49Smrg CleanUp(); 16132001f49Smrg exit(0); 16232001f49Smrg break; 16332001f49Smrg } 16432001f49Smrg glutPostRedisplay(); 16532001f49Smrg} 16632001f49Smrg 16732001f49Smrg 16832001f49Smrgstatic void 1697ec3b29aSmrgMakePointsVBO(void) 17032001f49Smrg{ 1717ec3b29aSmrg struct vert { 1727ec3b29aSmrg GLfloat pos[3]; 1737ec3b29aSmrg GLfloat color[3]; 1747ec3b29aSmrg }; 1757ec3b29aSmrg struct vert *v; 1767ec3b29aSmrg GLuint vbo; 17732001f49Smrg int i; 1787ec3b29aSmrg 1797ec3b29aSmrg glGenBuffers(1, &vbo); 1807ec3b29aSmrg glBindBuffer(GL_ARRAY_BUFFER, vbo); 1817ec3b29aSmrg glBufferData(GL_ARRAY_BUFFER, NumPoints * sizeof(struct vert), 1827ec3b29aSmrg NULL, GL_STATIC_DRAW); 1837ec3b29aSmrg 1847ec3b29aSmrg v = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); 18532001f49Smrg for (i = 0; i < NumPoints; i++) { 1867ec3b29aSmrg v[i].color[0] = (rand() % 1000) / 1000.0; 1877ec3b29aSmrg v[i].color[1] = (rand() % 1000) / 1000.0; 1887ec3b29aSmrg v[i].color[2] = (rand() % 1000) / 1000.0; 1897ec3b29aSmrg v[i].pos[0] = ((rand() % 2000) - 1000.0) / 500.0; 1907ec3b29aSmrg v[i].pos[1] = ((rand() % 2000) - 1000.0) / 500.0; 1917ec3b29aSmrg v[i].pos[2] = ((rand() % 2000) - 1000.0) / 500.0; 19232001f49Smrg } 1937ec3b29aSmrg glUnmapBuffer(GL_ARRAY_BUFFER); 1947ec3b29aSmrg 1957ec3b29aSmrg glVertexPointer(3, GL_FLOAT, sizeof(struct vert), (void *) 0); 1967ec3b29aSmrg glEnable(GL_VERTEX_ARRAY); 1977ec3b29aSmrg glColorPointer(3, GL_FLOAT, sizeof(struct vert), (void *) sizeof(float[3])); 1987ec3b29aSmrg glEnable(GL_COLOR_ARRAY); 19932001f49Smrg} 20032001f49Smrg 20132001f49Smrg 20232001f49Smrgstatic void 20332001f49SmrgInit(void) 20432001f49Smrg{ 20532001f49Smrg static const char *fragShaderText = 20632001f49Smrg "uniform float StipplePattern[16]; \n" 20732001f49Smrg "varying float stippleCoord; \n" 20832001f49Smrg "void main() \n" 20932001f49Smrg "{ \n" 21032001f49Smrg " // check the stipple pattern and discard if value is zero \n" 21132001f49Smrg " // TODO: we should really undo the perspective interpolation here \n" 21232001f49Smrg " // so that it's linear. \n" 21332001f49Smrg " float stip = StipplePattern[int(fract(stippleCoord) * 16.0)]; \n" 21432001f49Smrg " if (stip == 0.0) \n" 21532001f49Smrg " discard; \n" 21632001f49Smrg " gl_FragColor = gl_Color; \n" 21732001f49Smrg "} \n"; 21832001f49Smrg static const char *vertShaderText = 21932001f49Smrg "void main() \n" 22032001f49Smrg "{ \n" 22132001f49Smrg " gl_FrontColor = gl_Color; \n" 22232001f49Smrg " gl_Position = ftransform(); \n" 22332001f49Smrg "} \n"; 22432001f49Smrg static const char *geomShaderText = 22532001f49Smrg "#version 120 \n" 22632001f49Smrg "#extension GL_ARB_geometry_shader4: enable \n" 22732001f49Smrg "uniform vec2 ViewportSize; \n" 22832001f49Smrg "uniform float StippleFactor; \n" 22932001f49Smrg "varying float stippleCoord; \n" 23032001f49Smrg "void main() \n" 23132001f49Smrg "{ \n" 23232001f49Smrg " vec4 pos0 = gl_PositionIn[0]; \n" 23332001f49Smrg " vec4 pos1 = gl_PositionIn[1]; \n" 23432001f49Smrg " // Convert eye coords to window coords \n" 23532001f49Smrg " // Note: we're off by a factor of two here, make up for that below \n" 23632001f49Smrg " vec2 p0 = pos0.xy / pos0.w * ViewportSize; \n" 23732001f49Smrg " vec2 p1 = pos1.xy / pos1.w * ViewportSize; \n" 23832001f49Smrg " float len = length(p0.xy - p1.xy); \n" 23932001f49Smrg " // Emit first vertex \n" 24032001f49Smrg " gl_FrontColor = gl_FrontColorIn[0]; \n" 24132001f49Smrg " gl_Position = pos0; \n" 24232001f49Smrg " stippleCoord = 0.0; \n" 24332001f49Smrg " EmitVertex(); \n" 24432001f49Smrg " // Emit second vertex \n" 24532001f49Smrg " gl_FrontColor = gl_FrontColorIn[1]; \n" 24632001f49Smrg " gl_Position = pos1; \n" 24732001f49Smrg " stippleCoord = len / StippleFactor / 32.0; // Note: not 16, see above \n" 24832001f49Smrg " EmitVertex(); \n" 24932001f49Smrg "} \n"; 25032001f49Smrg 25132001f49Smrg if (!ShadersSupported()) 25232001f49Smrg exit(1); 25332001f49Smrg 25432001f49Smrg if (!glutExtensionSupported("GL_ARB_geometry_shader4")) { 25532001f49Smrg fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n"); 25632001f49Smrg exit(1); 25732001f49Smrg } 25832001f49Smrg 25932001f49Smrg VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText); 26032001f49Smrg FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText); 26132001f49Smrg GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText); 26232001f49Smrg assert(GeomShader); 26332001f49Smrg 26432001f49Smrg Program = LinkShaders3(VertShader, GeomShader, FragShader); 26532001f49Smrg assert(Program); 26632001f49Smrg CheckError(__LINE__); 26732001f49Smrg 26832001f49Smrg /* 26932001f49Smrg * The geometry shader accepts lines and produces lines. 27032001f49Smrg */ 27132001f49Smrg glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB, 27232001f49Smrg GL_LINES); 27332001f49Smrg glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB, 27432001f49Smrg GL_LINE_STRIP); 27532001f49Smrg glProgramParameteriARB(Program, GL_GEOMETRY_VERTICES_OUT_ARB, 4); 27632001f49Smrg CheckError(__LINE__); 27732001f49Smrg 27832001f49Smrg glLinkProgramARB(Program); 27932001f49Smrg 28032001f49Smrg /* check link */ 28132001f49Smrg { 28232001f49Smrg GLint stat; 28332001f49Smrg GetProgramiv(Program, GL_LINK_STATUS, &stat); 28432001f49Smrg if (!stat) { 28532001f49Smrg GLchar log[1000]; 28632001f49Smrg GLsizei len; 28732001f49Smrg GetProgramInfoLog(Program, 1000, &len, log); 28832001f49Smrg fprintf(stderr, "Shader link error:\n%s\n", log); 28932001f49Smrg } 29032001f49Smrg } 29132001f49Smrg 29232001f49Smrg glUseProgram(Program); 29332001f49Smrg 29432001f49Smrg uViewportSize = glGetUniformLocation(Program, "ViewportSize"); 29532001f49Smrg uStippleFactor = glGetUniformLocation(Program, "StippleFactor"); 29632001f49Smrg uStipplePattern = glGetUniformLocation(Program, "StipplePattern"); 29732001f49Smrg 29832001f49Smrg glUniform1f(uStippleFactor, StippleFactor); 29932001f49Smrg 30032001f49Smrg glClearColor(0.3f, 0.3f, 0.3f, 0.0f); 30132001f49Smrg 30232001f49Smrg printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER)); 30332001f49Smrg 30432001f49Smrg assert(glIsProgram(Program)); 30532001f49Smrg assert(glIsShader(FragShader)); 30632001f49Smrg assert(glIsShader(VertShader)); 30732001f49Smrg assert(glIsShader(GeomShader)); 30832001f49Smrg 30932001f49Smrg 31032001f49Smrg glLineStipple(StippleFactor, StipplePattern); 31132001f49Smrg SetStippleUniform(StippleFactor, StipplePattern); 31232001f49Smrg 3137ec3b29aSmrg MakePointsVBO(); 31432001f49Smrg} 31532001f49Smrg 31632001f49Smrg 31732001f49Smrgint 31832001f49Smrgmain(int argc, char *argv[]) 31932001f49Smrg{ 32032001f49Smrg glutInit(&argc, argv); 3217ec3b29aSmrg 3227ec3b29aSmrg if (argc > 1) { 3237ec3b29aSmrg int n = atoi(argv[1]); 3247ec3b29aSmrg if (n > 0) { 3257ec3b29aSmrg NumPoints = n; 3267ec3b29aSmrg } 3277ec3b29aSmrg else { 3287ec3b29aSmrg printf("Invalid number of points\n"); 3297ec3b29aSmrg return 1; 3307ec3b29aSmrg } 3317ec3b29aSmrg } 3327ec3b29aSmrg 33332001f49Smrg glutInitWindowSize(WinWidth, WinHeight); 33432001f49Smrg glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); 33532001f49Smrg Win = glutCreateWindow(argv[0]); 33632001f49Smrg glewInit(); 33732001f49Smrg glutReshapeFunc(Reshape); 33832001f49Smrg glutKeyboardFunc(Key); 33932001f49Smrg glutDisplayFunc(Redisplay); 34032001f49Smrg if (Anim) 34132001f49Smrg glutIdleFunc(Idle); 34232001f49Smrg 34332001f49Smrg Init(); 34432001f49Smrg glutMainLoop(); 34532001f49Smrg return 0; 34632001f49Smrg} 347