Skip to content

Instantly share code, notes, and snippets.

@ducklin5
Last active June 1, 2018 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ducklin5/c3a13bc0b852be34db9c4e66e2a23007 to your computer and use it in GitHub Desktop.
Save ducklin5/c3a13bc0b852be34db9c4e66e2a23007 to your computer and use it in GitHub Desktop.
Godot 2D remake of LuggLD's and cjacobwade's SmearFrame Effect
shader_type canvas_item;
uniform float texSizeLength = 1.0 ;
uniform vec2 prev_pos = vec2(1.0,1.0) ;
uniform vec2 current_pos = vec2(1.0,1.0);
uniform float prev_rot = 0.0;
uniform float current_rot = 10.0;
uniform float _NoiseHeight = 64;
uniform float _NoiseScale = 5.0;
float rand(vec2 n) {
return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
float noise(vec2 p){
vec2 ip = floor(p);
vec2 u = fract(p);
u = u*u*(3.0-2.0*u);
float res = mix(
mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x),
mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y);
return res*res;
}
mat2 rotate2d(float _angle){
return mat2(vec2(cos(_angle),-sin(_angle)),
vec2(sin(_angle),cos(_angle)));
}
void vertex(){
// global position of the current vertext in a previous frame
vec2 prevVertexWorldPos = prev_pos + rotate2d(-prev_rot) * VERTEX;
// global position of the current vertext now
vec2 vertexWorldPos = current_pos + rotate2d(-current_rot) * VERTEX;
//displacement of the unshaded vertex from previous frame
vec2 displacement = vertexWorldPos - prevVertexWorldPos;
// dirDot = cos(theta)
// where theta is the angle between the displacment vector and the position of this vertex from the object center
float dirDot = dot(normalize(displacement), normalize(VERTEX));
//determin the max smear length
vec2 unitVec = vec2(1, 1) * _NoiseHeight;
//smearVector is proportional to displacement vector
// and the magnitude of the fractional distance of the vertex from the center
// clamp that the between this maximum ( and minimum)
float fractionalVertexDistance = length(vertexWorldPos-current_pos)/texSizeLength;
vec2 smearVector = clamp(displacement * fractionalVertexDistance , -unitVec, unitVec);
float temp = 1.0;
if(length(displacement) < 0.0){
temp = 0.0;
}
//THE MAGIC
//smearOffset is proportional to smearVector
//smearOffset is negative if the vertex is on opposite side of prev_pos otherwise 0
vec2 smearOffset = smearVector * clamp((step(0,cos(current_rot))*2.0-1.0)*sign(dirDot), -1, 0) * temp;
// Calculate a noise float
temp = 1.0;
if(_NoiseScale > 0.0){
temp = noise(vertexWorldPos * _NoiseScale);
}
// multiply the smearOffset by the float
smearOffset *= temp;
VERTEX += smearOffset * rotate2d(-current_rot);
}
@ducklin5
Copy link
Author

ducklin5 commented May 31, 2018

Note: You have to set the current and previous position of the node every frame manually, like so:

extends Sprite

var prev_pos
var prev_rot

func _ready():
	get_material().set_shader_param("texSizeLength", texture.get_size().length())
	get_material().set_shader_param("_NoiseHeight", 64)
	get_material().set_shader_param("_NoiseScale", 0.5)
	prev_pos = global_position
	prev_rot = rotation
	set_process(true)

func _process(delta):
	var current_pos = global_position
	var current_rot = rotation
	
	get_material().set_shader_param("prev_pos", prev_pos)
	get_material().set_shader_param("current_pos", current_pos)
	get_material().set_shader_param("prev_rot", prev_rot)
	get_material().set_shader_param("current_rot", current_rot)
	
	prev_pos = lerp(prev_pos, current_pos,clamp(delta*2,0,1))
	
	prev_rot = current_rot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment