add a simple but nice example of convolution filters in glsl

shows basics of image processing with glsl
This commit is contained in:
Zack Rusin 2008-05-20 18:49:40 -04:00
parent 26f874e967
commit cd87aeae00
4 changed files with 475 additions and 1 deletions

View File

@ -14,7 +14,8 @@ PROGS = \
mandelbrot \
noise \
toyball \
texdemo1
texdemo1 \
convolutions
##### RULES #####
@ -62,6 +63,12 @@ texdemo1: texdemo1.o readtex.o
texdemo1.o: texdemo1.c readtex.h extfuncs.h
$(CC) -c -I$(INCDIR) $(CFLAGS) texdemo1.c
convolutions: convolutions.o readtex.o
$(CC) -I$(INCDIR) $(CFLAGS) convolutions.o readtex.o $(APP_LIB_DEPS) -o $@
convolutions.o: convolutions.c readtex.h
$(CC) -c -I$(INCDIR) $(CFLAGS) convolutions.c
clean:
-rm -f $(PROGS)

View File

@ -0,0 +1,21 @@
const int KernelSize = 9;
//texture offsets
uniform vec2 Offset[KernelSize];
//convolution kernel
uniform vec4 KernelValue[KernelSize];
uniform sampler2D srcTex;
uniform vec4 ScaleFactor;
uniform vec4 BaseColor;
void main(void)
{
int i;
vec4 sum = vec4(0.0);
for (i = 0; i < KernelSize; ++i) {
vec4 tmp = texture2D(srcTex, gl_TexCoord[0].st + Offset[i]);
sum += tmp * KernelValue[i];
}
gl_FragColor = sum * ScaleFactor + BaseColor;
}

View File

