How to curve a 2D Sprite?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By P
:warning: Old Version Published before Godot 3 was released.

Hi,

I’m trying to scale a 2D Sprite’s x-axis as a quadratic function of the y-value. Is there any way to do this using shaders?

I tried the following vertex shader, but it doesn’t work because only the corners are affected, causing the x-scale to increase linearly.

VERTEX.x = SRC_VERTEX.x + pow((1 - UV.y), 2) * sin(TIME) * 10;
:bust_in_silhouette: Reply From: mollusca

A Sprite has only four vertices so the types of deformation you can achieve in the vertex shader are limited. You could try doing it in the fragment shader instead:

vec2 uv_def = vec2(UV.x + pow((1-UV.y), 2) * sin(TIME) * 10, UV.y);
COLOR = tex(TEXTURE, uv_def);

You’ll probably have to add some extra transparent border around your texture to prevent the graphics from getting cut off, you can use the region setting for this. Another option could be using a rectangular Polygon2D. That way you could have more vertices in the y-direction and use the vertex shader to deform them.

Thank you! Both solutions work fine.

P | 2017-12-16 22:04

:bust_in_silhouette: Reply From: Zylann

You can try a script I created, which inherits Node2D but allows you to draw a texture with more vertices for the purpose of being deformed in a shader:

Here is the script used (you’ll have to write your custom vertex shader):
geosprite.gd

# Same as a sprite, but produces more geometry to make vertex shaders look nicer

tool
extends Node2D

export(Texture) var texture setget set_texture, get_texture
export var resolution = 4 setget set_resolution, get_resolution
export var debug_draw = false setget set_debug_draw


func get_texture():
	return texture

func set_texture(tex):
	texture = tex
	update()


func get_resolution():
	return resolution

func set_resolution(res):
	if res < 1 or res > 32:
		return
	resolution = res
	update()


func set_debug_draw(dd):
	debug_draw = dd
	update()


func _draw():
	if texture == null:
		return
	var tex_size = texture.get_size()
	var div_size = tex_size / Vector2(resolution, resolution)
	
	draw_set_transform(-tex_size/2.0, 0.0, Vector2(1,1))
	
	for y in range(0, resolution):
		for x in range(0, resolution):
			var pos = Vector2(x,y)
			var r = Rect2(pos*div_size, div_size)
			draw_texture_rect_region(texture, r, r)
	
	if debug_draw and get_tree().is_editor_hint():
		# Debug draw
		for y in range(0, resolution):
			for x in range(0, resolution):
				var pos = Vector2(x,y)
				var r = Rect2(pos*div_size, div_size)
				stroke_rect(r, Color(1,1,0, 0.5))


func stroke_rect(r, c):
	draw_line(r.pos, Vector2(r.pos.x, r.pos.y), c)
	draw_line(r.pos, Vector2(r.pos.x, r.end.y), c)
	draw_line(r.end, Vector2(r.end.x, r.pos.y), c)
	draw_line(r.end, Vector2(r.pos.x, r.end.y), c)

Thanks! This is also a great solution.

P | 2017-12-16 22:07