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

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.

2928851--216516--amostra.jpg

Shader "Custom/SimpleRotateUV" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap ("Bumpmap", 2D) = "bump" {}
         _RotationSpeed ("RotationSpeed", Float) = 2.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
   
        CGPROGRAM

        #pragma surface surf Standard fullforwardshadows vertex:vert
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _BumpMap;

        struct Input {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float2 val;
        };

        float _RotationSpeed;
        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);

            float sinX = sin ( _RotationSpeed * _Time );
            float cosX = cos ( _RotationSpeed * _Time );
            float sinY = sin ( _RotationSpeed * _Time );
            float2x2 rotationMatrix = float2x2( cosX, -sinX, sinY, cosX);
            o.val.xy = mul ( v.texcoord.xy, rotationMatrix );
        }

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.val) * _Color;
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal (tex2D (_BumpMap, IN.val));
        }
        ENDCG
    }
    FallBack "Diffuse"
}

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.

Shader "Custom/SimpleRotateUV" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap ("Bumpmap", 2D) = "bump" {}
         _RotationSpeed ("RotationSpeed", Float) = 2.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
  
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows vertex:vert
        #pragma target 3.0
        sampler2D _MainTex;
        sampler2D _BumpMap;
        struct Input {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float2 val;
        };
        float _RotationSpeed;
        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);
            float sinX = sin ( _RotationSpeed * _Time.y );
            float cosX = cos ( _RotationSpeed * _Time.y );
            float2x2 rotationMatrix = float2x2( cosX, -sinX, sinX, cosX);
            o.val.xy = mul ( v.texcoord.xy - 0.5, rotationMatrix ) + 0.5;
        }

        fixed4 _Color;
        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.val) * _Color;
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal (tex2D (_BumpMap, IN.val));

            // negative rotation speed
            float sinX = sin ( -_RotationSpeed * _Time.y );
            float cosX = cos ( -_RotationSpeed * _Time.y );
            float2x2 rotationMatrix = float2x2( cosX, -sinX, sinX, cosX);
            o.Normal.xy = mul ( o.Normal.xy, rotationMatrix );
        }
        ENDCG
    }
    FallBack "Diffuse"
}
3 Likes

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.

Yes, because _Time by itself is wrong and should never be used. The _Time variable is a float4.

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.

1 Like

Oh… sure!
Ok, understood.
Thank you for your attention, again.

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.

Shader "Custom/SimpleRotateUV" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap ("Bumpmap", 2D) = "bump" {}
        _Angle ("Angle", Range(-5.0,  5.0)) = 0.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows vertex:vert
        #pragma target 3.0
        sampler2D _MainTex;
        sampler2D _BumpMap;
        struct Input {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float2 val;
        };
        float _RotationSpeed;
        float _Angle;
        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);
            float sinX = sin(_Angle);
            float cosX = cos(_Angle);
            float2x2 rotationMatrix = float2x2( cosX, -sinX, sinX, cosX);
            o.val.xy = mul ( v.texcoord.xy - 0.5, rotationMatrix ) + 0.5;
        }
        fixed4 _Color;
        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.val) * _Color;
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal (tex2D (_BumpMap, IN.val));
        }
        ENDCG
    }
    FallBack "Diffuse"
}
1 Like