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