@ -0,0 +1,5 @@
void main() {
gl_FrontColor = gl_Color;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

441
progs/glsl/convolutions.c Normal file
View File

@ -0,0 +1,441 @@
#define GL_GLEXT_PROTOTYPES
#include "readtex.h"
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
enum Filter {
GAUSSIAN_BLUR,
SHARPEN,
MEAN_REMOVAL,
EMBOSS,
NO_FILTER,
LAST
};
#define QUIT LAST
struct BoundingBox {
float minx, miny, minz;
float maxx, maxy, maxz;
};
struct Texture {
GLuint id;
GLfloat x;
GLfloat y;
GLint width;
GLint height;
GLenum format;
};
static const char *textureLocation = "../images/girl2.rgb";
static GLfloat viewRotx = 0.0, viewRoty = 0.0, viewRotz = 0.0;
static struct BoundingBox box;
static struct Texture texture;
static GLuint program;
static GLint menuId;
static enum Filter filter = GAUSSIAN_BLUR;
static void checkError(int line)
{
GLenum err = glGetError();
if (err) {
printf("GL Error %s (0x%x) at line %d\n",
gluErrorString(err), (int) err, line);
}
}
static void loadAndCompileShader(GLuint shader, const char *text)
{
GLint stat;
glShaderSource(shader, 1, (const GLchar **) &text, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
if (!stat) {
GLchar log[1000];
GLsizei len;
glGetShaderInfoLog(shader, 1000, &len, log);
fprintf(stderr, "Problem compiling shader: %s\n", log);
exit(1);
}
else {
printf("Shader compiled OK\n");
}
}
static void readShader(GLuint shader, const char *filename)
{
const int max = 100*1000;
int n;
char *buffer = (char*) malloc(max);
FILE *f = fopen(filename, "r");
if (!f) {
fprintf(stderr, "Unable to open shader file %s\n", filename);
exit(1);
}
n = fread(buffer, 1, max, f);
printf("Read %d bytes from shader file %s\n", n, filename);
if (n > 0) {
buffer[n] = 0;
loadAndCompileShader(shader, buffer);
}
fclose(f);
free(buffer);
}
static void
checkLink(GLuint prog)
{
GLint stat;
glGetProgramiv(prog, GL_LINK_STATUS, &stat);
if (!stat) {
GLchar log[1000];
GLsizei len;
glGetProgramInfoLog(prog, 1000, &len, log);
fprintf(stderr, "Linker error:\n%s\n", log);
}
else {
fprintf(stderr, "Link success!\n");
}
}
static void fillConvolution(GLint *k,
GLfloat *scale,
GLfloat *color)
{
switch(filter) {
case GAUSSIAN_BLUR:
k[0] = 1; k[1] = 2; k[2] = 1;
k[3] = 2; k[4] = 4; k[5] = 2;
k[6] = 1; k[7] = 2; k[8] = 1;
*scale = 1./16.;
break;
case SHARPEN:
k[0] = 0; k[1] = -2; k[2] = 0;
k[3] = -2; k[4] = 11; k[5] = -2;
k[6] = 0; k[7] = -2; k[8] = 0;
*scale = 1./3.;
break;
case MEAN_REMOVAL:
k[0] = -1; k[1] = -1; k[2] = -1;
k[3] = -1; k[4] = 9; k[5] = -1;
k[6] = -1; k[7] = -1; k[8] = -1;
*scale = 1./1.;
break;
case EMBOSS:
k[0] = -1; k[1] = 0; k[2] = -1;
k[3] = 0; k[4] = 4; k[5] = 0;
k[6] = -1; k[7] = 0; k[8] = -1;
*scale = 1./1.;
color[0] = 0.5;
color[1] = 0.5;
color[2] = 0.5;
color[3] = 0.5;
break;
case NO_FILTER:
k[0] = 0; k[1] = 0; k[2] = 0;
k[3] = 0; k[4] = 1; k[5] = 0;
k[6] = 0; k[7] = 0; k[8] = 0;
*scale = 1.;
break;
default:
assert(!"Unhandled switch value");
}
}
static void setupConvolution()
{
GLint *kernel = (GLint*)malloc(sizeof(GLint) * 9);
GLfloat scale;
GLfloat *vecKer = (GLfloat*)malloc(sizeof(GLfloat) * 9 * 4);
GLuint loc;
GLuint i;
GLfloat baseColor[4];
baseColor[0] = 0;
baseColor[1] = 0;
baseColor[2] = 0;
baseColor[3] = 0;
fillConvolution(kernel, &scale, baseColor);
/*vector of 4*/
for (i = 0; i < 9; ++i) {
vecKer[i*4 + 0] = kernel[i];
vecKer[i*4 + 1] = kernel[i];
vecKer[i*4 + 2] = kernel[i];
vecKer[i*4 + 3] = kernel[i];
}
loc = glGetUniformLocationARB(program, "KernelValue");
glUniform4fv(loc, 9, vecKer);
loc = glGetUniformLocationARB(program, "ScaleFactor");
glUniform4f(loc, scale, scale, scale, scale);
loc = glGetUniformLocationARB(program, "BaseColor");
glUniform4f(loc, baseColor[0], baseColor[1],
baseColor[2], baseColor[3]);
free(vecKer);
free(kernel);
}
static void createProgram(const char *vertProgFile,
const char *fragProgFile)
{
GLuint fragShader = 0, vertShader = 0;
program = glCreateProgram();
if (vertProgFile) {
vertShader = glCreateShader(GL_VERTEX_SHADER);
readShader(vertShader, vertProgFile);
glAttachShader(program, vertShader);
}
if (fragProgFile) {
fragShader = glCreateShader(GL_FRAGMENT_SHADER);
readShader(fragShader, fragProgFile);
glAttachShader(program, fragShader);
}
glLinkProgram(program);
checkLink(program);
glUseProgram(program);
assert(glIsProgram(program));
assert(glIsShader(fragShader));
assert(glIsShader(vertShader));
checkError(__LINE__);
{/*texture*/
GLuint texLoc = glGetUniformLocationARB(program, "srcTex");
glUniform1iARB(texLoc, 0);
}
{/*setup offsets */
float offsets[] = { 1.0 / texture.width, 1.0 / texture.height,
0.0 , 1.0 / texture.height,
-1.0 / texture.width, 1.0 / texture.height,
1.0 / texture.width, 0.0,
0.0 , 0.0,
-1.0 / texture.width, 0.0,
1.0 / texture.width, -1.0 / texture.height,
0.0 , -1.0 / texture.height,
-1.0 / texture.width, -1.0 / texture.height };
GLuint offsetLoc = glGetUniformLocationARB(program, "Offset");
glUniform2fv(offsetLoc, 9, offsets);
}
setupConvolution();
checkError(__LINE__);
}
static void readTexture(const char *filename)
{
GLubyte *data;
texture.x = 0;
texture.y = 0;
glGenTextures(1, &texture.id);
glBindTexture(GL_TEXTURE_2D, texture.id);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
data = LoadRGBImage(filename, &texture.width, &texture.height,
&texture.format);
if (!data) {
printf("Error: couldn't load texture image '%s'\n", filename);
exit(1);
}
printf("Texture %s (%d x %d)\n",
filename, texture.width, texture.height);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
texture.width, texture.height, 0, texture.format,
GL_UNSIGNED_BYTE, data);
}
static void menuSelected(int entry)
{
switch (entry) {
case QUIT:
exit(0);
break;
default:
filter = (enum Filter)entry;
}
setupConvolution();
glutPostRedisplay();
}
static void menuInit()
{
menuId = glutCreateMenu(menuSelected);
glutAddMenuEntry("Gaussian blur", GAUSSIAN_BLUR);
glutAddMenuEntry("Sharpen", SHARPEN);
glutAddMenuEntry("Mean removal", MEAN_REMOVAL);
glutAddMenuEntry("Emboss", EMBOSS);
glutAddMenuEntry("None", NO_FILTER);
glutAddMenuEntry("Quit", QUIT);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
static void init()
{
fprintf(stderr, "GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
fprintf(stderr, "GL_VERSION = %s\n", (char *) glGetString(GL_VERSION));
fprintf(stderr, "GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR));
menuInit();
readTexture(textureLocation);
createProgram("convolution.vert", "convolution.frag");
glEnable(GL_TEXTURE_2D);
glClearColor(1.0, 1.0, 1.0, 1.0);
/*glShadeModel(GL_SMOOTH);*/
glShadeModel(GL_FLAT);
}
static void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
box.minx = 0;
box.maxx = width;
box.miny = 0;
box.maxy = height;
box.minz = 0;
box.maxz = 1;
glOrtho(box.minx, box.maxx, box.miny, box.maxy, -999999, 999999);
glMatrixMode(GL_MODELVIEW);
}
static void keyPress(unsigned char key, int x, int y)
{
switch(key) {
case 27:
exit(0);
default:
return;
}
glutPostRedisplay();
}
static void
special(int k, int x, int y)
{
switch (k) {
case GLUT_KEY_UP:
viewRotx += 2.0;
break;
case GLUT_KEY_DOWN:
viewRotx -= 2.0;
break;
case GLUT_KEY_LEFT:
viewRoty += 2.0;
break;
case GLUT_KEY_RIGHT:
viewRoty -= 2.0;
break;
default:
return;
}
glutPostRedisplay();
}
static void draw()
{
GLfloat center[2];
GLfloat anchor[2];
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
center[0] = box.maxx/2;
center[1] = box.maxy/2;
anchor[0] = center[0] - texture.width/2;
anchor[1] = center[1] - texture.height/2;
glTranslatef(center[0], center[1], 0);
glRotatef(viewRotx, 1.0, 0.0, 0.0);
glRotatef(viewRoty, 0.0, 1.0, 0.0);
glRotatef(viewRotz, 0.0, 0.0, 1.0);
glTranslatef(-center[0], -center[1], 0);
glTranslatef(anchor[0], anchor[1], 0);
glBegin(GL_TRIANGLE_STRIP);
{
glColor3f(1., 0., 0.);
glTexCoord2f(0, 0);
glVertex3f(0, 0, 0);
glColor3f(0., 1., 0.);
glTexCoord2f(0, 1.0);
glVertex3f(0, texture.height, 0);
glColor3f(1., 0., 0.);
glTexCoord2f(1.0, 0);
glVertex3f(texture.width, 0, 0);
glColor3f(0., 1., 0.);
glTexCoord2f(1, 1);
glVertex3f(texture.width, texture.height, 0);
}
glEnd();
glPopMatrix();
glFlush();
glutSwapBuffers();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitWindowPosition(0, 0);
glutInitWindowSize(400, 400);
glutInitDisplayMode(GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE);
if (!glutCreateWindow("Image Convolutions")) {
fprintf(stderr, "Couldn't create window!\n");
exit(1);
}
init();
glutReshapeFunc(reshape);
glutKeyboardFunc(keyPress);
glutSpecialFunc(special);
glutDisplayFunc(draw);
glutMainLoop();
return 0;
}