Using GetPixels on a normal map returns predominantly red?

I was playing with using GetPixels, and SetPixels to duplicate textures. This was just an experiment, and not being used in a project. My findings were that it did in fact work quite well, ignoring the speed of uploading to the gpu. However, when I attempt this with a normal map, the resulting texture switches from predominantly blue in the inspector to predominantly red. The normal appear correct when applied to an object though.
My question is, why does it appear to be mostly red in that case, and will the normal map still function correctly?

The color channels are centralized normal vectors. There’s some discussion here:

I quote,

Why the bluey-purple colours?
Understanding this is not vital for using normal maps! It’s ok to skip this paragraph. However if you really want to know: The RGB colour values are used to store the X,Y,Z direction of the vector, with Z being “up” (contrary to Unity’s usual convention of using Y as “up”). In addition, the values in the texture are treated as having been halved, with 0.5 added. This allows vectors of all directions to be stored. Therefore to convert an RGB colour to a vector direction, you must multiply by two, then subtract 1. For example, an RGB value of (0.5, 0.5, 1) or #8080FF in hex results in a vector of (0,0,1) which is “up” for the purposes of normal-mapping - and represents no change to the surface of the model. This is the colour you see in the flat areas of the “example” normal map earlier on this page.

What you see displayed in the editor might not even be an accurate RGB-to-RGB display, as it isn’t really relevant.

They pack the normal with a secret sauce, which is why you have a specific unpacknormal function in shader code. It’s either dropping the B and reconstructing it, or using another parametrization like octahedron encoding who only need 2 values.

@neoshaman Thanks for that! TIL… it led me to some more googling which OP might care about:

https://aras-p.info/texts/CompactNormalStorage.html

I however am happy to call it secret sauce and move back to making games. :slight_smile:

Thank you for the replies. This does somewhat clarify the matter.

1 Like

This is explained in some detail here (catlikecoding is a fantastic site) and comes down to data encoding. If you do any shader programming you will have come across UnpackScaleNormal, and looking at the GCINC file you’ll find that indeed

half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
{
    #if defined(UNITY_NO_DXT5nm)
        return packednormal.xyz * 2 - 1;
    #else
        half3 normal;
        normal.xy = (packednormal.wy * 2 - 1);
         #if (SHADER_TARGET >= 30)
             // SM2.0: instruction count limitation
             // SM2.0: normal scaler is not supported
             normal.xy *= bumpScale;
         #endif
         normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
         return normal;
     #endif
}

if you are using DX5 packed normals, they are encoded differently. I guess it’s so sparsely documented because Unity believes that if you mess with normals, you are in the shader domain anyway, and shader coders are used to intense pain.