r3dux.org

A number-pimping side project from the valleys in *NEW* upside-down flavour.

  • Home
  • ABOUT
  • OLD SITE
  • SEARCH
  • FEEDBACK

GLSL Image Processing

r3dux | June 3, 2011

I had a play around doing some image processing earlier using GLSL shaders from chapter 17 of the OpenGL SuperBible (4th Edition, I’m not seeing them in the 5th) and the results aren’t too shabby. I’m rendering the skybox plus torus’ with ambient/diffuse/specular lighting and cubemap texture lookup to a FBO, then rendering a full-screen quad textured with the result of the previous pass, and it’s this full-screen quad that I’m processing to get these effects.

YouTube Preview Image

Pretty easy & kinda fun – next up depth of field…

Image Processing Vertex Shader

#version 330
 
// Read-only uniform values shared across all vertexes
uniform mat4 mvpMatrix;
 
// Incoming per-vertex values
in vec4 vVertex;
in vec2 vTexCoord0;
 
// Outgoing interpolated values
smooth out vec2 vTex;
 
void main(void) 
{
	// Pass through the texture coordinates
	vTex = vTexCoord0;
 
	// Transform the geometry 
	gl_Position = mvpMatrix * vVertex;
}

Image Processing Fragment Shader

#version 330
 
uniform sampler2D quadTexture;
uniform int       filterNumber;
uniform vec2      tcOffset[25]; // Texture coordinate offsets
 
in vec2 vTex;
 
out vec4 vFragColour;
 
void main(void)
{
	// Standard
	if (filterNumber == 0)
	{
		vFragColour = texture2D(quadTexture, vTex);
	}
 
	// Greyscale
	if (filterNumber == 1)
	{
		// Convert to greyscale using NTSC weightings
		float grey = dot(texture2D(quadTexture, vTex).rgb, vec3(0.299, 0.587, 0.114));
 
		vFragColour = vec4(grey, grey, grey, 1.0);
	}
 
	// Sepia tone
	if (filterNumber == 2)
	{
		// Convert to greyscale using NTSC weightings
		float grey = dot(texture2D(quadTexture, vTex).rgb, vec3(0.299, 0.587, 0.114));
 
		// Play with these rgb weightings to get different tones.
		// (As long as all rgb weightings add up to 1.0 you won't lighten or darken the image)
		vFragColour = vec4(grey * vec3(1.2, 1.0, 0.8), 1.0);
	}
 
	// Negative
	if (filterNumber == 3)
	{
		vec4 texMapColour = texture2D(quadTexture, vTex);
 
		vFragColour = vec4(1.0 - texMapColour.rgb, 1.0);
	}
 
	// Blur (gaussian)
	if (filterNumber == 4)
	{
		vec4 sample[25];
 
		for (int i = 0; i < 25; i++)
		{
			// Sample a grid around and including our texel
			sample[i] = texture(quadTexture, vTex + tcOffset[i]);
		}
 
		// Gaussian weighting:
		// 1  4  7  4 1
		// 4 16 26 16 4
		// 7 26 41 26 7 / 273 (i.e. divide by total of weightings)
		// 4 16 26 16 4
		// 1  4  7  4 1
 
    		vFragColour = (
        	           (1.0  * (sample[0] + sample[4]  + sample[20] + sample[24])) +
	                   (4.0  * (sample[1] + sample[3]  + sample[5]  + sample[9] + sample[15] + sample[19] + sample[21] + sample[23])) +
	                   (7.0  * (sample[2] + sample[10] + sample[14] + sample[22])) +
	                   (16.0 * (sample[6] + sample[8]  + sample[16] + sample[18])) +
	                   (26.0 * (sample[7] + sample[11] + sample[13] + sample[17])) +
	                   (41.0 * sample[12])
	                   ) / 273.0;
 
	}
 
	// Blur (median filter)
	if (filterNumber == 5)
	{
		vFragColour = vec4(0.0);
 
		for (int i = 0; i < 25; i++)
		{
			// Sample a grid around and including our texel
			vFragColour += texture(quadTexture, vTex + tcOffset[i]);
		}
 
		vFragColour /= 25;
	}
 
	// Sharpen
	if (filterNumber == 6)
	{
		vec4 sample[25];
 
		for (int i = 0; i < 25; i++)
		{
			// Sample a grid around and including our texel
			sample[i] = texture(quadTexture, vTex + tcOffset[i]);
		}
 
		// Sharpen weighting:
		// -1 -1 -1 -1 -1
		// -1 -1 -1 -1 -1
		// -1 -1 25 -1 -1
		// -1 -1 -1 -1 -1
		// -1 -1 -1 -1 -1
 
    		vFragColour = 25.0 * sample[12];
 
		for (int i = 0; i < 25; i++)
		{
			if (i != 12)
				vFragColour -= sample[i];
		}
	}
 
	// Dilate
	if (filterNumber == 7)
	{
		vec4 sample[25];
		vec4 maxValue = vec4(0.0);
 
		for (int i = 0; i < 25; i++)
		{
			// Sample a grid around and including our texel
			sample[i] = texture(quadTexture, vTex + tcOffset[i]);
 
			// Keep the maximum value		
			maxValue = max(sample[i], maxValue);
		}
 
		vFragColour = maxValue;
	}
 
	// Erode
	if (filterNumber == 8)
	{
		vec4 sample[25];
		vec4 minValue = vec4(1.0);
 
		for (int i = 0; i < 25; i++)
		{
			// Sample a grid around and including our texel
			sample[i] = texture(quadTexture, vTex + tcOffset[i]);
 
			// Keep the minimum value		
			minValue = min(sample[i], minValue);
		}
 
		vFragColour = minValue;
	}
 
	// Laplacian Edge Detection (very, very similar to sharpen filter - check it out!)
	if (filterNumber == 9)
	{
		vec4 sample[25];
 
		for (int i = 0; i < 25; i++)
		{
			// Sample a grid around and including our texel
			sample[i] = texture(quadTexture, vTex + tcOffset[i]);
		}
 
		// Laplacian weighting:
		// -1 -1 -1 -1 -1
		// -1 -1 -1 -1 -1
		// -1 -1 24 -1 -1
		// -1 -1 -1 -1 -1
		// -1 -1 -1 -1 -1
 
    		vFragColour = 24.0 * sample[12];
 
		for (int i = 0; i < 25; i++)
		{
			if (i != 12)
				vFragColour -= sample[i];
		}
	}
}

