Search Unity

[SOLVED] Surface Shader: Rotating texture (and Normal map) is working but lighting response is not.

Discussion in 'Shaders' started by FrogSkull, Jan 20, 2017.

  1. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Hello guys.
    Look, im using this shader to rotate a simple texture with a normal map.
    Everything is working just fine.
    The only problem is: the response of light is not following the rotation of the normal map.
    The light behaviour only responds if the Object is rotated, but not the texture.
    How can i make this work? How can i make the light and shadow inside surface Shader texture behave according to the its rotation (the texture rotation)?
    Thanks.

    amostra.jpg

    Code (CSharp):
    1. Shader "Custom/SimpleRotateUV" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _BumpMap ("Bumpmap", 2D) = "bump" {}
    6.          _RotationSpeed ("RotationSpeed", Float) = 2.0
    7.     }
    8.     SubShader {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 200
    11.    
    12.         CGPROGRAM
    13.  
    14.         #pragma surface surf Standard fullforwardshadows vertex:vert
    15.         #pragma target 3.0
    16.  
    17.         sampler2D _MainTex;
    18.         sampler2D _BumpMap;
    19.  
    20.         struct Input {
    21.             float2 uv_MainTex;
    22.             float2 uv_BumpMap;
    23.             float2 val;
    24.         };
    25.  
    26.         float _RotationSpeed;
    27.         void vert (inout appdata_full v, out Input o) {
    28.             UNITY_INITIALIZE_OUTPUT(Input,o);
    29.  
    30.             float sinX = sin ( _RotationSpeed * _Time );
    31.             float cosX = cos ( _RotationSpeed * _Time );
    32.             float sinY = sin ( _RotationSpeed * _Time );
    33.             float2x2 rotationMatrix = float2x2( cosX, -sinX, sinY, cosX);
    34.             o.val.xy = mul ( v.texcoord.xy, rotationMatrix );
    35.         }
    36.  
    37.         void surf (Input IN, inout SurfaceOutputStandard o) {
    38.             fixed4 c = tex2D (_MainTex, IN.val) * _Color;
    39.             o.Albedo = c.rgb;
    40.             o.Normal = UnpackNormal (tex2D (_BumpMap, IN.val));
    41.         }
    42.         ENDCG
    43.     }
    44.     FallBack "Diffuse"
    45. }
    46.  
     
    Last edited: Jan 20, 2017
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Because normal maps are based off of the model's tangents, which are calculated from the original UVs. If you rotate the UVs after the fact, the normal map no longer lines up to the model's tangents.

    You need to apply the same rotation math to the unpacked normal map's .xy, just with the opposing rotation, to get this to work.

    Code (CSharp):
    1. Shader "Custom/SimpleRotateUV" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _BumpMap ("Bumpmap", 2D) = "bump" {}
    6.          _RotationSpeed ("RotationSpeed", Float) = 2.0
    7.     }
    8.     SubShader {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 200
    11.  
    12.         CGPROGRAM
    13.         #pragma surface surf Standard fullforwardshadows vertex:vert
    14.         #pragma target 3.0
    15.         sampler2D _MainTex;
    16.         sampler2D _BumpMap;
    17.         struct Input {
    18.             float2 uv_MainTex;
    19.             float2 uv_BumpMap;
    20.             float2 val;
    21.         };
    22.         float _RotationSpeed;
    23.         void vert (inout appdata_full v, out Input o) {
    24.             UNITY_INITIALIZE_OUTPUT(Input,o);
    25.             float sinX = sin ( _RotationSpeed * _Time.y );
    26.             float cosX = cos ( _RotationSpeed * _Time.y );
    27.             float2x2 rotationMatrix = float2x2( cosX, -sinX, sinX, cosX);
    28.             o.val.xy = mul ( v.texcoord.xy - 0.5, rotationMatrix ) + 0.5;
    29.         }
    30.  
    31.         fixed4 _Color;
    32.         void surf (Input IN, inout SurfaceOutputStandard o) {
    33.             fixed4 c = tex2D (_MainTex, IN.val) * _Color;
    34.             o.Albedo = c.rgb;
    35.             o.Normal = UnpackNormal (tex2D (_BumpMap, IN.val));
    36.  
    37.             // negative rotation speed
    38.             float sinX = sin ( -_RotationSpeed * _Time.y );
    39.             float cosX = cos ( -_RotationSpeed * _Time.y );
    40.             float2x2 rotationMatrix = float2x2( cosX, -sinX, sinX, cosX);
    41.             o.Normal.xy = mul ( o.Normal.xy, rotationMatrix );
    42.         }
    43.         ENDCG
    44.     }
    45.     FallBack "Diffuse"
    46. }
    47.  
     
    tiggaxxx, Flavelius and FrogSkull like this.
  3. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Perfect! Thanks a lot, bgolus.

    This not only solved my problem but also improved my view on understanding normalmap values.

    The only thing I needed to change in your code suggestion was to delete ".y" from " _Time.y". Is this a typo? Is it there for some reason?


    Anyway, thanks again.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Yes, because _Time by itself is wrong and should never be used. The _Time variable is a float4.
    https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

    The way you're using it is being interpreted as "_RotationSpeed * _Time.x", but some shader compilers (like for mobile) will just give you an error if you try to compile that without specifying a component.
     
    FrogSkull likes this.
  5. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Oh.. sure!
    Ok, understood.
    Thank you for your attention, again.
     
  6. tiggaxxx

    tiggaxxx

    Joined:
    Jan 13, 2020
    Posts:
    30
    Amazing job! Just saved me a lot of trouble, thanks!
    I simplified it a little to be controlled by a fixed angle, i'll leave the code here in case anyone needs it.

    Code (CSharp):
    1. Shader "Custom/SimpleRotateUV" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _BumpMap ("Bumpmap", 2D) = "bump" {}
    6.         _Angle ("Angle", Range(-5.0,  5.0)) = 0.0
    7.     }
    8.     SubShader {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 200
    11.         CGPROGRAM
    12.         #pragma surface surf Standard fullforwardshadows vertex:vert
    13.         #pragma target 3.0
    14.         sampler2D _MainTex;
    15.         sampler2D _BumpMap;
    16.         struct Input {
    17.             float2 uv_MainTex;
    18.             float2 uv_BumpMap;
    19.             float2 val;
    20.         };
    21.         float _RotationSpeed;
    22.         float _Angle;
    23.         void vert (inout appdata_full v, out Input o) {
    24.             UNITY_INITIALIZE_OUTPUT(Input,o);
    25.             float sinX = sin(_Angle);
    26.             float cosX = cos(_Angle);
    27.             float2x2 rotationMatrix = float2x2( cosX, -sinX, sinX, cosX);
    28.             o.val.xy = mul ( v.texcoord.xy - 0.5, rotationMatrix ) + 0.5;
    29.         }
    30.         fixed4 _Color;
    31.         void surf (Input IN, inout SurfaceOutputStandard o) {
    32.             fixed4 c = tex2D (_MainTex, IN.val) * _Color;
    33.             o.Albedo = c.rgb;
    34.             o.Normal = UnpackNormal (tex2D (_BumpMap, IN.val));
    35.         }
    36.         ENDCG
    37.     }
    38.     FallBack "Diffuse"
    39. }
     
    masterqwertyxxx likes this.