Search Unity

I cannot find an answer... Texture2D.PackTextures with normal maps.

Discussion in 'Scripting' started by bansheesoft, Oct 26, 2018.

  1. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    I have been searching for hours with no avail.

    I am constructing skinned meshes at runtime. The albedo maps pack properly and adjusted UVS, the meshes attach to my skeleton properly. However the normal maps when I pack them are not in the correct format from what I can tell. I cannot make a DXTnm at runtime for some odd reason (that should be built in imho). I have a function that I thought would work that spits out a rearranged ARGB format.(I found somewhere in the last 6 hours). I am using the standard shader. I really do not want to have to write a specialzed shader and all the extra work in the app it would take. I already have to many of those.

    Rearrangecode:
    Code (CSharp):
    1.  
    2. public static Texture2D NormalMapToUnityFormat(Texture2D aTexture)
    3.     {
    4.         Texture2D normalTexture = new Texture2D(aTexture.width, aTexture.height, TextureFormat.ARGB32, aTexture.mipmapCount > 1);
    5.         Color[] pixels = aTexture.GetPixels(0);
    6.         Color[] nPixels = new Color[pixels.Length];
    7.         for (int y = 0; y < aTexture.height; y++)
    8.         {
    9.             for (int x = 0; x < aTexture.width; x++)
    10.             {
    11.                 Color p = pixels[(y * aTexture.width) + x];
    12.                 Color np = new Color(0, 0, 0, 0);
    13.                 np.r = p.g;
    14.                 np.g = p.g;
    15.                 np.b = p.g;
    16.                 np.a = p.r;
    17.                 nPixels[(y * aTexture.width) + x] = np;
    18.             }
    19.         }
    20.         normalTexture.SetPixels(nPixels, 0);
    21.         normalTexture.Apply(true);
    22.         return normalTexture;
    23.     }
    24.  
    Yet no combination of setting my source textures to DXTnm to ARGB to DXT5 format will Textures2D.PackTextures to a usable format for a normal map. I get grayscale or pink normal maps.
    The UVs are correct. Yet the texture format for the normal maps is not correct. I am running out of hair to pull out ;)

    I have tried:
    - storing the normal maps in ARGB format then packing and converting
    - Storing in DXT5 and packing
    - I had non power of 2 as I was packing and wanted to get the most pixles formy buck in the pack
    - storing nearest power of 2 to debug
    - storing with alpha to pack and retain alpha
    - many many other ways

    Maybe a post process on the packed texture to accomodate Unitys DTXnm format?

    Any help would be great appreciated and the rest of my follicles would also appreciate it.

    Thanks!
     
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    np.r = p.g;
    np.g = p.g;
    np.b = p.g;
    Thats why the are greyscale
     
  3. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    How did I miss that..... must be working till 2am.....off to fix that thanks!
    sheesh. Sometimes you really just need to goto bed and then get a fresh look in the morning!
     
  4. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Well that did not fix it sadly... although it was wrong lol.

    Ok I fixed that to match what I found here.
    https://forum.unity.com/threads/runtime-generated-bump-maps-are-not-marked-as-normal-maps.413778/

    They state the DXTnm format shifts some things around and also blanks out some channels (I may find useful later in a shader)
    This is bgolus's format he posted: (Thanks sir)

    rgbvsdxtnm.jpg

    Here is the fixed code that attempts this solution:
    Code (CSharp):
    1.     public static Texture2D NormalMapToUnityFormat(Texture2D aTexture)
    2.     {
    3.         Texture2D normalTexture = new Texture2D(aTexture.width, aTexture.height, TextureFormat.ARGB32, aTexture.mipmapCount > 1);
    4.         Color[] pixels  = aTexture.GetPixels(0);
    5.         Color[] nPixels = new Color[pixels.Length];
    6.         for (int y = 0; y < aTexture.height; y++)
    7.         {
    8.             for (int x = 0; x < aTexture.width; x++)
    9.             {
    10.                 Color p = pixels[(y * aTexture.width) + x];
    11.                 Color np = new Color(0, 0, 0, 0);
    12.                 np.r = 0.0f;
    13.                 np.g = p.g;
    14.                 np.b = 0.0f;
    15.                 np.a = p.r;
    16.                 nPixels[(y * aTexture.width) + x] = np;
    17.                 //normalTexture.SetPixel(x,y,np);
    18.             }
    19.         }
    20.         normalTexture.SetPixels(nPixels, 0);
    21.         normalTexture.Apply(true);
    22.         return normalTexture;
    23.     }
    24.  
    25. Later I set it:
    26.         combinedMat.SetTexture("_BumpMap", skinnedMeshAtlasNRM);
    27.  
    28.  
    29.  
    And here are the results
    Original blue normal map and this result of course from code above:

    show.jpg


    Shader:
    shader.jpg

    The UVs have been packed.
    And that could be the issue with the normal map except it should be using the same main texture UVcoords. Also the packed textures both are in the exact same texel locations.

    The new normal map is in ARGB format. That was said to work with the standard shader also as the colors are in the same locations.

    I am baffled any help is greatly appreciated I am just spinning tires it seems.

    If only I could use the "FIX" code the editor uses to fix the normal maps to Unity's format.

    Thanks! Back to pulling hair.....

    I am going to try one more thing later today and not create a new material but duplicate an existing one.. possibly a flag is not set in the new material.
     

    Attached Files:

    • nmg.jpg
      nmg.jpg
      File size:
      39.6 KB
      Views:
      776
  5. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
  6. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    That information about DXTnm doesnt seem to be quite accurate

    Looks to me like RGBA is remapped to 1GGR, not 0G0R

    Heres the channels decomposed and drawn for the same image marked as 'default' and 'normal map'
    (rgba, R, G, B, A) :


    Heres the code to see this for yourself:

    Code (CSharp):
    1.  
    2.     public Texture2D normalMap;
    3.     Texture2D normR, normG, normB, normA;
    4.  
    5.     public Texture2D rgbMap;
    6.     Texture2D rgbR, rgbG, rgbB, rgbA;
    7.  
    8.     bool colorisedChannels = false; //Change to true if you want to colorise the channels
    9.  
    10.     private void Start()
    11.     {
    12.         Color32[] sourceCols = new Color32[normalMap.width * normalMap.height];
    13.         Color32[] channelCols = new Color32[normalMap.width * normalMap.height];
    14.  
    15.         byte zero = 0; //There is no 0b literal for bytes
    16.  
    17.         normR = new Texture2D(normalMap.width, normalMap.height);
    18.         sourceCols = normalMap.GetPixels32(0);
    19.  
    20.         int count = 0;
    21.         int total = 0;
    22.         for (int i = 0; i < sourceCols.Length; i++)
    23.         {
    24.             if (sourceCols[i].g != sourceCols[i].b)
    25.             {
    26.                 count++;
    27.                 total += Mathf.Abs(sourceCols[i].g - sourceCols[i].b);
    28.             }
    29.         }
    30.      
    31.         Debug.Log(count + " out of " + sourceCols.Length + " are different, total difference " + total);
    32.  
    33.  
    34.  
    35.         for (int i = 0; i < sourceCols.Length; i++)
    36.         {
    37.             channelCols[i] = new Color32(sourceCols[i].r, (colorisedChannels ? zero : sourceCols[i].r), (colorisedChannels ? zero : sourceCols[i].r), 255);
    38.         }
    39.         normR.SetPixels32(channelCols);
    40.         normR.Apply();
    41.  
    42.         normG = new Texture2D(normalMap.width, normalMap.height);
    43.         for (int i = 0; i < sourceCols.Length; i++)
    44.         {
    45.             channelCols[i] = new Color32((colorisedChannels ? zero : sourceCols[i].g), sourceCols[i].g, (colorisedChannels ? zero : sourceCols[i].g), 255);
    46.         }
    47.         normG.SetPixels32(channelCols);
    48.         normG.Apply();
    49.  
    50.         normB = new Texture2D(normalMap.width, normalMap.height);
    51.         for (int i = 0; i < sourceCols.Length; i++)
    52.         {
    53.             channelCols[i] = new Color32((colorisedChannels ? zero : sourceCols[i].b), (colorisedChannels ? zero : sourceCols[i].b), sourceCols[i].b, 255);
    54.         }
    55.         normB.SetPixels32(channelCols);
    56.         normB.Apply();
    57.  
    58.         normA = new Texture2D(normalMap.width, normalMap.height);
    59.         for (int i = 0; i < sourceCols.Length; i++)
    60.         {
    61.             channelCols[i] = new Color32(sourceCols[i].a, sourceCols[i].a, sourceCols[i].a, 255);
    62.         }
    63.         normA.SetPixels32(channelCols);
    64.         normA.Apply();
    65.  
    66.  
    67.  
    68.         sourceCols = rgbMap.GetPixels32(0);
    69.  
    70.         rgbR = new Texture2D(rgbMap.width, rgbMap.height);
    71.         for (int i = 0; i < sourceCols.Length; i++)
    72.         {
    73.             channelCols[i] = new Color32(sourceCols[i].r, (colorisedChannels ? zero : sourceCols[i].r), (colorisedChannels ? zero : sourceCols[i].r), 255);
    74.         }
    75.         rgbR.SetPixels32(channelCols);
    76.         rgbR.Apply();
    77.  
    78.         rgbG = new Texture2D(rgbMap.width, rgbMap.height);
    79.         for (int i = 0; i < sourceCols.Length; i++)
    80.         {
    81.             channelCols[i] = new Color32((colorisedChannels ? zero : sourceCols[i].g), sourceCols[i].g, (colorisedChannels ? zero : sourceCols[i].g), 255);
    82.         }
    83.         rgbG.SetPixels32(channelCols);
    84.         rgbG.Apply();
    85.  
    86.         rgbB = new Texture2D(rgbMap.width, rgbMap.height);
    87.         for (int i = 0; i < sourceCols.Length; i++)
    88.         {
    89.             channelCols[i] = new Color32((colorisedChannels ? zero : sourceCols[i].b), (colorisedChannels ? zero : sourceCols[i].b), sourceCols[i].b, 255);
    90.         }
    91.         rgbB.SetPixels32(channelCols);
    92.         rgbB.Apply();
    93.  
    94.         rgbA = new Texture2D(rgbMap.width, rgbMap.height);
    95.         for (int i = 0; i < sourceCols.Length; i++)
    96.         {
    97.             channelCols[i] = new Color32(sourceCols[i].a, sourceCols[i].a, sourceCols[i].a, 255);
    98.         }
    99.         rgbA.SetPixels32(channelCols);
    100.         rgbA.Apply();
    101.     }
    102.  
    103.     private void OnGUI()
    104.     {
    105.         GUILayout.BeginHorizontal();
    106.         GUILayout.Label(rgbMap);
    107.         GUILayout.Label(rgbR);
    108.         GUILayout.Label(rgbG);
    109.         GUILayout.Label(rgbB);
    110.         GUILayout.Label(rgbA);
    111.         GUILayout.EndHorizontal();
    112.  
    113.  
    114.         GUILayout.BeginHorizontal();
    115.         GUILayout.Label(normalMap);
    116.         GUILayout.Label(normR);
    117.         GUILayout.Label(normG);
    118.         GUILayout.Label(normB);
    119.         GUILayout.Label(normA);
    120.         GUILayout.EndHorizontal();
    121.  
    122.     }
    Addendum: the G and B channels might look the same, but i actually found them to be off by 1 or 2, this might just be compression artefacts tho

    One thing that occurs to me, when your maps are being packed, are they getting rotated at all?
    Not sure if that will have any effect on normals... probably not
     
    Last edited: Oct 26, 2018
  7. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    MAy also be worth notig that i can't reproduce your problem in a simple test case (in 2017.1)

    Top image, standard material using a DXTnm normal map, bottom image after running the code below, normal map channel now contains the generated RGBA 32bit texture


    Code (CSharp):
    1.     public Renderer r;
    2.     public Texture2D rgbMap;
    3.  
    4.     private void OnGUI()
    5.     {
    6.         if (GUILayout.Button("Switch"))
    7.         {
    8.             Texture2D t = new Texture2D(rgbMap.width, rgbMap.height);
    9.             Color32[] cols = rgbMap.GetPixels32(0);
    10.             for(int i = 0; i < cols.Length; i++)
    11.             {
    12.                 cols[i] = new Color32(1, cols[i].g, cols[i].g, cols[i].r);
    13.             }
    14.             t.SetPixels32(cols);
    15.             t.Apply();
    16.             r.material.SetTexture("_BumpMap", t);
    17.         }
    18.     }
    19.  
     
    Last edited: Oct 26, 2018
  8. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Thanks for the help I will look back into these ideas! And thanks for the sample code!... It could be an error anyhwere in the chain of commands from PackTextures to the shader itself. A lot of hidden unity magic in there we cannot see in the codebase. I will probably have to get back at this tomorrow. I should have tested the packing code seperate of pack textures. Let me get back at it tomorrow and see what I can pull out of a hat thanks again!

    O and no rotatio sorry forgot to ask. They are direct texel location matched
     
    Last edited: Oct 27, 2018
  9. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Ok so far still the same result but I did implement some code nit picked from the above to make sure I am not crazy.
    The normal map does now look like colorwise like your color map.

    I am starting to lean into its not the normal map but the normals are being made incorrect during the packing process possible.
     
  10. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Ruled out normals at least.

    Left is a prefab right is the Packed prefab one piece.
    Pre-drag right material to left

    upload_2018-10-27_14-50-41.png

    Post drag material right to left....

    upload_2018-10-27_14-49-54.png


    So that removes normals as the problem. It does look like it is a material or normal map problem.

    Moved the light and the normal map almost seems inversed or flipped .. so pursuing that.

    upload_2018-10-27_14-52-17.png


    Still rooting around.. I used your nicely written ease if use function and still have the issue. So I am investigating all around the material.

    Code (CSharp):
    1.    
    2. public static Texture2D NormalMapToUnityFormat(Texture2D normalMap)
    3.     {
    4.  
    5.         Texture2D t = new Texture2D(normalMap.width, normalMap.height);
    6.         Color32[] cols = normalMap.GetPixels32(0);
    7.         for (int i = 0; i < cols.Length; i++)
    8.         {
    9.             cols[i] = new Color32(1, cols[i].g, cols[i].g, cols[i].r);
    10.         }
    11.         t.SetPixels32(cols);
    12.         t.Apply();
    13.         return t;
    14.     }
    15.  
     
  11. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Kinda looks like your image for the 'correct' torso looks like the normal is wrong anyway...
    like, the shading on the pecs makes it look like they are dented in, instead of bulging out
     
  12. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    LOL ... yes they are... I just do not want to reverse the art currently .. Good eye. =)...
    He has inverse abs... its a new thing.. I would take either...( it is test art I should have mentioned that) It should not effect the result.

    I am doing simple tests today. Back in my AAA games graphics programmer days I had a mentor that had a great tip.. break it down to the smallest parts and solve the major problem with a simple problem. So now I am... working today at a lower level away from the multiple database, constructed skinned meshes based off item data and a base skeleton, and atlased normal mapped object to just the basics to find the issue. I do miss the days in the industry when I had access to the source so I could trace deep and read the issues. I have worked with the source engine, Doom engine, and Unreal engine (not to mention a few we tried out and I have stories lol) at the source level. It made it much easier to debug. BUT I love unity as it ease of quickly prototyping is unrivaled. =).. I will post with what I find.

    I just hope its not a 2018.2 issue as that could burn days and not know what it is without source access.
     
  13. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    This is what makes me think it might be deeper...2018.2 might be an issue....


    asd.jpg

    Left plain Jane prefab right prefab with this attached:

    Code (CSharp):
    1.     void Start () {
    2.         SkinnedMeshRenderer smr = this.gameObject.GetComponent<SkinnedMeshRenderer>();
    3.         if(smr)
    4.         {
    5.             Material x = GameObject.Instantiate(smr.sharedMaterial);
    6.             Texture2D n = x.GetTexture("_BumpMap") as Texture2D;
    7.             if(n)
    8.             {
    9.                 Texture2D t = TexTools.NormalMapToUnityFormat(n);
    10.                 x.SetTexture("_BumpMap", t);
    11.                 smr.sharedMaterial = x;
    12.             }
    13.         }
    14.     }
    15.    
    Code (CSharp):
    1.     public static Texture2D NormalMapToUnityFormat(Texture2D normalMap)
    2.     {
    3.  
    4.         Texture2D t = new Texture2D(normalMap.width, normalMap.height);
    5.         Color32[] cols = normalMap.GetPixels32(0);
    6.         for (int i = 0; i < cols.Length; i++)
    7.         {
    8.             cols[i] = new Color32(1, cols[i].g, cols[i].g, cols[i].r);
    9.         }
    10.         t.SetPixels32(cols);
    11.         t.Apply();
    12.         return t;
    13.     }
     
  14. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Well with that I am early in my project I am going to head over and check out the latest unreal build and see what it has. Today will be exploritory it seems =)... I updated to the latest version of unity and attached the above and same result. I do not want to have special shaders that do not agree with all the other systems I paid for and downloaded as they are not standard shaders... Off to explore!..I do not see a solution here. If that works in 2017 then

    I wonder why Unity made a format DXTnm that we cannot convert to in script easily? Strange choice.

    And the supprt of c++ makes me happy again =)
     
    Last edited: Oct 28, 2018
  15. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Texture2D n = x.GetTexture("_BumpMap") as Texture2D;
    Isnt this just getting a map thats already in the DXTnm format? Running the conversion again on that map will not do anything useful
     
  16. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Commented below for my own checking =)

    Code (CSharp):
    1.  
    2. Material x = GameObject.Instantiate(smr.sharedMaterial); // make new material from a existing material which has a Normal map in RGBA format
    3. Texture2D n = x.GetTexture("_BumpMap") as Texture2D; // get the RGBA image I did not convert in this test to DXTnm BTW in this test I left it as a RGBA for testing
    4.  
    5. if(n) // check
    6. {
    7.      Texture2D t = TexTools.NormalMapToUnityFormat(n); // use the below code to move the colors around
    8.      x.SetTexture("_BumpMap", t); // take the returned texture and set it0
    9.      smr.sharedMaterial = x; // set the new material.
    10.  }
    11.  
    12.     public static Texture2D NormalMapToUnityFormat(Texture2D normalMap)
    13.     {
    14.  
    15.         Texture2D t = new Texture2D(normalMap.width, normalMap.height); // create a new texture
    16.         Color32[] cols = normalMap.GetPixels32(0); // get the colors
    17.         for (int i = 0; i < cols.Length; i++)
    18.         {
    19.             cols[i] = new Color32(1, cols[i].g, cols[i].g, cols[i].r); // move the colors
    20.         }
    21.         t.SetPixels32(cols); // set the colors
    22.         t.Apply(); // apply it
    23.         return t; // return it
    24.     }
    25.  
    26.  
    Hope that clears it up =)

    Thanks again for the help.. I spent yesterday researching the Unreal engine. Seems very similar these days to Unity. Not like back in the day I was wrestling with it in the game industry. Kids these days are so spoiled =P....
     
  17. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Hold up hold up
    I think there has been a misunderstanding somewhere.
    Why are you trying to compress/create a DXTnm format at run-time.
    Once they load they get unpacked into rgb by the shader.
    If you are already in run-time, shouldn't you just be providing rgb maps to the shader?

    Also, a new concern, I don't know how/if you can guarantee that Texture2D.PackTextures will arrange all your albedo maps in the same configuration as your normal maps, if you are just using standard shader, you cant have different uvs for albedo and normal
     
  18. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Hello again!

    I am generating a new skinned single mesh from parts. I then pack the textures so I can cut draw calls. With each enemy being generated like this the batches would get very heavy. Rendering the scene twice per frame also doubles it for me (VR).

    Yes the shader has an unpack function. That is true about the RGB format. I am supplying an RGB format I was just stating I wish Unity supplied scrupt methods to seamlessly convert to that format so I could just plug it right in.

    I am afraid of the normal packing also generating different UVs. I wish they would allow multiple textures to go into the pack. I may have to pack them myslef as I used to.

    Again thanks for the time! =)

    I am currently looking into Unreal to see if it supports dynamically created everything. (I am guessing it maybe worse lol)

    I plan on retackling this tonight ot tomorrow hopefully.

    Thanks again! I hope I solve it and am able to explain what dumb mistake I made somewhere... =P
     
  19. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Ok bac at it in Unity. Unreal is nice but I see 2-3x the workload in code as apposed to unity.

    Anyway I am wondering if this is a 2018 version thing. I am duplicating your code exactly and it still blows up the standard shader... back to it..
     
  20. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Strange things happening.....

    I exported the exact RGB normal map from unity and got the red map below:
    (After unity had correctly done its pre-process swizzling.)
    This was to see what their swizzled texture looked like and compare.
    My swizzled version is exactly the same........

    Code (CSharp):
    1.    
    2. public static Texture2D NormalMapToUnityFormat(Texture2D normalMap)
    3.     {
    4.         Texture2D t = new Texture2D(normalMap.width, normalMap.height);
    5.         Color32[] cols = normalMap.GetPixels32(0);
    6.         for (int i = 0; i < cols.Length; i++)
    7.         {
    8.             //cols[i] = new Color32(255, cols[i].g, cols[i].g, cols[i].r);
    9.             cols[i] = new Color32(cols[i].r, cols[i].g, cols[i].b, cols[i].a); // straight copy to RGB for testing
    10.         }
    11.         t.SetPixels32(cols);
    12.         t.Apply();
    13.         TexTools.SaveToShotFolder(t,"proccessed");
    14.         return t;
    15.     }
    Straight Copy: (Unity's swizzle shown... My swizzle was the same...)

    !_NormalStraight.png


    Non unity normal map: (before unity's apply fix it and before my swizzle)

    HeadNrm.jpg

    So this tells me there is something somewhere else.
    It is not the normal map itself?
    Any ideas?

    Is there an Apply function somewhere beyond the texture?

    Also:
    Red seems to be used somewhere from the normal map? (Which supposedly does not effect it)
    It looks like it maybe used for shininess, however it also looks to effect normals..
    0 value for red and 255 for red below:

    255 left 0 right: (This is with me just copying the unity normal map into an RGBA texture)
    red.jpg

    Should look like:

    Untitled-4.jpg



    Stuck in a holding pattern it seems.....ideas?
     
    Last edited: Oct 30, 2018
  21. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Something is definetly weird.... I would think other people are combining normals at runtime in Unity 2018.2.14f1 personal edition.

    I simply took the normal map that was attached.
    Made an exacty color copy of it and reattached it to the "_BumpMap" as a non compressed RGB

    I exported the channels as below and they look right... I am stumped...

    There has to be some other process that has to be applied somewhere now.
    The shader should not care if it is compressed or uncompressed.
    It only recieves color values.

    I need to see if I can get to the standard shaders source and see if I can see anything there....Any ideas would really help thanks for viewing.

    1. Get the colors from main texture
    2. Copy colors to new texture
    3. Add to a new material
    4. Apply normal map to "_BumpMap"
    5. Broken.....?

    channels1.jpg
     
  22. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
  23. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    I found what was causing my issue.
    I was not in gamma lighting mode but linear. Changing to gamma fixed the issue.

    Thanks for all the tips and time... looks like I wasn't as crazy as i thought... -)
     
  24. bansheesoft

    bansheesoft

    Joined:
    Oct 3, 2014
    Posts:
    62
    Just to finalize this thread yes the texturepacker of course delivered different coordinates as they were not tied together between the normal maps and the color maps. I do wish we could supply multiple arrays as most objects need a normal map also. However, I wrote a script to take the returned rects form texture packer and scale down the normal maps to fit in those boxes. I do not support upscaling as I could not see a reason to have higher res normal maps then color maps. All in all it is all working now. It came down to the linear lighting mode switching back to gamma. Stange and I am not reseraching further as there must be some background differences I cannot dig into. Thanks for the ideas!... back to work... I hope this thread helps someone avoid my wasted time :)
     
  25. SkymapJosh

    SkymapJosh

    Joined:
    Nov 21, 2019
    Posts:
    16
    Did you ever discover why the normals were being packed as (1,g,g,r) instead of (0,g,0,r) like @bgolus had mentioned before?
     
  26. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Between my original post @bansheesoft mentioned and this post, Unity changed how they packed normal maps. Technically my original post was wrong too in terms of exactly how Unity did their packing, but accurately reflected how normal maps were being used by the internal shaders.

    At the time of my original post the were actually packed as (g,g,g,r), but the shaders only ever accessed the green and alpha channels of the texture, so the other two channels didn't actually matter what they held (as far as if was a solid color or a copy of the green channel, other data will cause some additional compression artifacts).

    For Unity 2017.2 the normal map format was changed to (1,g,1,r), and the shaders accessed the red, green, and alpha channels of the texture (blue is still ignored). This was changed to add support for the BC5 texture format, which is a two channel compressed texture used by most of the industry for storing normal maps for the last decade, but which Unity only got support for in Unity 5.5 (2016). These are stored as just (r,g,0,1). Technically since the blue and alpha channels aren't represented by the BC5 format, the blue and alpha channels are just the default outputs the GPU gives the shader when sampling a texture that doesn't have those channels.

    The trick is the shaders can support both swizzled DXT5 normal maps and BC5 normal maps by using a little math. Multiply the red and alpha together for the x component of the normal. Here's the code Unity uses:
    Code (csharp):
    1. // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1)
    2. // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5
    3. fixed3 UnpackNormalmapRGorAG(fixed4 packednormal)
    4. {
    5.     // This do the trick
    6.    packednormal.x *= packednormal.w;
    7.  
    8.     fixed3 normal;
    9.     normal.xy = packednormal.xy * 2 - 1;
    10.     normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
    11.     return normal;
    12. }
    Humorously, the Unity's own comments are wrong there. The default "bump" texture is not (0, 0, 1, 1), but (0.5, 0.5, 1.0, 1.0).

    I first learned of this trick from an old rendering paper Insomniac Games wrote back in the late 2000's. The BC5 format only got wide release on the desktop with DirectX 10 around 2007, but it had existed as an OpenGL extension on ATI (now AMD) GPUs since 2004, and the Xbox 360 (2005) had support for it. The format was named 3dc on desktop ATI GPUs, and DXn on the Xbox (confusingly since DXTC textures were often referred to as "DXTn" at the time). But the trick predates BC5 entirely, and wasn't even mentioned in the Insomniac paper since they were a Sony only developer at the time and the PS3 (2006) didn't support the format since its GPU was Nvidia based. Instead it's a started out as just a great trick for supporting both DXT1 and DXT5 normal maps allowing people to use the higher quality (and double the memory cost) DXT5 normal maps only when the compression artifacts of storing the normal map "straight" in DXT1 were obvious enough to warrant it without needing separate shaders.
     
    Last edited: Dec 4, 2019
  27. SkymapJosh

    SkymapJosh

    Joined:
    Nov 21, 2019
    Posts:
    16
    Thank you so much, we recently upgraded a project from 5.6 to 2019.2 and one shader that was accessing the normal maps directly (not using the UnpackNormal) was using norm.rg which in 5.6 I guess would have been the equivalent to norm.gg, but in the newer Unity we were getting the constant 1 for r which changed the appearance. By Unity being wrong in their comment you mean it should be 0.5,0.5 because the scale is 0,1 where they reference 0 on a scale of -1,1 correct? Sorry to throw so many questions at you, I really want to get into graphics, and it's not everyday I get the attention of someone with as much knowledge as you.
     
  28. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Correct.