GPU Gems Texture Bomb Shader

Hi,

I’ve been working on a GLSL version of the GPU Gems Texture Bomb shader, and have run into a slight problem. Here is my Fragment Shader code:

uniform sampler2D Texture;
uniform sampler2D RandomTexture;
uniform vec2 Scale;

void main()
{
	vec2 xy =  gl_TexCoord[0].xy;
	vec4 px = texture2D(Texture, xy);
	
	vec2 scaledUV = xy * Scale;
	vec2 cell = floor(scaledUV);
	vec2 offset = scaledUV - cell;
	
	vec2 randomUV;
	vec4 random, image;
	
	for (int i = -1; i <= 0; i++) {
		for (int j = -1; j <= 0; j++) {
			vec2 cell_t = cell + vec2(i, j);
			vec2 offset_t = offset - vec2(i, j);
			randomUV = cell_t.xy * vec2(0.037, 0.119);
			random = texture2D(RandomTexture, randomUV);
			image = texture2D(Texture, offset_t - random.xy);
			if (image.a > 0.0) {
				gl_FragColor = image;
			} else {
				gl_FragColor = vec4(0.0);
			}
		}
	}
}

I’m currently on section ‘20-1. Extending the Sampling to Four Cells’

This is what I’m getting (the star glyph has a transparent background):

Can anyone spot any obvious mistakes in my code?

Cheers guys,

a|x
http://machinesdontcare.wordpress.com

PS
Here’s the star glyph (it’s very hard to see because it’s white, on a white background, but it IS there, honestly):

