Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Rotation of Texture/UVs directly from a shader

Discussion in 'Shaders' started by rymbrant, Sep 9, 2012.

  1. rymbrant

    rymbrant

    Joined:
    May 11, 2009
    Posts:
    14
    I have been looking through the forums and google for ages to find a way of rotating a texture from within the shader. Is there some sort of built in texture handling for this or some operation that can be used on the UVs? Panning is quite simple as just adding to the UVs essentially but for rotation i cannot even begin to figure out a way to do it.

    I have read through many posts with no answers so i figured i would try a post on it myself, any help at all would be welcome but know that i fully understand how to rotate it from script but that is definitely not what i want for specific reasons.

    So to summarize: I want to rotate a texture/uvs in a CG shader and NOT through script (though i realize and acknowledge it is fully possible to do so and it has many merits.)
     
    serdar_ likes this.
  2. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    You have to apply a rotation matrix to your texture coordinates in the Cg shader before doing the texture lookup.
     
  3. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    This should do it...

    Code (csharp):
    1.  
    2. Shader "Custom/RotateUVs" {
    3.     Properties {
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.         _RotationSpeed ("Rotation Speed", Float) = 2.0
    6.     }
    7.     SubShader {
    8.         Tags { "RenderType"="Opaque" }
    9.         LOD 200
    10.        
    11.         CGPROGRAM
    12.         #pragma surface surf Lambert vertex:vert
    13.  
    14.         sampler2D _MainTex;
    15.  
    16.         struct Input {
    17.             float2 uv_MainTex;
    18.         };
    19.  
    20.         float _RotationSpeed;
    21.         void vert (inout appdata_full v) {
    22.             float sinX = sin ( _RotationSpeed * _Time );
    23.             float cosX = cos ( _RotationSpeed * _Time );
    24.             float sinY = sin ( _RotationSpeed * _Time );
    25.             float2x2 rotationMatrix = float2x2( cosX, -sinX, sinY, cosX);
    26.             v.texcoord.xy = mul ( v.texcoord.xy, rotationMatrix );
    27.         }
    28.  
    29.         void surf (Input IN, inout SurfaceOutput o) {  
    30.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    31.             o.Albedo = c.rgb;
    32.             o.Alpha = c.a;
    33.         }
    34.         ENDCG
    35.     }
    36.     FallBack "Diffuse"
    37. }
    38.  
     
  4. kaz2057

    kaz2057

    Joined:
    Jan 18, 2011
    Posts:
    326
    Farfarer thansk for your support.

    I need to move my texture rotation and I try so use your shader mixed with my CG shader
    But I have a problem.

    I use this C# script to calculate my rotationMatrix

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. using System.Collections;
    5. public class RotateUVs : MonoBehaviour {
    6.     public float rotateSpeed = 10f;
    7.     public Vector2 pivot = new Vector2(0.5f, 0.5f);
    8.  
    9.     protected void Update() {
    10.         // Construct a rotation matrix and set it for the shader
    11.         Matrix4x4 t = Matrix4x4.TRS(-pivot, Quaternion.identity, Vector3.one);
    12.         Quaternion rotation = Quaternion.Euler(0, 0, Time.time * rotateSpeed);
    13.         Matrix4x4 r = Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one);  
    14.         Matrix4x4 tInv = Matrix4x4.TRS(pivot, Quaternion.identity, Vector3.one);
    15.         renderer.material.SetMatrix("_Rotation", tInv*r*t);
    16.     }
    17.  
    18. }
    19.  
    and my shader piece is
    Code (csharp):
    1.  
    2. #include "UnityCG.cginc"       
    3.  
    4. sampler2D _MainTex;
    5. ..
    6.  
    7. CGINCLUDE      
    8.  
    9. struct v2f
    10. {
    11.     half4 pos : SV_POSITION;
    12.     half4 uv : TEXCOORD0;
    13. };
    14. ..
    15.  ---> uniform float2x2 _Rotation;  
    16.  
    17. ENDCG
    18.  
    19. SubShader {
    20.     Tags { "RenderType"="Opaque" }
    21.     LOD 300
    22.    
    23.     Pass {
    24.         CGPROGRAM
    25. ...    
    26.         v2f_full vert (appdata_full v)
    27.         {
    28.             v2f_full o;
    29.             o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    30.             o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex)
    31.              ---> v.texcoord.xy = mul ( v.texcoord.xy, _Rotation );
    32. ....
    33.  
    Ok ,after this nothing happen!!! No texture move. How is it possibile? Can you help me please? I'm going crazy because of this!!!
     
    Last edited: Oct 22, 2012
  5. kaz2057

    kaz2057

    Joined:
    Jan 18, 2011
    Posts:
    326
    Thank you Albergine for your interest but I don't need all these shades.

    Then this CG code line is a part of a complicated CG Bumped VertexPerPixelSpecular shader.

    If you can help me just with this part of code, I would be very grateful.
    Thanks

     
  6. kaz2057

    kaz2057

    Joined:
    Jan 18, 2011
    Posts:
    326
    Hi Farfarer,

    After a loto of testing, I have put my rotationMatrix Matrix4x4 from script to my shader.

    However I think your shader is more interesting. But it has a problem. The rotation pivot is located on the corner of my plane and not in the middle.

    Is it possibile center the rotation matrix pivot in CG Shader?

    Thanks
     
  7. lzt120

    lzt120

    Joined:
    Apr 13, 2010
    Posts:
    93
    Can just use the shader to make object move around like a circle ?
     
  8. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Try this one
    Code (csharp):
    1.     Shader "Custom/RotateUVs" {
    2.         Properties {
    3.             _MainTex ("Base (RGB)", 2D) = "white" {}
    4.             _RotationSpeed ("Rotation Speed", Float) = 2.0
    5.         }
    6.         SubShader {
    7.             Tags { "RenderType"="Opaque" }
    8.             LOD 200
    9.            
    10.             CGPROGRAM
    11.             #pragma surface surf Lambert vertex:vert
    12.      
    13.             sampler2D _MainTex;
    14.      
    15.             struct Input {
    16.                 float2 uv_MainTex;
    17.             };
    18.      
    19.             float _RotationSpeed;
    20.             void vert (inout appdata_full v) {
    21.                 v.texcoord.xy -=0.5;
    22.                 float s = sin ( _RotationSpeed * _Time );
    23.                 float c = cos ( _RotationSpeed * _Time );
    24.                 float2x2 rotationMatrix = float2x2( c, -s, s, c);
    25.                 rotationMatrix *=0.5;
    26.                 rotationMatrix +=0.5;
    27.                 rotationMatrix = rotationMatrix * 2-1;
    28.                 v.texcoord.xy = mul ( v.texcoord.xy, rotationMatrix );
    29.                 v.texcoord.xy += 0.5;
    30.             }
    31.      
    32.             void surf (Input IN, inout SurfaceOutput o) {  
    33.                 half4 c = tex2D (_MainTex, IN.uv_MainTex);
    34.                 o.Albedo = c.rgb;
    35.                 o.Alpha = c.a;
    36.             }
    37.             ENDCG
    38.         }
    39.         FallBack "Diffuse"
    40.     }
     
    Tubocass likes this.
  9. kaz2057

    kaz2057

    Joined:
    Jan 18, 2011
    Posts:
    326
    Rea thank you soo much!!!! I want know where you can find this solution because I try to implement this by wikipedia http://en.wikipedia.org/wiki/Rotation_matrix and after a lot of time I coldnot found any solution!!!!

    Thanks
     
  10. Seizure

    Seizure

    Joined:
    Dec 18, 2012
    Posts:
    9
    This Shader works great, however I would like for this same exact thing except for the texture to go around the xaxis rather then the Y axis, how would I go about doing that?
     
  11. Marco-Sperling

    Marco-Sperling

    Joined:
    Mar 5, 2012
    Posts:
    620
    $2d_rotation_around_x_axis.jpg

    You want this? Are you sure?
     
  12. ectogama

    ectogama

    Joined:
    Aug 27, 2013
    Posts:
    6
    Shader "Custom/RotateUVs" {

    Properties {

    _MainTex ("Base (RGB)", 2D) = "white" {}

    _RotationSpeed ("Rotation Speed", Float) = 2.0

    }

    SubShader {

    Tags { "RenderType"="Opaque" }

    LOD 200



    CGPROGRAM

    #pragma surface surf Lambert vertex:vert



    sampler2D _MainTex;



    struct Input {

    float2 uv_MainTex;

    };



    float _RotationSpeed;

    void vert (inout appdata_full v) {

    v.texcoord.xy -=0.5;

    float s = -sin ( _RotationSpeed * _Time );

    float c = cos ( _RotationSpeed * _Time );

    float2x2 rotationMatrix = float2x2( c, -s, s, c);

    rotationMatrix *=0.5;

    rotationMatrix +=0.5;

    rotationMatrix = rotationMatrix * 2-1;

    v.texcoord.xy = mul ( v.texcoord.xy, rotationMatrix );

    v.texcoord.xy += 0.5;

    }



    void surf (Input IN, inout SurfaceOutput o) {

    half4 c = tex2D (_MainTex, IN.uv_MainTex);

    o.Albedo = c.rgb;

    o.Alpha = c.a;

    }

    ENDCG

    }

    FallBack "Diffuse"

    }
     
  13. enviralDesigns

    enviralDesigns

    Joined:
    Jul 11, 2013
    Posts:
    1
    This thread has the closest solution to what I'm looking for and I thank everyone for their contributions!

    I'm stuck trying to incorporate MULTIPLE textures in this shader that all rotate at speeds independent from each other.

    I'm realizing my problem lies with in the vert function, I would need to somehow modify two sets of uv's one for each texture since it's not the texture that's rotating but the uv's correct? I've tried creating a new uvset in struct Input but I wasn't able to get that to work.

    Any ideas? I would be for ever grateful!
     
  14. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Thats correct, simply repeat the cos and sin calculation, and apply it to a different uv
     
  15. ciruelica

    ciruelica

    Joined:
    Apr 3, 2013
    Posts:
    15
    Hi everyone,

    This thread was very useful for me. I used a shader to rotate a texture to show it on my GUI with Graphics.DrawTexture.
    However I don't want the texture to rotate forever, but just a certain amount of degrees on demand. I know I can access the shader properties to modify, for example, a variable let's say "RotateDegrees". But, which code should I write on the Shader? Anyone?
    Thanks a lot
     
  16. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Code (csharp):
    1.  
    2.  
    3.                 float s = -sin ( _RotationSpeed * _Time );
    4.  
    5.                 float c = cos ( _RotationSpeed * _Time );
    6.  
     
    Last edited: Apr 15, 2015
  17. ciruelica

    ciruelica

    Joined:
    Apr 3, 2013
    Posts:
    15
    Thank you rea,
    sorry but I am comlpetely new with shaders. What do you mean by "B"? The degrees I want to rotate? and "/B"?
     
  18. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Those are forum tags for bold. So those are just to make the "-sin" bold.

    To not make it time dependent in the shader, just replace _RotationSpeed * _Time with _RotateDegrees for example and make _RotateDegrees your input property.
     
  19. ciruelica

    ciruelica

    Joined:
    Apr 3, 2013
    Posts:
    15
    Thanks a lot jvo3dc!! I got it!!
     
  20. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Woops sorrry bout that, didn't realize about the bold tag :p
     
  21. ciruelica

    ciruelica

    Joined:
    Apr 3, 2013
    Posts:
    15
    As I said I found out how to rotate. My shader works perfect. But now I need to modify the contrast. I found a shader which modifies image properties but it is fragment/vertex type. My rotation shader is surface type. I was unable to mix them and then I read they are not compatibles. Does anyone have an idea how to convert one in to the other type or how to make them work at the same time? I want to rotate and I want to modify the contrast.

    Or maybe is there a simple way to change the contrast from the first shader??

    Code (csharp):
    1. Shader "Custom/NewShader" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.         _RotationSpeed ("Rotation Speed", Float) = 2.0
    5.         _RotationDegrees ("Rotation Degrees", Float) = 0.0
    6.        
    7.     }
    8.     SubShader {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 200
    11.        
    12.         CGPROGRAM
    13.         #pragma surface surf Lambert vertex:vert
    14.  
    15.         sampler2D _MainTex;
    16.  
    17.         struct Input {
    18.             float2 uv_MainTex;
    19.         };
    20.        
    21.         float _RotationDegrees;
    22.  
    23.         void vert (inout appdata_full v) {
    24.             v.texcoord.xy -=0.5;
    25.            
    26.             float s = sin ( _RotationDegrees);
    27.             float c = cos ( _RotationDegrees);
    28.            
    29.             float2x2 rotationMatrix = float2x2( c, -s, s, c);
    30.             rotationMatrix *=0.5;
    31.             rotationMatrix +=0.5;
    32.             rotationMatrix = rotationMatrix * 2-1;
    33.             v.texcoord.xy = mul ( v.texcoord.xy, rotationMatrix );
    34.             v.texcoord.xy += 0.5;
    35.         }
    36.  
    37.         void surf (Input IN, inout SurfaceOutput o) {
    38.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    39.             o.Albedo = c.rgb;
    40.             o.Alpha = c.a;
    41.         }
    42.         ENDCG
    43.     }
    44.     FallBack "Diffuse"
    45. }
    Code (csharp):
    1. Shader "Custom/Effects" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.         _SaturationAmount ("Saturation Amount", Range(0.0, 1.0)) = 1.0
    5.         _BrightnessAmount ("Brightness Amount", Range(0.0, 1.0)) = 1.0
    6.         _ContrastAmount ("Contrast Amount", Range(0.0,1.0)) = 1.0
    7.     }
    8.     SubShader {
    9.         Pass {
    10.        
    11.             CGPROGRAM
    12.             #pragma vertex vert_img
    13.             #pragma fragment frag
    14.             #pragma fragmentoption ARB_precision_hint_fastest
    15.             #include "UnityCG.cginc"
    16.            
    17.             uniform sampler2D _MainTex;
    18.             uniform float _SaturationAmount;
    19.             uniform float _BrightnessAmount;
    20.             uniform float _ContrastAmount;
    21.            
    22.             float3 ContrastSaturationBrightness( float3 color, float brt, float sat, float con)
    23.             {
    24.                 //RGB Color Channels
    25.                 float AvgLumR = 0.5;
    26.                 float AvgLumG = 0.5;
    27.                 float AvgLumB = 0.5;
    28.                
    29.                 //Luminace Coefficients for brightness of image
    30.                 float3 LuminaceCoeff = float3(0.2125,0.7154,0.0721);
    31.                
    32.                 //Brigntess calculations
    33.                 float3 AvgLumin = float3(AvgLumR,AvgLumG,AvgLumB);
    34.                 float3 brtColor = color * brt;
    35.                 float intensityf = dot(brtColor, LuminaceCoeff);
    36.                 float3 intensity = float3(intensityf, intensityf, intensityf);
    37.                
    38.                 //Saturation calculation
    39.                 float3 satColor = lerp(intensity, brtColor, sat);
    40.                
    41.                 //Contrast calculations
    42.                 float3 conColor = lerp(AvgLumin, satColor, con);
    43.                
    44.                 return conColor;
    45.             }
    46.            
    47.             float4 frag (v2f_img i) : COLOR
    48.             {
    49.                 float4 renderTex = tex2D(_MainTex, i.uv);
    50.                 renderTex.rgb = ContrastSaturationBrightness(renderTex.rgb, _BrightnessAmount, _SaturationAmount, _ContrastAmount);
    51.                 return renderTex;
    52.             }
    53.             ENDCG
    54.         }
    55.     }
    56.     FallBack "Diffuse"
    57. }
     
    Last edited: Feb 4, 2014
    LostPanda and plockhartt17 like this.
  22. Johnscomeling

    Johnscomeling

    Joined:
    Feb 19, 2013
    Posts:
    1
    How would i make a shader that rotated a image rotate a transparent image
     
    ReemOnspec2020 likes this.
  23. alienheretic

    alienheretic

    Joined:
    Oct 15, 2008
    Posts:
    60
    how to get the rotation angle based on the objects rotation inside the shader itself?
     
  24. alienheretic

    alienheretic

    Joined:
    Oct 15, 2008
    Posts:
    60
    ok I figured that out but can it be done with out breaking batching? seems like setting the mesh to static kills the rotation
     
  25. mrgrimmig

    mrgrimmig

    Joined:
    Jan 11, 2013
    Posts:
    4
    Hey,
    i also need a shader to rotate a texture on a simple plane.
    While using a non squared plane i get some shearing effects.
    shearing.png
    How can i avoid this effect?
    I'm using this shader:

    Code (CSharp):
    1. Shader "Custom/RotateUVs" {
    2.     Properties {
    3.         _MainTex("Base (RGB)", 2D) = "white" {}
    4.         _Rotation("Rotation", Float) = 0.0
    5.     }
    6.  
    7.     SubShader {
    8.         Pass {
    9.             CGPROGRAM
    10.             #pragma vertex vert
    11.             #pragma fragment frag
    12.  
    13.             sampler2D _MainTex;
    14.             fixed4 _MainTex_ST;
    15.             float _Rotation;
    16.             float _OffsetX;
    17.             float _OffsetY;
    18.  
    19.             struct appdata {
    20.                 float4 vertex : POSITION;
    21.                 float4 texcoord : TEXCOORD0;
    22.             };
    23.  
    24.             struct v2f {
    25.                 float4 pos : SV_POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.             };
    28.    
    29.             v2f vert (appdata v) {
    30.                 float s = sin ( _Rotation );
    31.                 float c = cos ( _Rotation );
    32.                 float2x2 rotationMatrix = float2x2( c, -s, s, c);
    33.  
    34.                 v2f o;
    35.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    36.  
    37.                 float offsetX = .5; //_MainTex_ST.z +_MainTex_ST.x / 2;
    38.                 float offsetY = .5; //_MainTex_ST.w +_MainTex_ST.y / 2;
    39.  
    40.                 float x = v.texcoord.x - offsetX; //* _MainTex_ST.x + _MainTex_ST.z - offsetX;
    41.                 float y = v.texcoord.y - offsetY; //* _MainTex_ST.y + _MainTex_ST.w - offsetY;
    42.  
    43.                 o.uv = mul (float2(x, y), rotationMatrix ) + float2(offsetX, offsetY);
    44.                
    45.                 return o;
    46.             }
    47.    
    48.             fixed4 frag (v2f i) : COLOR {
    49.                 return tex2D(_MainTex, i.uv);
    50.             }
    51.             ENDCG
    52.         }
    53.     }
    54. }
    Hope someone can help me.
     
  26. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    You should use a mesh with a isotropic UV layout. My guess is you're scaling the built-in quad non-uniformly, making its UVs anisotropic. The shader code doesn't have enough information to correct for your scaling unless you get really fancy.
     
  27. mrgrimmig

    mrgrimmig

    Joined:
    Jan 11, 2013
    Posts:
    4
    Many thanks, Daniel.
    Now i build the plane mesh per script.
    The uvs get the same aspect ratio as the mesh and i can leave the scaling of the gameobject at (1,1,1).
     
  28. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Now that I think about it, you could do the rotation when you build the mesh too, unless you want that to change more easily than each plane's dimensions.
     
  29. mrgrimmig

    mrgrimmig

    Joined:
    Jan 11, 2013
    Posts:
    4
    Oh, still have a problem.
    The texture i want to display on the plane can also have another aspect ratio...
    For example:
    The plane has the size of 2.5 * 2.5 units
    The texture's dimensions are 512 * 256 px
    I have to set the tiling to 0.25 * 0.5 for displaying one square at the plane (see picture above).

    With a squared texture and a non squared plane it works, after "resizing" the uvs
    (range of u = 0 - 1; range of v = 0.25 - 0.75).

    Maybe i could implement a solution with projectors, but it would be nicer, if i can use a shader.
     
  30. FranckS

    FranckS

    Joined:
    Jan 14, 2014
    Posts:
    29
    It would be much more efficient to compute the rotation matrix in a script in order to avoid the costly sincos() at each vert, and pass it as a float4 attribute to the shader, as it is constant across all verts (constant within a frame).
     
  31. mrgrimmig

    mrgrimmig

    Joined:
    Jan 11, 2013
    Posts:
    4
    Actually you're right ;)
    But the shearing would still exist
     
  32. Mikhail007

    Mikhail007

    Joined:
    Sep 11, 2015
    Posts:
    7
    Hi everyone,

    Changed shader to fix rotate 90 degrees.

    Shader "Custom/Rotate90s" {
    Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _RotationSpeed ("Rotation Speed", Float) = 2.0
    _RotationDegrees ("Rotation Degrees", Float) = 0.0
    _isRotat ("isRotat", int) = 0

    }
    SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200

    CGPROGRAM
    #pragma surface surf Lambert vertex:vert
    sampler2D _MainTex;
    struct Input {
    float2 uv_MainTex;
    };

    float _RotationDegrees;
    int _isRotat;
    void vert (inout appdata_full v) {
    v.texcoord.xy -=0.5;
    if (_isRotat==1) { _RotationDegrees=4.712385; } // 90 degrees.
    float s = sin ( _RotationDegrees);
    float c = cos ( _RotationDegrees);

    float2x2 rotationMatrix = float2x2( c, -s, s, c);
    rotationMatrix *=0.5;
    rotationMatrix +=0.5;
    rotationMatrix = rotationMatrix * 2-1;
    v.texcoord.xy = mul ( v.texcoord.xy, rotationMatrix );
    v.texcoord.xy += 0.5;
    }
    void surf (Input IN, inout SurfaceOutput o) {
    half4 c = tex2D (_MainTex, IN.uv_MainTex);
    o.Albedo = c.rgb;
    o.Alpha = c.a;
    }
    ENDCG
    }
    FallBack "Diffuse"
    }

    This Shader (Custom/Rotate90s) not working in android 5.1, but Shader (Custom/RotateUV) works. In android version 4.1 - 4.4. work both. What could be wrong?
     
  33. Xorxor

    Xorxor

    Joined:
    Oct 15, 2014
    Posts:
    24
    Note that you can remove the redundant scaling and substitute the sincos intrinsic.

    i.texcoord0.xy -= 0.5; // shift the center of the coordinates to (0,0)
    float s, c;
    sincos(radians(_Rotation), s, c); // compute the sin and cosine
    float2x2 rotationMatrix = float2x2(c, -s, s, c);
    i.texcoord0.xy = mul(i.texcoord0.xy, rotationMatrix);
    i.texcoord0.xy += 0.5; // shift the center of the coordinates back to (0.5,0.5)
     
    Alekxss and yaroslavpl like this.
  34. David-Lindsay

    David-Lindsay

    Joined:
    May 20, 2009
    Posts:
    121
    Just want to note that Metal API does not support this type of matrix contruction.

    For Metal, you should write (which I found rather annoying, haha)

    Matrix = float2x2( float2(c, -s), float2(s, c));
    rotationMatrix *= float2x2(float2(0.5,0.5),float2(0.5,0.5));
    rotationMatrix += float2x2(float2(0.5,0.5),float2(0.5,0.5));
    rotationMatrix = rotationMatrix * float2x2(float2(2,2),float2(2,2)) -float2x2(float2(1,1),float2(1,1));

    If you don't it makes a compile time error (material is flat black on the mobile device);

    EDIT: this won't affect other platforms -they will all work just fine too
     
  35. stationx

    stationx

    Joined:
    Jul 9, 2012
    Posts:
    251
    How does all this work on normals?
     
  36. David-Lindsay

    David-Lindsay

    Joined:
    May 20, 2009
    Posts:
    121
    If you are talking about per pixel normals (a normal map texture), then, well, you can rotate that for sure.

    If you are talking about the per vertex mesh normals, then rotate the object in the scene.

    Note (a point of confusion to some) that normal textures are only a per-pixel direction modifier that still requires the normal direction of a mesh (or sometimes hardcoded direction).
     
  37. stationx

    stationx

    Joined:
    Jul 9, 2012
    Posts:
    251
    Yup, i was talking about normal map textures. Problem with rotating is that at some point the normals are flipped and the light info is mirrored!
     
  38. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    If you rotate the texture, or more specifically rotate the UVs, you have to apply the inverse rotation to the resulting normal map's tangent space x and y.

    Normal maps store a direction relative to the tangent space of the model, but that tangent is based on the orientation of the UVs and the surface normal. If you generate or alter the UVs in a shader you'll also have to generate or alter your tangents for that normal map or transform your normal map back to match those on the model. If you're just rotating then the inverse rotation is the easiest way to accomplish that.
     
  39. stationx

    stationx

    Joined:
    Jul 9, 2012
    Posts:
    251
    Do you mind showing me how to alter the tangents?
     
  40. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    @stationx - If you're asking related to triplanar, that's a more involved question. That's not a case of "rotating the texture" and altering the existing tangents, that's creating all new tangents from scratch since the UVs you use have no relation to those stored in the mesh. That's a discussion better taken someplace else (ie: your triplanar thread).

    If it's actually being rotated like the code on this page then you'll want something like this (based off of @Xorxor 's example):

    i.texcoord0.xy -= 0.5; // shift the center of the coordinates to (0,0)
    float s, c;
    sincos(radians(_Rotation), s, c); // compute the sin and cosine
    float2x2 rotationMatrix = float2x2(c, -s, s, c);
    i.texcoord0.xy = mul(i.texcoord0.xy, rotationMatrix);
    i.texcoord0.xy += 0.5; // shift the center of the coordinates back to (0.5,0.5)

    fixed4 normal = UnpackNormal(tex2D(_BumpMap, i.texcoord0.xy)); // get normal
    normal = mul(normal, inverse(rotationMatrix)); // rotate using inverse of rotation matrix


    It's important that the rotation matrix does not have the offset in it!

    I'd also contend the 90 degree rotation example is a lot of wasted math if all you want is a 90 degree rotation.

    IN.uv_MainTex.yx would have the same result for zero cost.
     
    Reanimate_L likes this.
  41. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    Hmmm, how could this code be modified further to take the offsets and tiling into account as well while still rotating it around its center? I have tried several things, but none of them seem to work right to keep the texture perfectly centered and rotating when these values are changed.

    Code (CSharp):
    1.  
    2. //This shader can be used to create overlays that use already colored gradients (that cam also be given a glow color) and uses additive LIT blending
    3. Shader "SOTN Custom/gradientOverlaySpinColorAddLit"
    4. {
    5. Properties {
    6.      _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    7.      _AlphaMap ("Gradient Transparency Map", 2D) = "white" {}
    8.         _GlowColor ("Glow Color", Color ) = ( 1.0, 1.0, 1.0, 1.0 )
    9.         _ScrollXSpeed("X Scroll Speed", Float) = 2
    10.         _ScrollYSpeed("Y Scroll Speed", Float) = 2
    11.         _RotationSpeed ("Rotation Speed", Float) = 2.0
    12.         _PivotPointX ("Pivot Point X", Range(0, 1)) = 0.5
    13.         _PivotPointY ("Pivot Point Y", Range(0, 1)) = 0.5
    14. }
    15. SubShader {
    16.     Tags {"IgnoreProjector"="True"}
    17.       blend SrcAlpha One
    18.       Cull off
    19.  
    20. CGPROGRAM
    21. #pragma surface surf Standard vertex:vert keepalpha
    22. sampler2D _MainTex;
    23. sampler2D _AlphaMap;
    24. float4 _Color;
    25. fixed4    _GlowColor;
    26. fixed _ScrollXSpeed;
    27. fixed _ScrollYSpeed;
    28. float _RotationSpeed;
    29. float _PivotPointX;
    30. float _PivotPointY;
    31.  
    32. struct Input {
    33.      float2 uv_MainTex;
    34.      float2 uv2_AlphaMap;
    35. };
    36.  
    37. //rotation code starts here
    38. void vert (inout appdata_full v) {
    39.  
    40.     float s = sin ( _RotationSpeed * _Time );
    41.     float c = cos ( _RotationSpeed * _Time );
    42.  
    43.     float2x2 rotationMatrix = float2x2( c, -s, s, c);
    44.     //NOTE: to get the 0.5 that USED to be below (which makes it rotate around the middle) the calculation is: (tiling + offset*2)/2
    45.     v.texcoord1.xy = mul ( v.texcoord1.xy - float2(_PivotPointX, _PivotPointY), rotationMatrix ) + float2(_PivotPointX, _PivotPointY);
    46. }
    47. //rotation code ends here
    48.            
    49. void surf (Input IN, inout SurfaceOutputStandard o) {
    50.  
    51.     fixed2 scrolledUV = IN.uv2_AlphaMap;
    52.     fixed xScrollValue = _ScrollXSpeed*_Time;
    53.     fixed yScrollValue = _ScrollYSpeed*_Time;
    54.  
    55.     scrolledUV += fixed2(xScrollValue,yScrollValue);
    56.    
    57.      half4 c = tex2D(_MainTex, IN.uv_MainTex) * _GlowColor.a;
    58.      o.Albedo =  tex2D(_AlphaMap, scrolledUV).rgb * _GlowColor.rgb;
    59.      if (c.a<0.01) discard;
    60.      o.Alpha = c.a * tex2D(_AlphaMap, scrolledUV).a;
    61.    
    62. }
    63. ENDCG
    64. }
    65. }
    66.  
     
    Last edited: Aug 13, 2016
  42. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    You're doing everything right, but it's highly dependant on the base UVs; you need to have your mesh's secondary UVs' (0.5, 0.5) at the position you want to rotate around. For any built in mesh Unity has lightmap UVs stored in "uv2" (texcoord1) so (0.5, 0.5) is probably some random face's edge and no where near the center of the other faces. However uv "1" is usually nicely centered.

    So, for something like a surface shader you probably want to do two things; copy texcoord"0" into the uvs for your secondary texture, and use uv3 instead of uv2 since nothing using uv3.

    Code (CSharp):
    1. Shader "Hidden/gradientOverlayColoredAddLit"
    2. {
    3. Properties {
    4.      _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    5.      _AlphaMap ("Gradient Transparency Map", 2D) = "white" {}
    6.         _GlowColor ("Glow Color", Color ) = ( 1.0, 1.0, 1.0, 1.0 )
    7.         _ScrollXSpeed("X Scroll Speed", Float) = 2
    8.         _ScrollYSpeed("Y Scroll Speed", Float) = 2
    9.         _RotationSpeed ("Rotation Speed", Float) = 2.0
    10. }
    11.  
    12. SubShader {
    13.     Tags {"IgnoreProjector"="True"}
    14.        blend SrcAlpha One
    15.        Cull off
    16.  
    17. CGPROGRAM
    18. #pragma surface surf Standard vertex:vert keepalpha
    19.  
    20. sampler2D _MainTex;
    21. sampler2D _AlphaMap;
    22. float4 _Color;
    23. fixed4    _GlowColor;
    24. fixed _ScrollXSpeed;
    25. fixed _ScrollYSpeed;
    26. float _RotationSpeed;
    27.  
    28. struct Input {
    29.      float2 uv_MainTex;
    30.      float2 uv3_AlphaMap; // using uv3 aka texcoord2
    31. };
    32.  
    33.  
    34. //rotation code begins here; rotates the alphamap
    35. void vert (inout appdata_full v) {
    36.     float s = sin ( _RotationSpeed * _Time );
    37.     float c = cos ( _RotationSpeed * _Time );
    38.  
    39.     float2x2 rotationMatrix = float2x2( c, -s, s, c);
    40.  
    41.     v.texcoord2.xy = mul ( v.texcoord.xy - 0.5, rotationMatrix ) + 0.5; // grab texcoord and overwrite texcoord2 instead of using texcoord1 at all
    42. }
    43. //rotation code ends here        
    44.  
    45. void surf (Input IN, inout SurfaceOutputStandard o) {
    46.  
    47.      fixed2 scrolledUV = IN.uv3_AlphaMap;
    48.      fixed xScrollValue = frac(_ScrollXSpeed*_Time.y);
    49.      fixed yScrollValue = frac(_ScrollYSpeed*_Time.y);
    50.  
    51.      scrolledUV += fixed2(xScrollValue,yScrollValue);
    52.  
    53.      half4 c = tex2D(_MainTex, IN.uv_MainTex) * _GlowColor.a;
    54.      o.Albedo =  tex2D(_AlphaMap, scrolledUV).rgb;
    55.      if (c.a<0.01) discard;
    56.      o.Alpha = c.a * tex2D(_AlphaMap, scrolledUV).a;
    57.  
    58. }
    59. ENDCG
    60. }
    61. }
    Also used frac() on the scrolling value. You never want to use _Time values straight as your UVs will eventually get to really, really large values and the texture will start to look pixelated as you run out of float precision. Also used _Time.y as that's actual time, _Time.x is time / 20.
     
    fergamboa_ and esco1979 like this.
  43. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    Thanks for the tips, I can definitely use all the good ones I can get as I learn these. :) But I was actually asking about how to include the x/y tiling and x/y offset values found on the material in the calculation for making sure the pivot point stays in the center of the texture IMAGE on screen. Since unless x/y tiling = 1 and x/y offset = 0, just using a flat 0.5 won't always keep it centered when rotated. I want to do this because due to how the gradient on my texture is non repeating, when rotated the outer edges looked messed up. So I need to adjust the tiling and offset values some, but then using the code as it was, it no longer rotated directly in its center. If tiling < 1, the center is clearly still the middle of the texture, but if let's say tiling for x was 3 then the center would be right smack in the middle of the 2nd texture assuming the xoffset was 0. Of course then changing that too moves it again

    I already modified the script so that the user can now put in their own pivot points of choice which helped me find the values I needed with the tiling and offsets changed. But I'm still curious as to how I can get these values from within a shader and then return the value out for the center pivot point of the texture image on screen when tiling and offsetting is accounted for, just for the sake of learning it. From what I read, I need to use _AlphaMap_ST to get the tiling (xy) and the offset (zw). But I tried several things and they all returned errors so I'm getting my syntax wrong with it somehow.
     
  44. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    So you want to be able to have a tiling texture and rotate each tile from the center?

    For that you'll need to do the rotation in the fragment shader. Remove the rotation from the vert function (maybe even remove the vert function entirely).

    Then in the pixel shader:
    float2 uv = frac(IN.uv_AlphaTex);
    float s = sin ( _RotationSpeed * _Time );
    float c = cos ( _RotationSpeed * _Time );
    float2x2 rotationMatrix = float2x2( c, -s, s, c);
    uv.xy = mul ( uv.xy - 0.5, rotationMatrix ) + 0.5;

    If that's not what you want I'm not sure I understand what you're trying to do.
     
    esco1979 likes this.
  45. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    Lol no, no that's not what I meant. For that, I could just use several objects copied and pasted. I appreciate the attempt though definitely. :D

    It's not really an issue at this point. I had to tile my texture x and y wise at 0.7, and then I had to offset it by 0.15 x and y wise to make sure the texture fully covered the sprite, but without repeating. Then when I rotated it the edges didn't look all messed up because the texture doesn't repeat. I was just curious how to do it programmatically, but since I got the values I need and it works perfect rotating, no point sweating it.

    BTW my main 2D shader at this point, can do scrolling gradient overlays, rotating ones that generally use a radian type of gradient overlay, a mask/clipping effect and I've made a version for lit, unlit, additive and lit, and darken (subtractive?). I even combined it with a palette shader that can do color cycling. I made a few minor tweaks to some other minor shaders to practice too, nothing major, but I'm not doing too bad for a beginner. I'm about to mess with a wave distortion one and see if there a simpler way to do the sine wave distortion. Since the one I have is somewhat.... limited.

    My point is I'm definitely learning and it's thanks to people like you who take the time to help. It's appreciated.
     
  46. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Ah, I get it.

    If you want to scale without having to reposition you need to apply the scale before doing the final "+ 0.5" of the mul line. With a surface shader you could do this by not adding the + 0.5 in the vert function and doing it later in the surf function.
    If you want to move where the "center pivot" is, that depends if you want to change the position on surface or within the texture, one requires adding the offset before the mul() at the same time as the -0.5, and the other after the mul but before you scale and + 0.5.

    You're direction looking at _AlphaTex_ST is the right one, but surface shaders automatically apply that to the uvs as part of the "uv_AlphaTex" input definition. If you want to scale and offset the texture center then the kind of ugly way mentioned above of doing the + 0.5 in the surface function is all you need.

    If you really want to do it manually it looks like this:

    Code (csharp):
    1. float4 _AlphaMap_ST;
    2.  
    3. struct Input {
    4.      float2 uv_MainTex;
    5.      float2 texcoord_AlphaMap; // don't use special case "uv_"
    6. };
    7.  
    8. // includes "out Input o" to fill in custom data to the surface function Input
    9. void vert (inout appdata_full v, out Input o) {
    10.     UNITY_INITIALIZE_OUTPUT(Input,o);
    11.     float s = sin ( _RotationSpeed * _Time );
    12.     float c = cos ( _RotationSpeed * _Time );
    13.  
    14.     float2x2 rotationMatrix = float2x2( c, -s, s, c);
    15.  
    16.     o.texcoord_AlphaMap = TRANSFORM_TEX(mul ( v.texcoord.xy - 0.5, rotationMatrix ), _AlphaTex) + 0.5;
    17. }
     
    fergamboa_ and esco1979 like this.
  47. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    Excellent information! Thanks so much for your help bgolus. :D
     
  48. shekhar331

    shekhar331

    Joined:
    Nov 11, 2014
    Posts:
    10
    Adding alpha to the texture gives issue.

    Code (CSharp):
    1.  Shader "Custom/RotateUVs" {
    2.         Properties {
    3.             _MainTex ("Base (RGB)", 2D) = "white" {}
    4.             _Color ("Alpha", Color) = (1,1,1,1)
    5.             _RotationSpeed ("Rotation Speed", Float) = 2.0
    6.         }
    7.         SubShader {
    8.             Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
    9.             LOD 200
    10.          
    11.             CGPROGRAM
    12.             #pragma surface surf Lambert vertex:vert
    13.    
    14.             sampler2D _MainTex;
    15.             float4 _Color;
    16.    
    17.             struct Input {
    18.                 float2 uv_MainTex;
    19.             };
    20.    
    21.             float _RotationSpeed;
    22.  
    23.             void vert (inout appdata_full v) {
    24.                 v.texcoord.xy -=0.5;
    25.                 float s = sin ( _RotationSpeed * _Time );
    26.                 float c = cos ( _RotationSpeed * _Time );
    27.                 float2x2 rotationMatrix = float2x2( c, -s, s, c);
    28.                 rotationMatrix *=0.5;
    29.                 rotationMatrix +=0.5;
    30.                 rotationMatrix = rotationMatrix * 2-1;
    31.                 v.texcoord.xy = mul ( v.texcoord.xy, rotationMatrix );
    32.                 v.texcoord.xy += 0.5;
    33.             }
    34.    
    35.             void surf (Input IN, inout SurfaceOutput o) {
    36.                 half4 c = tex2D (_MainTex, IN.uv_MainTex);
    37.                 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 1;
    38.                 o.Albedo *=  _Color.rgb;
    39.                 o.Alpha = _Color.a;
    40.             }
    41.             ENDCG
    42.         }
    43.         FallBack "Diffuse"
    44.     }
     
  49. waverz

    waverz

    Joined:
    May 23, 2017
    Posts:
    14
    @shekhar331
    That shader code is really good but I need to add scroll Speed for x,y I tried but it gives me many errors I can't do it?
     
  50. Handman50

    Handman50

    Joined:
    Feb 12, 2018
    Posts:
    2
    How would you go about making this an Unlit shader? I managed to make it transparent, but it's not exactly what I want.
     
    L0tan likes this.