2 * Convolution with GLSL.
3 * Note: uses GL_ARB_shader_objects, GL_ARB_vertex_shader, GL_ARB_fragment_shader,
4 * not the OpenGL 2.0 shader API.
8 #define GL_GLEXT_PROTOTYPES
29 float minx
, miny
, minz
;
30 float maxx
, maxy
, maxz
;
41 static const char *textureLocation
= "../images/girl2.rgb";
43 static GLfloat viewRotx
= 0.0, viewRoty
= 0.0, viewRotz
= 0.0;
44 static struct BoundingBox box
;
45 static struct Texture texture
;
46 static GLuint program
;
48 static enum Filter filter
= GAUSSIAN_BLUR
;
51 static void checkError(int line
)
53 GLenum err
= glGetError();
55 printf("GL Error %s (0x%x) at line %d\n",
56 gluErrorString(err
), (int) err
, line
);
60 static void loadAndCompileShader(GLuint shader
, const char *text
)
64 glShaderSource(shader
, 1, (const GLchar
**) &text
, NULL
);
66 glCompileShader(shader
);
68 glGetShaderiv(shader
, GL_COMPILE_STATUS
, &stat
);
72 glGetShaderInfoLog(shader
, 1000, &len
, log
);
73 fprintf(stderr
, "Problem compiling shader: %s\n", log
);
77 printf("Shader compiled OK\n");
81 static void readShader(GLuint shader
, const char *filename
)
83 const int max
= 100*1000;
85 char *buffer
= (char*) malloc(max
);
86 FILE *f
= fopen(filename
, "r");
88 fprintf(stderr
, "Unable to open shader file %s\n", filename
);
92 n
= fread(buffer
, 1, max
, f
);
93 printf("Read %d bytes from shader file %s\n", n
, filename
);
96 loadAndCompileShader(shader
, buffer
);
105 checkLink(GLuint prog
)
108 glGetProgramiv(prog
, GL_LINK_STATUS
, &stat
);
112 glGetProgramInfoLog(prog
, 1000, &len
, log
);
113 fprintf(stderr
, "Linker error:\n%s\n", log
);
116 fprintf(stderr
, "Link success!\n");
120 static void fillConvolution(GLint
*k
,
126 k
[0] = 1; k
[1] = 2; k
[2] = 1;
127 k
[3] = 2; k
[4] = 4; k
[5] = 2;
128 k
[6] = 1; k
[7] = 2; k
[8] = 1;
133 k
[0] = 0; k
[1] = -2; k
[2] = 0;
134 k
[3] = -2; k
[4] = 11; k
[5] = -2;
135 k
[6] = 0; k
[7] = -2; k
[8] = 0;
140 k
[0] = -1; k
[1] = -1; k
[2] = -1;
141 k
[3] = -1; k
[4] = 9; k
[5] = -1;
142 k
[6] = -1; k
[7] = -1; k
[8] = -1;
147 k
[0] = -1; k
[1] = 0; k
[2] = -1;
148 k
[3] = 0; k
[4] = 4; k
[5] = 0;
149 k
[6] = -1; k
[7] = 0; k
[8] = -1;
158 k
[0] = 1; k
[1] = 1; k
[2] = 1;
159 k
[3] = 0; k
[4] = 0; k
[5] = 0;
160 k
[6] = -1; k
[7] = -1; k
[8] = -1;
169 k
[0] = 0; k
[1] = 0; k
[2] = 0;
170 k
[3] = 0; k
[4] = 1; k
[5] = 0;
171 k
[6] = 0; k
[7] = 0; k
[8] = 0;
176 assert(!"Unhandled switch value");
180 static void setupConvolution()
182 GLint
*kernel
= (GLint
*)malloc(sizeof(GLint
) * 9);
184 GLfloat
*vecKer
= (GLfloat
*)malloc(sizeof(GLfloat
) * 9 * 4);
187 GLfloat baseColor
[4];
193 fillConvolution(kernel
, &scale
, baseColor
);
195 for (i
= 0; i
< 9; ++i
) {
196 vecKer
[i
*4 + 0] = kernel
[i
];
197 vecKer
[i
*4 + 1] = kernel
[i
];
198 vecKer
[i
*4 + 2] = kernel
[i
];
199 vecKer
[i
*4 + 3] = kernel
[i
];
202 loc
= glGetUniformLocationARB(program
, "KernelValue");
203 glUniform4fv(loc
, 9, vecKer
);
204 loc
= glGetUniformLocationARB(program
, "ScaleFactor");
205 glUniform4f(loc
, scale
, scale
, scale
, scale
);
206 loc
= glGetUniformLocationARB(program
, "BaseColor");
207 glUniform4f(loc
, baseColor
[0], baseColor
[1],
208 baseColor
[2], baseColor
[3]);
214 static void createProgram(const char *vertProgFile
,
215 const char *fragProgFile
)
217 GLuint fragShader
= 0, vertShader
= 0;
219 program
= glCreateProgram();
221 vertShader
= glCreateShader(GL_VERTEX_SHADER
);
222 readShader(vertShader
, vertProgFile
);
223 glAttachShader(program
, vertShader
);
227 fragShader
= glCreateShader(GL_FRAGMENT_SHADER
);
228 readShader(fragShader
, fragProgFile
);
229 glAttachShader(program
, fragShader
);
232 glLinkProgram(program
);
235 glUseProgram(program
);
238 assert(glIsProgram(program));
239 assert(glIsShader(fragShader));
240 assert(glIsShader(vertShader));
243 checkError(__LINE__
);
245 GLuint texLoc
= glGetUniformLocationARB(program
, "srcTex");
246 glUniform1iARB(texLoc
, 0);
249 float offsets
[] = { 1.0 / texture
.width
, 1.0 / texture
.height
,
250 0.0 , 1.0 / texture
.height
,
251 -1.0 / texture
.width
, 1.0 / texture
.height
,
252 1.0 / texture
.width
, 0.0,
254 -1.0 / texture
.width
, 0.0,
255 1.0 / texture
.width
, -1.0 / texture
.height
,
256 0.0 , -1.0 / texture
.height
,
257 -1.0 / texture
.width
, -1.0 / texture
.height
};
258 GLuint offsetLoc
= glGetUniformLocationARB(program
, "Offset");
259 glUniform2fv(offsetLoc
, 9, offsets
);
263 checkError(__LINE__
);
267 static void readTexture(const char *filename
)
274 glGenTextures(1, &texture
.id
);
275 glBindTexture(GL_TEXTURE_2D
, texture
.id
);
276 glTexParameteri(GL_TEXTURE_2D
,
277 GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
278 glTexParameteri(GL_TEXTURE_2D
,
279 GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
280 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
281 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
282 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
283 data
= LoadRGBImage(filename
, &texture
.width
, &texture
.height
,
286 printf("Error: couldn't load texture image '%s'\n", filename
);
289 printf("Texture %s (%d x %d)\n",
290 filename
, texture
.width
, texture
.height
);
291 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGB
,
292 texture
.width
, texture
.height
, 0, texture
.format
,
293 GL_UNSIGNED_BYTE
, data
);
296 static void menuSelected(int entry
)
303 filter
= (enum Filter
)entry
;
310 static void menuInit()
312 menuId
= glutCreateMenu(menuSelected
);
314 glutAddMenuEntry("Gaussian blur", GAUSSIAN_BLUR
);
315 glutAddMenuEntry("Sharpen", SHARPEN
);
316 glutAddMenuEntry("Mean removal", MEAN_REMOVAL
);
317 glutAddMenuEntry("Emboss", EMBOSS
);
318 glutAddMenuEntry("Edge detect", EDGE_DETECT
);
319 glutAddMenuEntry("None", NO_FILTER
);
321 glutAddMenuEntry("Quit", QUIT
);
323 glutAttachMenu(GLUT_RIGHT_BUTTON
);
328 if (!glutExtensionSupported("GL_ARB_shader_objects") ||
329 !glutExtensionSupported("GL_ARB_vertex_shader") ||
330 !glutExtensionSupported("GL_ARB_fragment_shader")) {
331 fprintf(stderr
, "Sorry, this program requires GL_ARB_shader_objects, GL_ARB_vertex_shader, and GL_ARB_fragment_shader\n");
335 fprintf(stderr
, "GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER
));
336 fprintf(stderr
, "GL_VERSION = %s\n", (char *) glGetString(GL_VERSION
));
337 fprintf(stderr
, "GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR
));
340 readTexture(textureLocation
);
341 createProgram("convolution.vert", "convolution.frag");
343 glEnable(GL_TEXTURE_2D
);
344 glClearColor(1.0, 1.0, 1.0, 1.0);
345 /*glShadeModel(GL_SMOOTH);*/
346 glShadeModel(GL_FLAT
);
349 static void reshape(int width
, int height
)
351 glViewport(0, 0, width
, height
);
352 glMatrixMode(GL_PROJECTION
);
360 glOrtho(box
.minx
, box
.maxx
, box
.miny
, box
.maxy
, -999999, 999999);
361 glMatrixMode(GL_MODELVIEW
);
364 static void keyPress(unsigned char key
, int x
, int y
)
376 special(int k
, int x
, int y
)
403 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
408 center
[0] = box
.maxx
/2;
409 center
[1] = box
.maxy
/2;
410 anchor
[0] = center
[0] - texture
.width
/2;
411 anchor
[1] = center
[1] - texture
.height
/2;
413 glTranslatef(center
[0], center
[1], 0);
414 glRotatef(viewRotx
, 1.0, 0.0, 0.0);
415 glRotatef(viewRoty
, 0.0, 1.0, 0.0);
416 glRotatef(viewRotz
, 0.0, 0.0, 1.0);
417 glTranslatef(-center
[0], -center
[1], 0);
419 glTranslatef(anchor
[0], anchor
[1], 0);
420 glBegin(GL_TRIANGLE_STRIP
);
422 glColor3f(1., 0., 0.);
426 glColor3f(0., 1., 0.);
427 glTexCoord2f(0, 1.0);
428 glVertex3f(0, texture
.height
, 0);
430 glColor3f(1., 0., 0.);
431 glTexCoord2f(1.0, 0);
432 glVertex3f(texture
.width
, 0, 0);
434 glColor3f(0., 1., 0.);
436 glVertex3f(texture
.width
, texture
.height
, 0);
445 int main(int argc
, char **argv
)
447 glutInit(&argc
, argv
);
449 glutInitWindowPosition(0, 0);
450 glutInitWindowSize(400, 400);
451 glutInitDisplayMode(GLUT_RGB
| GLUT_ALPHA
| GLUT_DOUBLE
);
453 if (!glutCreateWindow("Image Convolutions")) {
454 fprintf(stderr
, "Couldn't create window!\n");
460 glutReshapeFunc(reshape
);
461 glutKeyboardFunc(keyPress
);
462 glutSpecialFunc(special
);
463 glutDisplayFunc(draw
);