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.
10 #define GL_GLEXT_PROTOTYPES
31 float minx
, miny
, minz
;
32 float maxx
, maxy
, maxz
;
43 static const char *textureLocation
= "../images/girl2.rgb";
45 static GLfloat viewRotx
= 0.0, viewRoty
= 0.0, viewRotz
= 0.0;
46 static struct BoundingBox box
;
47 static struct Texture texture
;
48 static GLuint program
;
50 static enum Filter filter
= GAUSSIAN_BLUR
;
53 static void checkError(int line
)
55 GLenum err
= glGetError();
57 printf("GL Error %s (0x%x) at line %d\n",
58 gluErrorString(err
), (int) err
, line
);
62 static void loadAndCompileShader(GLuint shader
, const char *text
)
66 glShaderSource(shader
, 1, (const GLchar
**) &text
, NULL
);
68 glCompileShader(shader
);
70 glGetShaderiv(shader
, GL_COMPILE_STATUS
, &stat
);
74 glGetShaderInfoLog(shader
, 1000, &len
, log
);
75 fprintf(stderr
, "Problem compiling shader: %s\n", log
);
79 printf("Shader compiled OK\n");
83 static void readShader(GLuint shader
, const char *filename
)
85 const int max
= 100*1000;
87 char *buffer
= (char*) malloc(max
);
88 FILE *f
= fopen(filename
, "r");
90 fprintf(stderr
, "Unable to open shader file %s\n", filename
);
94 n
= fread(buffer
, 1, max
, f
);
95 printf("Read %d bytes from shader file %s\n", n
, filename
);
98 loadAndCompileShader(shader
, buffer
);
107 checkLink(GLuint prog
)
110 glGetProgramiv(prog
, GL_LINK_STATUS
, &stat
);
114 glGetProgramInfoLog(prog
, 1000, &len
, log
);
115 fprintf(stderr
, "Linker error:\n%s\n", log
);
118 fprintf(stderr
, "Link success!\n");
122 static void fillConvolution(GLint
*k
,
128 k
[0] = 1; k
[1] = 2; k
[2] = 1;
129 k
[3] = 2; k
[4] = 4; k
[5] = 2;
130 k
[6] = 1; k
[7] = 2; k
[8] = 1;
135 k
[0] = 0; k
[1] = -2; k
[2] = 0;
136 k
[3] = -2; k
[4] = 11; k
[5] = -2;
137 k
[6] = 0; k
[7] = -2; k
[8] = 0;
142 k
[0] = -1; k
[1] = -1; k
[2] = -1;
143 k
[3] = -1; k
[4] = 9; k
[5] = -1;
144 k
[6] = -1; k
[7] = -1; k
[8] = -1;
149 k
[0] = -1; k
[1] = 0; k
[2] = -1;
150 k
[3] = 0; k
[4] = 4; k
[5] = 0;
151 k
[6] = -1; k
[7] = 0; k
[8] = -1;
160 k
[0] = 1; k
[1] = 1; k
[2] = 1;
161 k
[3] = 0; k
[4] = 0; k
[5] = 0;
162 k
[6] = -1; k
[7] = -1; k
[8] = -1;
171 k
[0] = 0; k
[1] = 0; k
[2] = 0;
172 k
[3] = 0; k
[4] = 1; k
[5] = 0;
173 k
[6] = 0; k
[7] = 0; k
[8] = 0;
178 assert(!"Unhandled switch value");
182 static void setupConvolution()
184 GLint
*kernel
= (GLint
*)malloc(sizeof(GLint
) * 9);
186 GLfloat
*vecKer
= (GLfloat
*)malloc(sizeof(GLfloat
) * 9 * 4);
189 GLfloat baseColor
[4];
195 fillConvolution(kernel
, &scale
, baseColor
);
197 for (i
= 0; i
< 9; ++i
) {
198 vecKer
[i
*4 + 0] = kernel
[i
];
199 vecKer
[i
*4 + 1] = kernel
[i
];
200 vecKer
[i
*4 + 2] = kernel
[i
];
201 vecKer
[i
*4 + 3] = kernel
[i
];
204 loc
= glGetUniformLocationARB(program
, "KernelValue");
205 glUniform4fv(loc
, 9, vecKer
);
206 loc
= glGetUniformLocationARB(program
, "ScaleFactor");
207 glUniform4f(loc
, scale
, scale
, scale
, scale
);
208 loc
= glGetUniformLocationARB(program
, "BaseColor");
209 glUniform4f(loc
, baseColor
[0], baseColor
[1],
210 baseColor
[2], baseColor
[3]);
216 static void createProgram(const char *vertProgFile
,
217 const char *fragProgFile
)
219 GLuint fragShader
= 0, vertShader
= 0;
221 program
= glCreateProgram();
223 vertShader
= glCreateShader(GL_VERTEX_SHADER
);
224 readShader(vertShader
, vertProgFile
);
225 glAttachShader(program
, vertShader
);
229 fragShader
= glCreateShader(GL_FRAGMENT_SHADER
);
230 readShader(fragShader
, fragProgFile
);
231 glAttachShader(program
, fragShader
);
234 glLinkProgram(program
);
237 glUseProgram(program
);
240 assert(glIsProgram(program));
241 assert(glIsShader(fragShader));
242 assert(glIsShader(vertShader));
245 checkError(__LINE__
);
247 GLuint texLoc
= glGetUniformLocationARB(program
, "srcTex");
248 glUniform1iARB(texLoc
, 0);
251 float offsets
[] = { 1.0 / texture
.width
, 1.0 / texture
.height
,
252 0.0 , 1.0 / texture
.height
,
253 -1.0 / texture
.width
, 1.0 / texture
.height
,
254 1.0 / texture
.width
, 0.0,
256 -1.0 / texture
.width
, 0.0,
257 1.0 / texture
.width
, -1.0 / texture
.height
,
258 0.0 , -1.0 / texture
.height
,
259 -1.0 / texture
.width
, -1.0 / texture
.height
};
260 GLuint offsetLoc
= glGetUniformLocationARB(program
, "Offset");
261 glUniform2fv(offsetLoc
, 9, offsets
);
265 checkError(__LINE__
);
269 static void readTexture(const char *filename
)
276 glGenTextures(1, &texture
.id
);
277 glBindTexture(GL_TEXTURE_2D
, texture
.id
);
278 glTexParameteri(GL_TEXTURE_2D
,
279 GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
280 glTexParameteri(GL_TEXTURE_2D
,
281 GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
282 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
283 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
284 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
285 data
= LoadRGBImage(filename
, &texture
.width
, &texture
.height
,
288 printf("Error: couldn't load texture image '%s'\n", filename
);
291 printf("Texture %s (%d x %d)\n",
292 filename
, texture
.width
, texture
.height
);
293 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGB
,
294 texture
.width
, texture
.height
, 0, texture
.format
,
295 GL_UNSIGNED_BYTE
, data
);
298 static void menuSelected(int entry
)
305 filter
= (enum Filter
)entry
;
312 static void menuInit()
314 menuId
= glutCreateMenu(menuSelected
);
316 glutAddMenuEntry("Gaussian blur", GAUSSIAN_BLUR
);
317 glutAddMenuEntry("Sharpen", SHARPEN
);
318 glutAddMenuEntry("Mean removal", MEAN_REMOVAL
);
319 glutAddMenuEntry("Emboss", EMBOSS
);
320 glutAddMenuEntry("Edge detect", EDGE_DETECT
);
321 glutAddMenuEntry("None", NO_FILTER
);
323 glutAddMenuEntry("Quit", QUIT
);
325 glutAttachMenu(GLUT_RIGHT_BUTTON
);
330 if (!glutExtensionSupported("GL_ARB_shader_objects") ||
331 !glutExtensionSupported("GL_ARB_vertex_shader") ||
332 !glutExtensionSupported("GL_ARB_fragment_shader")) {
333 fprintf(stderr
, "Sorry, this program requires GL_ARB_shader_objects, GL_ARB_vertex_shader, and GL_ARB_fragment_shader\n");
337 fprintf(stderr
, "GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER
));
338 fprintf(stderr
, "GL_VERSION = %s\n", (char *) glGetString(GL_VERSION
));
339 fprintf(stderr
, "GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR
));
342 readTexture(textureLocation
);
343 createProgram("convolution.vert", "convolution.frag");
345 glEnable(GL_TEXTURE_2D
);
346 glClearColor(1.0, 1.0, 1.0, 1.0);
347 /*glShadeModel(GL_SMOOTH);*/
348 glShadeModel(GL_FLAT
);
351 static void reshape(int width
, int height
)
353 glViewport(0, 0, width
, height
);
354 glMatrixMode(GL_PROJECTION
);
362 glOrtho(box
.minx
, box
.maxx
, box
.miny
, box
.maxy
, -999999, 999999);
363 glMatrixMode(GL_MODELVIEW
);
366 static void keyPress(unsigned char key
, int x
, int y
)
378 special(int k
, int x
, int y
)
405 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
410 center
[0] = box
.maxx
/2;
411 center
[1] = box
.maxy
/2;
412 anchor
[0] = center
[0] - texture
.width
/2;
413 anchor
[1] = center
[1] - texture
.height
/2;
415 glTranslatef(center
[0], center
[1], 0);
416 glRotatef(viewRotx
, 1.0, 0.0, 0.0);
417 glRotatef(viewRoty
, 0.0, 1.0, 0.0);
418 glRotatef(viewRotz
, 0.0, 0.0, 1.0);
419 glTranslatef(-center
[0], -center
[1], 0);
421 glTranslatef(anchor
[0], anchor
[1], 0);
422 glBegin(GL_TRIANGLE_STRIP
);
424 glColor3f(1., 0., 0.);
428 glColor3f(0., 1., 0.);
429 glTexCoord2f(0, 1.0);
430 glVertex3f(0, texture
.height
, 0);
432 glColor3f(1., 0., 0.);
433 glTexCoord2f(1.0, 0);
434 glVertex3f(texture
.width
, 0, 0);
436 glColor3f(0., 1., 0.);
438 glVertex3f(texture
.width
, texture
.height
, 0);
447 int main(int argc
, char **argv
)
449 glutInit(&argc
, argv
);
451 glutInitWindowSize(400, 400);
452 glutInitDisplayMode(GLUT_RGB
| GLUT_ALPHA
| GLUT_DOUBLE
);
454 if (!glutCreateWindow("Image Convolutions")) {
455 fprintf(stderr
, "Couldn't create window!\n");
462 glutReshapeFunc(reshape
);
463 glutKeyboardFunc(keyPress
);
464 glutSpecialFunc(special
);
465 glutDisplayFunc(draw
);