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