Sprite Water Reflection Pixel Art Pokémon Style

Shader that simulates the Pokémon Gen 3 water reflection effect.

It is ready to use with sprite sheets textures. You can modify the movement options and the color.

It is my first shader in Godot, any feedback is welcome.

Edit:

[2023-10-24]

* Fixed problem with displacementPixels different to 1.

Shader code
shader_type canvas_item;

uniform int hFrames = 1; //Number of horizontal frames in the Texture
uniform int vFrames = 1; //Number of vertical frames in the Texture

group_uniforms Displacement;
uniform bool enableMovement = true;
uniform int displacementPixels = 1; // Number of pixels to displace

uniform float timeGap : hint_range(0.0, 1.0, 0.01) = 0.4; // Time in seconds that Left side movement will wait to move

uniform float speed = 1.25; // Movement speed
/*
SideRelationTime determines the time ratio spent in each step.
For example, with a value of 0.25, a quarter of the time is spent in the normal position,
and the remaining three-quarters in the shifted position.
*/
uniform float leftSideRelationTime : hint_range(0.0, 1.0, 0.01) = 0.3;
uniform float rightSideRelationTime : hint_range(0.0, 1.0, 0.01) = 0.3;

group_uniforms Color;
uniform vec3 color: source_color = vec3(1.0);
uniform float colorMix: hint_range(0.0, 1.0, 0.01) = 0.0;
uniform float alpha: hint_range(0.0, 1.0, 0.01) = 1.0;

float ShiftPixels(float uvX, float normalizedUvX, float shift) {
	if (normalizedUvX <= 0.5) {
		//left side
		uvX -= shift * step(mod((TIME + timeGap) * speed, 1.0), leftSideRelationTime);
	}
	else {
		//right side
		uvX -= shift * step(mod(TIME * speed, 1.0), rightSideRelationTime);
	}

	return uvX;
}

// We have to resize the sprite so it is not cut
void vertex(){
	switch (VERTEX_ID){
		case 0:
			VERTEX.x -= float(displacementPixels);
			break;
		case 1:
			VERTEX.x -= float(displacementPixels);
			break;
		case 2:
			VERTEX.x += float(displacementPixels);
			break;
		case 3:
			VERTEX.x += float(displacementPixels);
			break;
	}
}

vec2 AdjustUvToNewSize(vec2 originalUV, float texturePixelSizeX, vec2 numberFrames, float frameX) {
	// relation between the previous size and the new one
	float relation = (1.0 + 2.0 * texturePixelSizeX * float(displacementPixels) * numberFrames.x);
	
	vec2 uv = originalUV;
	
	uv.x *= relation;
	
	// adapt new position to new uv
	//uv.x -= float(displacementPixels) * texturePixelSizeX;
	
	// displaces the pixels depending on the sprite column
	//uv.x -= float(displacementPixels) * frameX * numberFrames.x * texturePixelSizeX / 2.0;
	
	// optimized operation
	uv.x -= float(displacementPixels) * texturePixelSizeX * (1.0 + frameX * numberFrames.x / 2.0);
	
	return uv;
}

void fragment() {
	vec2 numberFrames = vec2(float(hFrames), float(vFrames));
	vec2 normalizedUv = mod(UV * numberFrames, vec2(1.0));
	float frameX = floor(UV.x * numberFrames.x);

	vec2 uv = AdjustUvToNewSize(UV, TEXTURE_PIXEL_SIZE.x, numberFrames, frameX);

	if (enableMovement) {
		uv.x = ShiftPixels(uv.x, normalizedUv.x, TEXTURE_PIXEL_SIZE.x * float(displacementPixels));
	}

	// discard pixels that are out of the sprite
	if (uv.x <= frameX * 1.0 / numberFrames.x || uv.x >= 1.0 / numberFrames.x * (frameX + 1.0)) {
		discard;
	}

	COLOR = texture(TEXTURE, uv);
	COLOR.rgb = mix(COLOR.rgb, color, colorMix);
	COLOR.a *= alpha;
}
Tags
2d, pokemon, reflection, water
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from christt105

Unmoving Plaid Effect

Related shaders

Pixel Art Style Shader

Pixel Art Water

2D Pixel Art Water

Subscribe
Notify of
guest

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Thagmar
Thagmar
1 year ago

This looks great, but I can’t figure out how to implement it. Do you have a tutorial written up anywhere?

Sergi Martinez
Sergi Martinez
1 year ago

Hi, nice work, how can i make the reflection shows only on water tiles and it cuts the reflection on corners??

Rathalos23
Rathalos23
1 month ago

Hey Christt105 !

Just discovered your Shader and it works perfectly for AnimatedSprite2D !
Using multiple layers and Z index allow for perfect Pokémon Ruby/Sapphire/Emerald water reflection.

Sadly, i can’t make it work for a simple TimeMap (or tile).
The effect is kinda working but not at intended, no matter the parameters of the Shader.

Do yo have any idea why ?

Here’s the result so far :
https://streamable.com/wsdzv4

The idea would be to apply your Shader to a tile of a Tilemap called “Reflections Layer”, and simply reverse the Y axis. Playing with other layers and Z axis, i’m sure we can pretty much implement the simplest version of Pokémon water reflect.