The only other bit of code worth mentioning is the generation of the texture coordinate offsets, which is accomplished with the following C++ code (yes, it’s kinda ugly – no, I didn’t write it – yes, it works):

// Set up texture sampling offset storage
const GLint tcOffsetColumns = 5;
const GLint tcOffsetRows    = 5;
GLfloat	texCoordOffsets[tcOffsetColumns * tcOffsetRows * 2];
 
// Calculate texture coordinate offsets for kernel convolution effects
void genTexCoordOffsets(GLuint width, GLuint height, GLfloat step = 1.0f) // Note: Change this step value to increase the number of pixels we sample across...
{
	// Note: You can multiply the step to displace the samples further. Do this with diff values horiz and vert and you have directional blur of a sort...
	float xInc = step / (GLfloat)(windowWidth);
	float yInc = step / (GLfloat)(windowHeight);
 
	for (int i = 0; i < tcOffsetColumns; i++)
	{
		for (int j = 0; j < tcOffsetRows; j++)
		{
			texCoordOffsets[(((i*5)+j)*2)+0] = (-2.0f * xInc) + ((GLfloat)i * xInc);
			texCoordOffsets[(((i*5)+j)*2)+1] = (-2.0f * yInc) + ((GLfloat)j * yInc);
		}
	}
}

You can then draw your full-screen quad something like this:

// *** Draw the original geometry to a FBO here ***
 
// Now draw our full-screen quad
projectionMatrix.PushMatrix();
 
	projectionMatrix.LoadIdentity();
	projectionMatrix.LoadMatrix(orthoMatrix);
 
	modelViewMatrix.PushMatrix();
 
		modelViewMatrix.LoadIdentity();
 
		glDisable(GL_DEPTH_TEST);
 
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, fboTexture); // Select the texture in our framebuffer
 
		glUseProgram(r3dTextureProgram);
		glUniformMatrix4fv(locTexMVPMatrix, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
		glUniform1i(locTexTextureMap,       0);
		glUniform1i(locTexFilterNumber,     filterNumber);
		glUniform2fv(locTexTCOffsets,       25, texCoordOffsets); // Pass in 25 vec2s in our texture coordinate offset array
 
		screenQuad.Draw();
 
		glEnable(GL_DEPTH_TEST);
 
	modelViewMatrix.PopMatrix();
 
projectionMatrix.PopMatrix();

Job’s a good ‘un.

Comments
4 Comments »
Categories
Coding
Tags
Blur, C++, Dilate, Erode, Gaussian, GLSL, Grayscale, Greyscale, Image Processing, Laplacian, Median, Negative, OpenGL, Sepia
Comments rss Comments rss
Trackback Trackback

How To: Create a Simple Fireworks Effect in OpenGL and SDL

r3dux | October 31, 2010

I’m teaching the programming stream for Diploma in I.T. – Multimedia at the moment, so we’re going to be making some simple games and things over the next couple of months, but to start them off we’re doing a little bit of work on particle effects because they’re simple and fun – you just write a single class which describes the properties and behavior of your particle, and then scale it up by adding a large number of particles to get some pretty nice effects.

So instead of doing a particle fountain, I thought some fireworks might be kinda neat… Take a swiz at the video below and see what you think!

YouTube Preview Image

Full C++ source-code available after the break for those of a curious nature =D

Read the rest of this entry »

Comments
20 Comments »
Categories
Coding, How-To
Tags
Accumulation, Blur, C++, Effects, Fireworks, Motion, OpenGL, Particle, SDL, Trails
Comments rss Comments rss
Trackback Trackback

Translate

Categories

Archives

Tags

3D ActionScript ActionScript 3.0 Adobe AI Ballarat Bash C++ Class Cover CS4 Effect Error Film Flash FPS GLFW Glitch GLSL Hack How-To install Java Kinect Linux Live Mash-Up Microsoft Motion mount OpenGL Particle Problem PS3 Remix Retro script Slides Sound Ubuntu Video VirtualBox Wii Windows XBox

Gamercard

OpenR3dux

Misc.

Flattr this

RSS Feed

r3dux twitter feed



“APATHY ERROR: Don't bother striking any key.”

rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox