4 Corner Pin Distortion Shader

Hey everyone,

I’m just getting into shader programming in Unity, and I’m trying to implement a 4 corner pin distortion shader. Apologies for incorrect terminology.

I’ve tried two different ways of going about the problem. The first is through the vertex function and the second is through the fragment function. In both cases I’m using what I think is the same algorithm.

Basically, I have an offset float read through a shader property. That offset amount gets multiplied by the vertex/fragment’s U texcoord. This means that the offset transforms to 0 as the U coordinate is 0. I also multiply the offset by the Y Coordinate. This means that the offset transforms to 0 as you move down the object as well. I think that this means that my offset value only gets applied to one specific corner, with a fading off as the texture moves to other corners.

If I implement this as a vertex shader where the vertices actually move, the I get distortion along triangle edges which I understand. If I increase the number of subdivisions, then this distortion is minimized but still apparent.

If I implement this as a fragment shader then I no longer having triangle distortion. I assume this is because the algorithm is working on a pixel basis. However, when doing this the result has a curved quality to it as opposed to a linear interpolation.

Hopefully the pictures below demonstrate this clearer. Optimally, I’m looking for a solution that will give me a straight transformation (like in the vertex shader with a highly subdivided polygon), but implemented in a fragment shader.

I hope this all makes sense and thanks for the help.

Shader "Distort/4 Corner Pin" {
Properties {
	_MainTex ("Base (RGB)", 2D) = "white" {}
	_URx ("URx", Range(0,1.0)) = 0.0
}

	SubShader {
		Pass {
			ZTest Always Cull Off ZWrite Off
			Fog { Mode off }
					
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest 
			#include "UnityCG.cginc"

			uniform sampler2D _MainTex;

			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};
			
			float _URx;

			v2f vert( appdata_img v )
			{
				v2f o;
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex); //physical rotation on screen
				o.uv = v.texcoord;
				
				// this is commented out when running the fragment shader
				// adjust the x position based on both the UV values
				o.pos.x += _URx*o.uv.x*o.uv.y;
				
				return o;
			}
			
			float4 frag (v2f i) : SV_Target
			{
				float2 offset = i.uv;
				
				// this is commented out when running the vertex shader
				offset.x += _URx*origUVs.x*origUVs.y;


				return tex2D(_MainTex, offset);
			}
			ENDCG
		}
	}
	Fallback off
}

You need to transform the x points linearly towards (or away from) 0 as a function of height

hence I think your fragment method should read:

float2 uv = i.uv;
uv.x *= 1/(uv.y*_URx+1);
return tex2D(_MainTex, uv);

Hope that helps!

Scribe

Hi,

I adapted your solution with the vertex shader in order to control the 4 corners of a plane, thought it might be useful to some developers.

Shader "Distort/4 Corner Pin" {
	Properties{
		_MainTex("Base (RGB)", 2D) = "white" {}
		_URx("URx", Range(-1.0,1.0)) = 0.0
		_URy("URy", Range(-1.0,1.0)) = 0.0
		_ULx("ULx", Range(-1.0,1.0)) = 0.0
		_ULy("ULy", Range(-1.0,1.0)) = 0.0
		_BLx("BLx", Range(-1.0,1.0)) = 0.0
		_BLy("BLy", Range(-1.0,1.0)) = 0.0
		_BRx("BRx", Range(-1.0,1.0)) = 0.0
		_BRy("BRy", Range(-1.0,1.0)) = 0.0
	}

		SubShader{
			Pass {
				ZTest Always Cull Off ZWrite Off
				Fog { Mode off }

				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma fragmentoption ARB_precision_hint_fastest 
				#include "UnityCG.cginc"

				uniform sampler2D _MainTex;

				struct v2f {
					float4 pos : SV_POSITION;
					float2 uv : TEXCOORD0;
				};

				float _URx;
				float _URy;
				float _ULx;
				float _ULy;
				float _BLx;
				float _BLy;
				float _BRx;
				float _BRy;

				v2f vert(appdata_img v)
				{
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);
					o.uv = v.texcoord;
					o.pos.x += _BLx + ((_URx * o.uv.x*o.uv.y) + (_BRx * o.uv.x) + (_ULx * o.uv.y));
					o.pos.y += _BLy + ((_URy * o.uv.x*o.uv.y) + (_ULy * o.uv.y) + (_BRy * o.uv.x));
					return o;
				}

				float4 frag(v2f i) : SV_Target
				{
					float2 uv = i.uv;
					return tex2D(_MainTex, uv);
				}
				ENDCG
			}
		}
			Fallback off
}