- Home /
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
}
Actually, I have another question to add to this. I've implemented Scribe's solution and gotten the upper-left/lower-left and upper-right/lower-right working separately. However when I try to combine them, I get a curved line in the texture. I know I'm making some math mistake when trying to combine everything, but I can't quite figure out what it is.
float temp_URx = offset.y*_URx;
float temp_LRx = (1-offset.y)*_LRx;
float temp_ULx = offset.y*_ULx;
float temp_LLx = (1-offset.y)*_LLx;
float temp_URx_LRx = offset.x/(temp_URx + temp_LRx + 1);
float temp_ULx_LLx = (1-offset.x)/(temp_ULx + temp_LLx + 1);
offset.x = temp_URx_LRx ; // this works for the right corners
offset.x = temp_ULx_LLx; // this works for the left corners
Hey, did you get this to work properly? I am in the same place and can't find a solution.
Hi, yes I did, but it was a bit more involved than I first thought.
Because any specific vertex on the quad depends on the positions of all the vertices, it can not be calculated as simple as this example.
The solution was to create a $$anonymous$$atrix within a separate script that was set up to transform everything correctly. Then that matrix gets passed into the vertex shader. So something like the following:
float4 H; // this gets set with a material.setmatrix() from a script
v2f vert( appdata_img v )
{
v2f o;
o.pos = mul (UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, v.vertex); //physical rotation on screen
o.uv = v.texcoord;
o.pos = mul (H, o.pos);
return o;
}
Answer by Scribe · Nov 19, 2014 at 01:41 PM
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
Thanks for the reply.
However, if I implement that then the entire row moves as one based on height. I only want one specific corner to move, so doesn't my method need to be a function of height and width?
The middle picture I included has the exact look that I'm going for, but unfortunately running the same code in the fragment section gives me a curve ins$$anonymous$$d of a straight linear interpolation.
this is a function of both x and y positions due to the *=
it is equivalent to uv.x = uv.x/(uv.y*_URx+1);
Answer by OPfeifferBweez · Mar 19, 2019 at 03:51 PM
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
}
This shader suffers from the triangle distortion problem shown in OP's first figure.
Curious, because I do not get any distortion. I used the shader on plane with a render texture on it, plus an orthographic camera fil$$anonymous$$g it.
$$anonymous$$aybe it depends on the graphics API you are using ?
Your answer
Follow this Question
Related Questions
How to force the compilation of a shader in Unity? 5 Answers
UnityObjectToClipPos is inverted? 0 Answers
Unity 5 upgrade broke lightmapped shader 1 Answer
How to get view space position of a pixel 0 Answers
Sizeof float in cg shader 1 Answer