• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by ajk48n · Nov 19, 2014 at 09:48 AM · shadercgdistortion

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
 }
 

Shader Examples

shader-examples.png (167.8 kB)
Comment
Add comment · Show 3
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image ajk48n · Nov 20, 2014 at 03:29 AM 0
Share

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
avatar image utzleben · Dec 09, 2014 at 03:58 PM 0
Share

Hey, did you get this to work properly? I am in the same place and can't find a solution.

avatar image ajk48n · Dec 10, 2014 at 04:56 AM 0
Share

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;
 }

2 Replies

· Add your reply
  • Sort: 
avatar image
0
Best Answer

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

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image ajk48n · Nov 19, 2014 at 01:58 PM 0
Share

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.

avatar image Scribe · Nov 19, 2014 at 03:52 PM 0
Share

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);

alt text

avatar image ajk48n · Nov 19, 2014 at 10:15 PM 0
Share

Oh yes, that makes sense for individual corners, thanks.

avatar image
0

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
 }
 
Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image green-giant · Mar 27, 2019 at 02:58 PM 0
Share

This shader suffers from the triangle distortion problem shown in OP's first figure.

avatar image OPfeifferBweez green-giant · Mar 28, 2019 at 09:35 AM 0
Share

Curious, because I do not get any distortion. I used the shader on plane with a render texture on it, plus an orthographic camera filming it.

$$anonymous$$aybe it depends on the graphics API you are using ?

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

6 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How to force the compilation of a shader in Unity? 4 Answers

Sizeof float in cg shader 1 Answer

How to get color of a fragment in CG after it's lit? 0 Answers

Dissolve Shader Problem 1 Answer

How can I make my own shader include file (.cginc or .glslinc)? 2 Answers

  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges