2D-perspective

This shader “fakes” a 3D-camera perspective on CanvasItems.

The shader works out-of-the-box with nodes Sprite and TextureRect, as long as the rect_size equals the size of the texture. If this isn’t the case, you can do the following change:

//VERTEX += (UV - 0.5) / TEXTURE_PIXEL_SIZE * tang * (1.0 - inset);
// to (rect_size is a uniform):
VERTEX += (UV - 0.5) * rect_size * tang * (1.0 - inset);

Also, remember to enable mipmaps and anisotropic for the texture to retain quality with harsh angles.

Shader code
// Hey this is Hei! This shader "fakes" a 3D-camera perspective on CanvasItems.
// License: MIT

shader_type canvas_item;

// Camera FOV
uniform float fov : hint_range(1, 179) = 90;
uniform bool cull_back = true;
uniform float y_rot : hint_range(-180, 180) = 0.0;
uniform float x_rot : hint_range(-180, 180) = 0.0;
// At 0, the image retains its size when unrotated.
// At 1, the image is resized so that it can do a full
// rotation without clipping inside its rect.
uniform float inset : hint_range(0, 1) = 0.0;
// Consider changing this to a uniform and changing it from code

varying flat vec2 o;
varying vec3 p;

const float PI = 3.14159;

// Creates rotation matrix
void vertex(){
	float sin_b = sin(y_rot / 180.0 * PI);
	float cos_b = cos(y_rot / 180.0 * PI);
	float sin_c = sin(x_rot / 180.0 * PI);
	float cos_c = cos(x_rot / 180.0 * PI);
	
	mat3 inv_rot_mat;
	inv_rot_mat[0][0] = cos_b;
	inv_rot_mat[0][1] = 0.0;
	inv_rot_mat[0][2] = -sin_b;
	
	inv_rot_mat[1][0] = sin_b * sin_c;
	inv_rot_mat[1][1] = cos_c;
	inv_rot_mat[1][2] = cos_b * sin_c;
	
	inv_rot_mat[2][0] = sin_b * cos_c;
	inv_rot_mat[2][1] = -sin_c;
	inv_rot_mat[2][2] = cos_b * cos_c;
	
	
	float t = tan(fov / 360.0 * PI);
	p = inv_rot_mat * vec3((UV - 0.5), 0.5 / t);
	float v = (0.5 / t) + 0.5;
	p.xy *= v * inv_rot_mat[2].z;
	o = v * inv_rot_mat[2].xy;

	VERTEX += (UV - 0.5) / TEXTURE_PIXEL_SIZE * t * (1.0 - inset);
}

void fragment(){
	if (cull_back && p.z <= 0.0) discard;
	vec2 uv = (p.xy / p.z).xy - o;
    COLOR = texture(TEXTURE, uv + 0.5);
	COLOR.a *= step(max(abs(uv.x), abs(uv.y)), 0.5);
}
Tags
2d, perspective, skew
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Hei

Texture population using Texture

Subscribe
Notify of
guest

11 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Torguen
Torguen
2 years ago

Very good shader, I was looking for something like this.
Just one question, everything moves taking the center of the sprite as the center point, can that be changed?
Thank you for this.

Torguen
Torguen
2 years ago
Reply to  Hei

Thanks! it works very well.

yikescloud
yikescloud
2 years ago

Thanks for your shader! But I’m not very understand about how the shader works, would you like to explain it a little bit? And I came up some issue about this shader, very appreciate for any advice. Thanks!

ymh
ymh
8 months ago
Reply to  yikescloud

me too

AndreWharn
2 years ago

This shader is amazing, I was looking for something exactly like this, thank you so much for making it!!
One small request though, is it possible to make it compatible with a NinePatchRect node? I would really need it for a node like that, but the texture looks and behaves weirdly with it.

mreliptik
1 year ago

Awesome shader, thank you!

John Miller
John Miller
9 months ago

great, but how would one do this toe the entire camera scene?

boko
6 months ago

incredible shader, works flawlessly, exactly what i needed !!!!!

minzojian
minzojian
4 months ago

cool shader but not work fine under tilesprite texture ๐Ÿ™

ElusiveMoose
3 months ago

Hey, just for anybody running into the issue I was –
If you are having problems with dynamically sized textures getting scaled improperly by the shader… Just set the FOV property on the shader to 1. Maybe this was obvious to some but I lost a full day of work to this, due to my inexperience with shaders! Hope this can help somebody avoid my fate. Love the shader by the way! Happy shading ๐Ÿ™‚