4 Point Perspective Transformation

A canvas shader implementation of the 4-point perspective transformation, adapted from a code from this BLOG (by Haritha Thilakarathne, who themselves adapted it from a snippet by Florian Segginger).

4 Points used as parameters of this shader operate in UV scale.

Shader allows transformation to extend beyond the original size. Although if you want the image to stay properly centered, you need to provide the size of the node into the “plane_size” parameter (in most Control nodes, it is simply the “size” parameter, while in Node2D ones, you may need to calculate it based on Texture size, scale, etc).

 

Shader code
shader_type canvas_item;

uniform vec2 up_left = vec2(0.0);
uniform vec2 up_right = vec2(1.0, 0.0);
uniform vec2 down_right = vec2(1.0, 1.0);
uniform vec2 down_left = vec2(0.0, 1.0);

uniform vec2 plane_size;

varying mat3 trans_inv;
varying vec2 trans_scale;
varying vec2 trans_offset;

mat3 perspective_transform(vec2[4] poly) { 
	float dx1 = poly[1].x - poly[2].x;
	float dx2 = poly[3].x - poly[2].x;
	float dx3 = poly[0].x - poly[1].x + poly[2].x - poly[3].x;
	float dy1 = poly[1].y - poly[2].y;
	float dy2 = poly[3].y - poly[2].y;
	float dy3 = poly[0].y - poly[1].y + poly[2].y - poly[3].y;
 
	float a13 = (dx3 * dy2 - dy3 * dx2) / (dx1 * dy2 - dy1 * dx2);
	float a23 = (dx1 * dy3 - dy1 * dx3) / (dx1 * dy2 - dy1 * dx2);
	float a11 = poly[1].x - poly[0].x + a13 * poly[1].x;
	float a21 = poly[3].x - poly[0].x + a23 * poly[3].x;
	float a31 = poly[0].x;
	float a12 = poly[1].y - poly[0].y + a13 * poly[1].y;
	float a22 = poly[3].y - poly[0].y + a23 * poly[3].y;
	float a32 = poly[0].y;
 
	mat3 transform_mat = mat3(
		vec3(a11, a12, a13),
		vec3(a21, a22, a23),
		vec3(a31, a32, 1)
	);
	
	return inverse(transform_mat);
}

vec2 mult_mat_inv_point(mat3 mat_inv, vec2 point) {
	vec3 result = mat_inv * vec3(point, 1.0);
	return vec2(result.x / result.z, result.y / result.z);
}

void vertex() {
	vec2[] poly = {up_left, up_right, down_right, down_left};
	//PRECALCULATING TRANSFORMATION MATRIX
	trans_inv = perspective_transform(poly);
	//SCALE AND OFFSET TO TRANSFORM VERTEX AND LATER UV TO SUPPORT OUT OF BOUNDING BOX TRANSFORMATION
	trans_scale = vec2(
		max(max(up_right.x, down_right.x) - min(up_left.x, down_left.x), 1.0),
		max(max(down_left.y, down_right.y) - min(up_left.y, up_right.y), 1.0)
	);
	trans_offset = vec2(
		min(min(up_left.x, down_left.x), 0.0),
		min(min(up_left.y, up_right.y), 0.0)
	);
	VERTEX = VERTEX * trans_scale + plane_size * trans_offset;
}

void fragment() {
	vec2 pUV = mult_mat_inv_point(trans_inv, UV * trans_scale + trans_offset);
	//CUTTING OUT OF BOUNDS TEXTURE OFF. FEEL FREE TO REMOVE IF NOT NEEDED
	if (pUV.x > 1.0 || pUV.y > 1.0 || pUV.x < 0.0 || pUV.y < 0.0) {
		COLOR = vec4(0.0);
	} else {
		vec4 point = texture(TEXTURE, pUV);
		COLOR = point;
	}
}
Tags
4 points, perspective, polygon, Transformation
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.

Related shaders

Perspective grid animated

Perspective Grid Remix March2021

Perspective Warp/Skew Shader

Subscribe
Notify of
guest

10 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
saletrak
saletrak
3 months ago

whenever i set this shader on node, it becomes transparent

saletrak
saletrak
3 months ago
Reply to  viiragon

Sprite2D, TextureRect. I used 4.3.dev6.

saletrak
saletrak
3 months ago
Reply to  viiragon

Found what is wrong. Shader doesn’t work in Compatibility mode.

saletrak
saletrak
3 months ago
Reply to  viiragon

great mate, looks like it works fine now

shaderNoob
shaderNoob
3 months ago

Im a newcomer to gdshader and I wonder that how to prevent it from being cut by the edges of node?