1 /**
2  * Test using a geometry shader to implement point sprites.
3  * XXX we should also demo point size attenuation.
4  *
5  * Brian Paul
6  * March 2011
7  */
8 
9 #include <assert.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <math.h>
14 #include <glad/glad.h>
15 #include "glut_wrap.h"
16 #include "shaderutil.h"
17 
18 static GLint WinWidth = 500, WinHeight = 500;
19 static GLint Win = 0;
20 static GLuint VertShader, GeomShader, FragShader, Program;
21 static GLboolean Anim = GL_TRUE;
22 static GLfloat Xrot = 0, Yrot = 0;
23 static int uPointSize = -1, uInverseViewportSize = -1;
24 
25 static const int NumPoints = 50;
26 static float Points[100][3];
27 
28 static void
CheckError(int line)29 CheckError(int line)
30 {
31    GLenum err = glGetError();
32    if (err) {
33       printf("GL Error %s (0x%x) at line %d\n",
34              gluErrorString(err), (int) err, line);
35    }
36 }
37 
38 
39 static void
Redisplay(void)40 Redisplay(void)
41 {
42    int i;
43 
44    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
45 
46    glPushMatrix();
47    glRotatef(Xrot, 1, 0, 0);
48    glRotatef(Yrot, 0, 0, 1);
49 
50    glBegin(GL_POINTS);
51    for (i = 0; i < NumPoints; i++) {
52       glVertex3fv(Points[i]);
53    }
54    glEnd();
55 
56    glPopMatrix();
57 
58    glutSwapBuffers();
59 }
60 
61 
62 static void
Idle(void)63 Idle(void)
64 {
65    int curTime = glutGet(GLUT_ELAPSED_TIME);
66    Xrot = curTime * 0.02;
67    Yrot = curTime * 0.05;
68    glutPostRedisplay();
69 }
70 
71 
72 static void
Reshape(int width,int height)73 Reshape(int width, int height)
74 {
75    float ar = (float) width / height;
76    glViewport(0, 0, width, height);
77    glMatrixMode(GL_PROJECTION);
78    glLoadIdentity();
79    glFrustum(-ar, ar, -1, 1, 3, 25);
80    glMatrixMode(GL_MODELVIEW);
81    glLoadIdentity();
82    glTranslatef(0, 0, -10);
83 
84    {
85       GLfloat viewport[4];
86       glGetFloatv(GL_VIEWPORT, viewport);
87       glUniform2f(uInverseViewportSize, 1.0F / viewport[2], 1.0F / viewport[3]);
88    }
89 }
90 
91 
92 static void
CleanUp(void)93 CleanUp(void)
94 {
95    glDeleteShader(FragShader);
96    glDeleteShader(VertShader);
97    glDeleteShader(GeomShader);
98    glDeleteProgram(Program);
99    glutDestroyWindow(Win);
100 }
101 
102 
103 static void
Key(unsigned char key,int x,int y)104 Key(unsigned char key, int x, int y)
105 {
106   (void) x;
107   (void) y;
108 
109    switch(key) {
110    case ' ':
111    case 'a':
112       Anim = !Anim;
113       if (Anim) {
114          glutIdleFunc(Idle);
115       }
116       else
117          glutIdleFunc(NULL);
118       break;
119    case 27:
120       CleanUp();
121       exit(0);
122       break;
123    }
124    glutPostRedisplay();
125 }
126 
127 
128 static GLuint
MakeTexture(void)129 MakeTexture(void)
130 {
131 #define TEX_SIZE 32
132    GLubyte image[TEX_SIZE][TEX_SIZE][3];
133    GLuint i, j;
134    GLuint tex;
135 
136    glGenTextures(1, &tex);
137    glBindTexture(GL_TEXTURE_2D, tex);
138 
139    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
140    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
141 
142    /* 'X' pattern */
143    for (i = 0; i < TEX_SIZE; i++) {
144       for (j = 0; j < TEX_SIZE; j++) {
145          int p1 = i - j, p2 = TEX_SIZE - 1 - i - j;
146          p1 = (p1 >= -2 && p1 <= 2);
147          p2 = (p2 >= -2 && p2 <= 2);
148          if (p1 || p2) {
149             image[i][j][0] = 255;
150             image[i][j][1] = 255;
151             image[i][j][2] = 255;
152          }
153          else {
154             image[i][j][0] = 50;
155             image[i][j][1] = 50;
156             image[i][j][2] = 50;
157          }
158       }
159    }
160 
161    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_SIZE, TEX_SIZE, 0,
162                 GL_RGB, GL_UNSIGNED_BYTE, image);
163 
164    return tex;
165 }
166 
167 
168 static void
MakePoints(void)169 MakePoints(void)
170 {
171    int i;
172    for (i = 0; i < NumPoints; i++) {
173       Points[i][0] = ((rand() % 2000) - 1000.0) / 500.0;
174       Points[i][1] = ((rand() % 2000) - 1000.0) / 500.0;
175       Points[i][2] = ((rand() % 2000) - 1000.0) / 500.0;
176    }
177 }
178 
179 static void
Init(void)180 Init(void)
181 {
182    static const char *fragShaderText =
183       "uniform sampler2D tex; \n"
184       "void main() \n"
185       "{ \n"
186       "   gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); \n"
187       "} \n";
188    static const char *vertShaderText =
189       "void main() \n"
190       "{ \n"
191       "   gl_FrontColor = gl_Color; \n"
192       "   gl_Position = ftransform(); \n"
193       "} \n";
194    static const char *geomShaderText =
195       "#version 120 \n"
196       "#extension GL_ARB_geometry_shader4: enable \n"
197       "uniform vec2 InverseViewportSize; \n"
198       "uniform float PointSize; \n"
199       "void main() \n"
200       "{ \n"
201       "   vec4 pos = gl_PositionIn[0]; \n"
202       "   vec2 d = vec2(PointSize * pos.w) * InverseViewportSize; \n"
203       "   gl_FrontColor = gl_FrontColorIn[0]; \n"
204       "   gl_TexCoord[0] = vec4(0, 0, 0, 1); \n"
205       "   gl_Position = pos + vec4(-d.x, -d.y, 0, 0); \n"
206       "   EmitVertex(); \n"
207       "   gl_TexCoord[0] = vec4(1, 0, 0, 1); \n"
208       "   gl_Position = pos + vec4( d.x, -d.y, 0, 0); \n"
209       "   EmitVertex(); \n"
210       "   gl_TexCoord[0] = vec4(0, 1, 0, 1); \n"
211       "   gl_Position = pos + vec4(-d.x,  d.y, 0, 0); \n"
212       "   EmitVertex(); \n"
213       "   gl_TexCoord[0] = vec4(1, 1, 0, 1); \n"
214       "   gl_Position = pos + vec4( d.x,  d.y, 0, 0); \n"
215       "   EmitVertex(); \n"
216       "} \n";
217 
218    if (!ShadersSupported())
219       exit(1);
220 
221    if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
222       fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
223       exit(1);
224    }
225 
226    VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
227    FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
228    GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
229    assert(GeomShader);
230 
231    Program = LinkShaders3(VertShader, GeomShader, FragShader);
232    assert(Program);
233    CheckError(__LINE__);
234 
235    /*
236     * The geometry shader will convert incoming points to quads (4-vertex
237     * triangle strips).
238     */
239    glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
240                           GL_POINTS);
241    glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
242                           GL_TRIANGLE_STRIP);
243    glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 4);
244    CheckError(__LINE__);
245 
246    glLinkProgramARB(Program);
247 
248    /* check link */
249    {
250       GLint stat;
251       GetProgramiv(Program, GL_LINK_STATUS, &stat);
252       if (!stat) {
253          GLchar log[1000];
254          GLsizei len;
255          GetProgramInfoLog(Program, 1000, &len, log);
256          fprintf(stderr, "Shader link error:\n%s\n", log);
257       }
258    }
259 
260    CheckError(__LINE__);
261 
262    glUseProgram(Program);
263    CheckError(__LINE__);
264 
265    uInverseViewportSize = glGetUniformLocation(Program, "InverseViewportSize");
266    uPointSize = glGetUniformLocation(Program, "PointSize");
267 
268    glUniform1f(uPointSize, 24.0);
269 
270    glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
271 
272    printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
273 
274    assert(glIsProgram(Program));
275    assert(glIsShader(FragShader));
276    assert(glIsShader(VertShader));
277    assert(glIsShader(GeomShader));
278 
279    glEnable(GL_DEPTH_TEST);
280 
281    MakeTexture();
282    MakePoints();
283 }
284 
285 
286 int
main(int argc,char * argv[])287 main(int argc, char *argv[])
288 {
289    glutInit(&argc, argv);
290    glutInitWindowSize(WinWidth, WinHeight);
291    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
292    Win = glutCreateWindow(argv[0]);
293    gladLoadGL();
294    glutReshapeFunc(Reshape);
295    glutKeyboardFunc(Key);
296    glutDisplayFunc(Redisplay);
297    if (Anim)
298       glutIdleFunc(Idle);
299 
300    Init();
301    glutMainLoop();
302    return 0;
303 }
304