Out of Texture Fragment Shader

Hey there :slight_smile:

I currently trying to implement a wave/ripple shader simliar to the one of the RPG Maker 2000 (hard so show a picture here :(). I kinda got it working with a texture shader which goes like this:

Vertex Shader:


void main(void) {
	gl_TexCoord[0] = gl_MultiTexCoord0;
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;  
}

Fragment Shader:


uniform float time; // time in seconds
uniform sampler2D tex0; 

void main(void) {

	vec2 fragment = gl_TexCoord[0].xy;	
	vec2 temp = vec2(fragment.x, fragment.y);
	
	// playing with sinus
	fragment.x += sin(temp.y * 3.0 * 2.0 * 3.14159 + (time)) / 100.0;

	vec4 color = texture2D(tex0, fragment);
	
	gl_FragColor = color;
}

This works as expected and a get a nice wave. BUT this is only in texture spaces. So if I bind the shader and draw a texture, the fragments which will be pushed to the left or the right are jsut ignored:

I also get this strange artifacts as you can see (The rextangle is an FBO and you can also see that there are dirsty areas in the top left part of the fbo image) . I guess there is something wrong with my wave shader too… But want I want to know is, HOW can I tell the shader to set fragments out of the texture bounds?

I hope someone can help :frowning:

You can’t edit pixels that are outside of the polygon, so it’s probably better if you draw a fullscreen rectangle and adapt the texture coordinates so the (0.0 - 1.0) range is at the middle of the screen. To solve the distortion at the edges (which will fill the whole black frame if you draw a fullscreen rectangle), you can add an if-statement to check if the texture coordinates are out of bounds and return 0 if they are. Another solution is to simply have a 1 pixel black frame around the FBO texture (easily achieved if you render to an FBO, just set the glViewport() and make your texture 1 pixel larger in both horizontal directions) and enable texture coordinate clamping with GL_CLAMP_TO_EDGE.

This can be easily done this at vertex shader. Use grid instead of quad and render that and make wave effect for vertex positions. This give better performance too. I have did heatshimmering and blastwave and some ripple effects with this approach and those work really well on mobile enviroment where you are allways fillrate limited.

Here is really quick and dirty blastwave that I did. Grid is about 24x22 for 800x480 screen.

[quote]You can’t edit pixels that are outside of the polygon, so it’s probably better if you draw a fullscreen rectangle and adapt the texture coordinates so the (0.0 - 1.0) range is at the middle of the screen.
[/quote]
I don’t understand this .___. btw I do all this stuff in Slick2D and I’m really not good at all of this. I always just black box everything and tend to forget stuff :confused:

@pitbuller
Grid? Ho do I use this “grid”?

I don’t understand this .___. btw I do all this stuff in Slick2D and I’m really not good at all of this. I always just black box everything and tend to forget stuff :confused:

@pitbuller
Grid? Ho do I use this “grid”?
[/quote]
Its vertex buffer object or vertex array. Really easy to use with libgdx but dont know how it would tranfer to slick2d. Basically I create 1d array that mimic 2d array and then index it so I can draw it with triangle strips. There is some tricks to get winding right but if you want I can poste grid creation code some where.

Mh, that sounds like quite the work just to get it working :confused: Wouldn’t the grid give the vertrices an edgy look? May I should just make a bigger FBO. It’s 2D anyway so I guess there is no big performance hit.

Look the video does it look too edgy?
Postprocess effects have same cost in 2d or 3d cases. But if you don’t need performance then there is nothing to gain with grid aproach.

The grid approach would be a bit overkill here, IMO. You can achieve what you’re looking for by applying the shader to the entire screen, rather than only applying it to the quad of the image.

Did you check my reply in the Slick forums PM? It showed how to fix the artifacts (which were caused because you were sampling the texture as it changed iirc). Applying the wave effect to the entire screen would look like this:

		// enable the FBO for render-to-texture
		fbo.bind();

		// clear the offscreen FBO image
		g.clear();

		// draw the image to the FBO, centered
		picture.draw(container.getWidth() / 2f - picture.getWidth() / 2f,
				container.getHeight() / 2f - picture.getHeight() / 2f);

		// draw the moving red square 
		object.draw(effect.getValue(), 100);
		object.draw(100, effect.getValue());

		// unbind the FBO so we can draw to the screen
		fbo.unbind();

		// bind the wave shader
		wave.bind();
		wave.setUniform1f("time", time / 1000f);

		// draw our full-screen FBO to the screen -- the shader will be applied
		fboImage.draw();

		// disable shaders so we can go back to normal rendering
		ShaderProgram.unbind();

Result:

Yeah grid is overkill for desktop but necessity for mobile. Performance is so much better. Amount of shaders ALU’s is about 1/500 of fragment version without almoust any visual impact.

@davedes
Yepp I checkd your PM but I still get artifacts :confused: Dunno why, but’s always visible for a short time. I will try it as you said, It’s my only option anyway. I still need a way to make it possible for a single object ratehr then the whole scene.

@pitbuller
it was smooth, but was wondering^^

The code could be changed to do it on a per-image basis:

It’s not efficient (since the shader is applied to the full screen FBO texture each time we draw a wave-effected image) but it will probably be fast enough for most uses.

	public void drawWave(Graphics g, Image image, float x, float y) {
		fbo.bind();
		g.clear();
		image.draw(x, y);
		fbo.unbind();
		wave.bind();
		wave.setUniform1f("time", time / 1000f);
		fboImage.draw();
		ShaderProgram.unbind();
	}

	public void render(GameContainer container, Graphics g)
			throws SlickException {
		bg.draw(0, 0, container.getWidth(), container.getHeight());

		drawWave(g, picture, 50, 50);
		
		drawWave(g, picture.getSubImage(125, 70, 100, 100).getScaledCopy(2f), 415, 55);
	}

The end result is the same, though. Why do you think you need it on a “per object” basis? Maybe if you let us know what you’re going for we can provide a better solution…

Looks awesome, I will try that the next days thanks :slight_smile:

I simply want to have the option to make a effect on an object, for a battle system. I used that in the RPG Maker too because it’s a nice effect. That’s all to it xD
It’s also for learning stuff and I couldn’t make it work be myself ._.