You shouldn’t output a black pixel (gl_FragColor = vec4(0.0):wink: when you actually want to discard it.
So use discard instead.

Hi Inquisitor,

thanks for getting back to me!
Good point. I’ve changed the code as you suggest, but oddly it still doesn’t work (although now in a slightly different way).

I now get:

Any ideas?

a|x

I don’t have time to have an in depth look at it at the moment but reading the next section “20.1.4 Image Priority” starts with it saying:

There’s something not quite right.

Have you simply tried moving on to the next section “Example 20-2. Adding Image Priority” where everything looks a lot better?

Good point 4fingers.

I have tried the next bit too, but this doesn’t seem to help either.

Hmm…

a|x

One of the main things you have to remember to do is set the wrap parameter for texture coordinates to GL_CLAMP.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

You should be able to check this by setting the scale to 1 allowing just one grid to be seen. There should then be one star visible with the shape being cut off at the edges.
Annoyingly it doesn’t mention this till half way down the page at the “20.2 Technical Considerations” section:

The wrap mode for the texture image should be set to CLAMP. Otherwise, we’ll see ghost samples where we shouldn’t. Alternatively, we can clamp the coordinates to the range [0…1]. Most GPUs have hardware to do this at no cost.

Does that help at all?

Hi 4fingers,

ah… I missed that bit! I can’t actually change the texture mode in the app I’m using, but I can clamp the sample position to the 0>1 range and see if that sorts things out.

Thanks for the tip,

a|x

Just tried the following code, which still doesn’t work:

uniform sampler2D Texture;
uniform sampler2D RandomTexture;
uniform vec2 Scale;

void main()
{
	vec2 xy =  gl_TexCoord[0].xy;
	vec4 px = texture2D(Texture, xy);
	
	vec2 scaledUV = xy * Scale;
	vec2 cell = floor(scaledUV);
	vec2 offset = scaledUV - cell;
	
	vec2 randomUV, randomPlusOffset;
	vec4 random, image;
	float priority = -1.0;
	
	for (int i = -1; i <= 0; i++) {
		for (int j = -1; j <= 0; j++) {
			vec2 cell_t = cell + vec2(i, j);
			vec2 offset_t = offset - vec2(i, j);
			randomUV = cell_t.xy * vec2(0.037, 0.119);
			random = texture2D(RandomTexture, randomUV);
			randomPlusOffset = offset_t - random.xy;
			randomPlusOffset.x = clamp(0.0,1.0,randomPlusOffset.x);
			randomPlusOffset.y = clamp(0.0,1.0,randomPlusOffset.y);
			image = texture2D(Texture, randomPlusOffset);
			if (random.a > priority && image.a > 0.0) {
				gl_FragColor = image;
				priority = random.w;
			} else {
				gl_FragColor = vec4(0.0);
				//discard;
			}
		}
	}
}

I may just give up on this implementation, and try adapting the Orange Book texture bomb shader. I’m really interesting in the Voronoi implementation mentioned later on on the page, which I guess could be tweaked to fit in with the Orange Book method.

a|x

I’ve just found someone else’s GLSL adaptation of the same code. This works nicely, so all is now cool.

// This is a fragment shader to show texture bombing.  It uses
//   a random texture to place 3D polka dots of random color
//   in random locations on the model.  For more information
//   on Texture Bombing techniques see the following books:
//      1.  Texturing and Modeling - A Procedural Approach
//         Copyright 1998 by Ebert et al.
//      2.  GPU Gems Copyright 2005 
//         Edited by Randima Fernando
//
// author(s): Joshua Doss
//
// Copyright (C) 2002-2006  3Dlabs Inc. Ltd.
//
// See 3Dlabs-License.txt for license information
//

varying vec3 MCPosition;

//Create uniform variables so dots can be spaced and scaled by user
uniform float Scale, DotSize;
uniform sampler2D RandomTex;

// Create colors as uniform variables so they can be easily changed
uniform vec4 BGColor, PolkaDotColor;
uniform vec3 DotOffset;
uniform vec2 PositionTextureMultiplier;

void main(void)
{
	float radius2;
	vec2  randomXY;
	vec3  finalcolor = BGColor.xyz;
	vec3  dotSpacing = vec3(Scale);
	vec3  scaledXYZ = MCPosition * dotSpacing;
	vec3  cell = floor(scaledXYZ);
	vec3  offset = scaledXYZ - cell;
	vec3  currentOffset;
	vec4  random;
	
	float priority = -1.0;
	for(float i = -1.0; i <= 0.0; i++)
	{
		for(float j = -1.0; j <= 0.0; j++)
		{
			for(float k = -1.0; k <= 0.0; k++)
			{
				vec3 currentCell = cell + vec3(i, j, k);
				vec3 cellOffset = offset - vec3(i, j, k);
				randomXY = currentCell.xy * PositionTextureMultiplier + currentCell.z * 0.003;
				random = texture2D(RandomTex, randomXY);
                	currentOffset = cellOffset - (vec3(0.5, 0.5, 0.5) + vec3(random));
             		
				radius2 = dot(currentOffset, currentOffset);
				if(random.w > priority && radius2 < DotSize)
				{
					finalcolor = texture2D(RandomTex, randomXY).xyz;
					priority = random.w;
				}
			}
		}
	}
	
	gl_FragColor = vec4(finalcolor, 1.0);
}

Thanks for all your advice, guys!
The key turned out to be to create a variable for the final output color, and set it before the nested loops.

Thanks again,

a|x

Yeah, that method gets rid off the texture lookup and applies a procedural texture instead. This is fine if primitive shapes is all that your want.

In the Book “Texturing and Modeling - A Procedural Approach” it manages to use a procedural texture of a star, but creating anything with a more complex design and your better off using a texture. So before you go I thought I might share with you my implementation that prevents the texture from displaying the repeating artefacts despite having GL_TEXTURE_WRAP set to GL_REPEAT.

Here is what you might expect to see with nothing stopping the texture from wrapping:

Then with the added checks it looks like this:

All it does is tell the shader to ignore placing the colour once its in the area outside the texture being placed. By doing these checks it also reduces the number of texture calls for each cell:

uniform sampler2D BombingTexture;
uniform sampler2D RandomTexture;

void main() {
    vec4 random, colour;
    vec2 randomUV, current, cell_t, offset_t;
    float i, j;
    bool checkX, checkY;

    vec2 scaledUV = gl_TexCoord[0].xy * 20.0;
    vec2 cell = floor(scaledUV);
    vec2 offset = scaledUV - cell;
      
    float priority = -1.0;
    colour = vec4(gl_TexCoord[1]);
    for (i = -1.0; i <= 0.0; i++) {
      for (j = -1.0; j <= 0.0; j++) {
            current = vec2(i, j);
            cell_t = cell + current;
            offset_t = offset - current;

            randomUV = cell_t * vec2(0.037, 0.119);
            random = texture2D(RandomTexture, randomUV);

            if(i == 0.0) checkX = (offset.x>random.x);
            else checkX = (offset.x<random.x);
            if(j == 0.0) checkY = (offset.y>random.y);
            else checkY = (offset.y<random.y);
            
            if(checkX && checkY){
                if ( texture2D(BombingTexture, offset_t - random.rg).a > 0.0 && 
                    random.b > priority) {
                    colour = vec4(random.rgb, 1.0);
                    priority = random.b;
                }
            }
        }
    }
    gl_FragColor = colour;
}

The only thing you need to watch out for is that you have to have the same texture cordinate system as me. This is my current set up:
Top Left = (0,1) | Top Right = (1,1)
Bottom Left = (0,0) | Bottom Right = (1,0)
If you have a different set up and cant change it on the OpenGL side then you will have to change some of the code on the fragment shader to account for the different coordinate system.

Anyway I hope this is as much use as it was for me.

Hi again 4fingers.

I should have mentioned that I’d replaced the texture lookup with a procedural circle in my code above. I did this mainly because I was working through the GPU Gems page primarily because I wanted to implement the Voronoi method mentioned later on. I did actually get somewhere near 20-10a, but it’s still a bit ‘glitchy’ in places.

varying vec3 MCPosition;

//Create uniform variables so dots can be spaced and scaled by user
uniform float Scale, DotSize;
uniform sampler2D RandomTex;

// Create colors as uniform variables so they can be easily changed
uniform vec4 BGColor, PolkaDotColor;
uniform vec3 DotOffset;
uniform vec2 PositionTextureMultiplier;

void main(void)
{
	float radius2;
	vec2  randomXY;
	vec3  finalcolor = BGColor.xyz;
	vec3  dotSpacing = vec3(Scale);
	vec3  scaledXYZ = MCPosition * dotSpacing;
	vec3  cell = floor(scaledXYZ);
	vec3  offset = scaledXYZ - cell;
	vec3  currentOffset;
	vec4  random;
	
	float priority = 999.0;
	
	for(float i = -1.0; i <= 0.0; i++)
	{
		for(float j = -1.0; j <= 0.0; j++)
		{
			for(float k = -1.0; k <= 0.0; k++)
			{
				vec3 currentCell = cell + vec3(i, j, k);
				vec3 cellOffset = offset - vec3(i, j, k);
				randomXY = currentCell.xy * PositionTextureMultiplier + currentCell.z;// * 0.003;
				random = texture2D(RandomTex, randomXY);
                	currentOffset = cellOffset - (vec3(0.5, 0.5, 0.5) + vec3(random));
             		
				radius2 = dot(currentOffset, currentOffset);
				if(radius2 < priority)
				{
					//finalcolor = texture2D(RandomTex, randomXY + vec2(0.13,0.4)).xyz;
					finalcolor = vec3(radius2 * 2.0);//texture2D(RandomTex, randomXY).xyz;
					priority = radius2;
				}
			}
		}
	}
	
	gl_FragColor = vec4(finalcolor, 1.0);
}

I’d like to get to 20-10b, but I’m not to sure how to increase the number of samples per cell. In fact, this itself is only a stepping-stone to what I’m really after, which is some kind of realtime voronoi approximation based in live camera input rather than a random texture. I think the code will need quite a lot of tweaking to get to that though. I’ll need much larger variation in the sizes of the regions, which I guess means a smaller cell size, but more samples per cell. Lots of intriguing possibilities…

a|x

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.