How do I rotate a normal map?

So I have a normal map that I use on sprite.

The problem I have is that when I rotate the sprite, the shadows rotate with it.

Is there any way I could make the shader to rotate the normal map, so the shadows don’t rotate with the sprite?

Here is the shader (Yes I know the shader is a bit of a mess right now sorry :(, I will polish it once the normal map works)

Shader "Costum/Normal-Map-Sprite"
{
    Properties
    { 
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {} //this is the normal map
    _Color("_Color", Color) = (1,1,1,1)
    _Color1("_ColorLight", Color) = (1,1,1,1)
    _Color2("_ColorDark", Color) = (1,1,1,1)
    }

        SubShader
    {
        Tags
    {
        "Queue" = "Transparent"
        "PreviewType" = "Plane"
        "PreviewType" = "Plane"
        "CanUseSpriteAtlas" = "True"
    }

        Pass
    {
        ZWrite Off
        Cull Off
        Blend SrcAlpha OneMinusSrcAlpha

        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        half4 color : COLOR;
    };

    struct v2f
    {
        float4 vertex : SV_POSITION;
        float2 uv : TEXCOORD0;
        float2 screenuv : TEXCOORD1;
        half4 color : COLOR;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.uv;
        o.screenuv = ((o.vertex.xy / o.vertex.w) + 1) * 0.5;
        o.color = v.color;
        return o;
    }

    float2 safemul(float4x4 M, float4 v)
{
        float2 r;
        r.x = dot(M._m00_m01_m02, v);
        r.y = dot(M._m10_m11_m12, v);

        return r;
    }

    uniform sampler2D _MainTex;
    fixed4 _Color;
    fixed4 _Color1;
    fixed4 _Color2;


    float4 frag(v2f i) : SV_Target
    {
     
        float offset = tex2D(_MainTex, i.uv).y * 2 - 1; // this takes the color from the normal map, and *2 -1 it, so I get a range of -1 to 1.

    float4 c;

    if (offset > 0) {
        c = lerp(_Color, _Color1, offset);
    } else {
        c = lerp(_Color, _Color2, -offset);
    }
        return c;
    }
        ENDCG
    }
    }
}

Also here is the normal map that i’m testing the shader with:

For rotating normal maps you need the mesh’s normal and tangent (and eventually binormal / bitangent).

See Unity’s vertex fragment example page.

So after hours of searching around… I found this

    float2 safemul(float4x4 M, float4 v)
    {
        float2 r;
        r.x = dot(M._m00_m01_m02, v);
        r.y = dot(M._m10_m11_m12, v);

        return r;
    }
float2 offset = safemul(_Object2World, tex2D(_MainTex, i.uv) * 2 - 1).y;

It works as I want it to, but there is 1 problem. and its when I change the Transform.scale.
The script is meant to be used as a refraction, so it also scales with the sprite size and the screen size.

Here is what it looks like in the game.

This sprites have the scale of 3, 1 and 0.5

And here is the sprite with a scale of 50.

So basically its multiplying the offset by the sprite size.

You really should be using Unity’s built in functions for manipulating normals, as shown in the example page I linked to. Just naively rotating two components of the normal map using the object to world matrix will mean having to extract the scale out of the matrix which is very costly.

If you’re looking to use this for refraction what you really want is the screen space normal, not world space (though for 2d stuff they’re often the same for directional vectors).

Try this:
half3 objectNormal = UnpackNormal(tex2d(_Tex, uv)); // assumes you’re using a texture imported as a normal map
half3 viewNormal = normalize(mul(UNITY_MATRIX_IT_MV, objectNormal)); // transform normal from object space to view space

Then use the .xy from viewNormal. Technically this isn’t really correct either, but it’ll kind of work with sprites since the normal map is kind of in object space. If the sprite / mesh gets batched the above will stop working properly.

1 Like

im having this same problem rotating a normal map using shader graph, i’ve tried unpack normal node and rotate about axis Z, but the result is not looking correct (in any of the axis), maybe im missing something? i made it work using a triplanar node like the screen attached, but i cannot make it work for 2D texture.

4773332--454718--normals.jpg

Please post in the Shader Graph forum:
https://forum.unity.com/forums/shader-graph.346/

uh … that, um … that isn’t doing what you think it’s doing. I’m not even sure how that’s producing anything useable.