1/** 2 * Implement smooth (AA) points with shaders. 3 * A simple variation could be used for sprite points. 4 * Brian Paul 5 * 29 July 2007 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 GLuint FragShader; 19static GLuint VertShader; 20static GLuint Program; 21 22static GLint Win = 0; 23static GLint WinWidth = 500, WinHeight = 200; 24static GLfloat Xpos = 0.0f, Ypos = 0.0f; 25static GLint uViewportInv; 26static GLboolean Smooth = GL_TRUE, Blend = GL_TRUE; 27 28 29/** 30 * Issue vertices for a "shader point". 31 * The position is duplicated, only texcoords (or other vertex attrib) change. 32 * The vertex program will compute the "real" quad corners. 33 */ 34static void 35PointVertex3f(GLfloat x, GLfloat y, GLfloat z) 36{ 37 glTexCoord2f(-1, -1); 38 glVertex3f(x, y, z); 39 40 glTexCoord2f( 1, -1); 41 glVertex3f(x, y, z); 42 43 glTexCoord2f( 1, 1); 44 glVertex3f(x, y, z); 45 46 glTexCoord2f(-1, 1); 47 glVertex3f(x, y, z); 48} 49 50 51static void 52DrawPoints(GLboolean shaderPoints) 53{ 54 int i; 55 for (i = 0; i < 9; i++) { 56 GLfloat x = i - 4, y = 0, z = 0; 57 /* note: can't call glPointSize inside Begin/End :( */ 58 glPointSize( 2 + i * 5 ); 59 if (shaderPoints) { 60 glBegin(GL_QUADS); 61 PointVertex3f(x, y, z); 62 glEnd(); 63 } 64 else { 65 glBegin(GL_POINTS); 66 glVertex3f(x, y, z); 67 glEnd(); 68 } 69 } 70} 71 72 73/** 74 * Top row of points is rendered conventionally with GL_POINT_SMOOTH. 75 * Bottom row is rendered with special vertex/fragment shaders (see Init()). 76 */ 77static void 78Redisplay(void) 79{ 80 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 81 82 if (Smooth) 83 glEnable(GL_POINT_SMOOTH); 84 else 85 glDisable(GL_POINT_SMOOTH); 86 87 if (Blend) 88 glEnable(GL_BLEND); 89 else 90 glDisable(GL_BLEND); 91 92 glPushMatrix(); 93 glTranslatef(Xpos, Ypos, 0); 94 95 /* 96 * regular points 97 */ 98 glPushMatrix(); 99 glTranslatef(0, 1.2, 0); 100 glUseProgram(0); 101 DrawPoints(GL_FALSE); 102 glPopMatrix(); 103 104 /* 105 * shader points 106 */ 107 glPushMatrix(); 108 glTranslatef(0, -1.2, 0); 109 glUseProgram(Program); 110 if (uViewportInv != -1) { 111 glUniform2f(uViewportInv, 1.0 / WinWidth, 1.0 / WinHeight); 112 } 113 DrawPoints(GL_TRUE); 114 glPopMatrix(); 115 116 glPopMatrix(); 117 118 glutSwapBuffers(); 119} 120 121 122static void 123Reshape(int width, int height) 124{ 125 WinWidth = width; 126 WinHeight = height; 127 glViewport(0, 0, width, height); 128 glMatrixMode(GL_PROJECTION); 129 glLoadIdentity(); 130 glFrustum(-1.0, 1.0, -1.0, 1.0, 4.0, 30.0); 131 glMatrixMode(GL_MODELVIEW); 132 glLoadIdentity(); 133 glTranslatef(0.0f, 0.0f, -20.0f); 134} 135 136 137static void 138Key(unsigned char key, int x, int y) 139{ 140 (void) x; 141 (void) y; 142 143 switch(key) { 144 case 'b': 145 Blend = !Blend; 146 break; 147 case 's': 148 Smooth = !Smooth; 149 break; 150 case 27: 151 glDeleteShader(FragShader); 152 glDeleteShader(VertShader); 153 glDeleteProgram(Program); 154 glutDestroyWindow(Win); 155 exit(0); 156 } 157 glutPostRedisplay(); 158} 159 160 161static void 162SpecialKey(int key, int x, int y) 163{ 164 const GLfloat step = 1/100.0; 165 switch(key) { 166 case GLUT_KEY_UP: 167 Ypos += step; 168 break; 169 case GLUT_KEY_DOWN: 170 Ypos -= step; 171 break; 172 case GLUT_KEY_LEFT: 173 Xpos -= step; 174 break; 175 case GLUT_KEY_RIGHT: 176 Xpos += step; 177 break; 178 } 179 glutPostRedisplay(); 180} 181 182 183static void 184Init(void) 185{ 186 /* Fragment shader: compute distance of fragment from center of point 187 * (we're using texcoords but another varying could be used). 188 * if dist > 1, discard (coverage==0) 189 * if dist < k, coverage = 1 190 * else, coverage = func(dist) 191 * Note: length() uses sqrt() and may be expensive. The distance could 192 * be squared instead (with adjustments to the threshold (k) test) 193 */ 194 static const char *fragShaderText = 195 "void main() {\n" 196 " float cover; \n" 197 " float k = 2.0 / gl_Point.size; \n" 198 " float d = length(gl_TexCoord[0].xy); \n" 199 " if (d >= 1.0) \n" 200 " discard; \n" 201 " if (d < 1.0 - k) \n" 202 " cover = 1.0; \n" 203 " else \n" 204 " cover = (1.0 - d) * 0.5 * gl_Point.size; \n" 205 " gl_FragColor.rgb = gl_Color.rgb; \n" 206 " gl_FragColor.a = cover; \n" 207 "}\n"; 208 /* Vertex shader: compute new vertex position based on incoming vertex pos, 209 * texcoords, point size, and inverse viewport scale factor. 210 * Note: should compute point size attenuation here too. 211 */ 212 static const char *vertShaderText = 213 "uniform vec2 viewportInv; \n" 214 "void main() {\n" 215 " vec4 pos = gl_ModelViewProjectionMatrix * gl_Vertex;\n" 216 " gl_Position.xy = pos.xy + gl_MultiTexCoord0.xy * viewportInv \n" 217 " * gl_Point.size * pos.w; \n" 218 " gl_Position.zw = pos.zw; \n" 219 " gl_TexCoord[0] = gl_MultiTexCoord0; \n" 220 " gl_FrontColor = gl_Color; \n" 221 "}\n"; 222 223 if (!ShadersSupported()) 224 exit(1); 225 226 VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText); 227 FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText); 228 Program = LinkShaders(VertShader, FragShader); 229 230 glUseProgram(Program); 231 232 uViewportInv = glGetUniformLocation(Program, "viewportInv"); 233 234 glUseProgram(0); 235 236 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 237} 238 239 240int 241main(int argc, char *argv[]) 242{ 243 glutInit(&argc, argv); 244 glutInitWindowSize(WinWidth, WinHeight); 245 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); 246 Win = glutCreateWindow(argv[0]); 247 glewInit(); 248 glutReshapeFunc(Reshape); 249 glutKeyboardFunc(Key); 250 glutSpecialFunc(SpecialKey); 251 glutDisplayFunc(Redisplay); 252 Init(); 253 glutMainLoop(); 254 return 0; 255} 256 257 258