Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Question How to use Texture2DArray with Normal map and Normal Unpack node in Shader Graph

Discussion in 'Shader Graph' started by blackshtormx, Jun 6, 2021.

  1. blackshtormx

    blackshtormx

    Joined:
    Oct 15, 2013
    Posts:
    15
    I am extracting `Normal Map` from `Texture2DArray` in shader graph and then converting it to normal using `Normal Unpack` node. But results I get are different, compared to usual `Texure2D` `Normal Map`.
    `Texture2DArray` Normal doesn't work correctly at all, except it just makes terrain look darker.

    `Texture2DArray` is generated using `TextureFormat.RGBA32` as well.
    I've tried setting import settings of normal as "Normal Map" and as "Default". Normals are a bit different in both cases(When generating with import setting of "Normal Map", it becomes Red, when generating with setting of "Default", its colors are more correct), but overall results are the same - dark terrain without correct lighting in both cases.
    Thanks for any help!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,242
    Unity's normal maps are packed into one of three possible layouts.

    On mobile they're generally just stored exactly like the imported texture, ie: no modifications are made apart from making sure the texture is marked as being linear (ie: not sRGB), which is required for all normal maps on all platforms.

    On desktop & console they're packed either as a specially setup DXT5 or BC7 texture (which the inspector shows as DXTnm or BC7 UNorm), or as BC5.

    DXT5 and BC7 textures are stored with the original source texture's red channel moved to the alpha channel, and the green channel copied to both the green and blue. The final texture's red channel is set to 1.0.

    For BC5 textures the red and green channels are left as they are, but the BC5 format has no blue or alpha channels. Shaders still get a float4 value when the texture is sampled, but the blue is always 0.0 and the alpha is always 1.0.

    The Normal Unpack node set to tangent space assumes the normal map is packed in either of these two formats, but does not actually know which one is being used. Instead it always multiplies the alpha and red channel together for the x, then reconstructs the z from the rescaled x and y. This should work with normal maps that aren't using any special packing too. But if you use a DXT5 or BC7 format for your texture array and are storing other data or 0.0 in the alpha channels, the Normal Unpack node will fail to produce a proper normal vector. It'll also fail if the texture isn't set to be a linear space (non-sRGB) texture.

    You can try setting the Normal Unpack node to Space > Object, which skips the special channel unpacking and z reconstruction and instead just does the basic
    normal.xyz = tex.rgb * 2.0 - 1.0;
    . But if that still doesn't work it probably means you have sRGB enabled on your texture array.
     
  3. blackshtormx

    blackshtormx

    Joined:
    Oct 15, 2013
    Posts:
    15
    How do I set TextureArray to be in a nonSRGB texture? I can set individual textures to disable sRGB, but after TextureArray gets generated is seems to be sRGB again.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,242
    The setting on the individual textures is somewhat irrelevant, as you're copying the texel data from the the original texture into the array, and the texel data doesn't say anything about if it's sRGB or not.

    When you create the Texture2DArray you need to set it to be linear.
    https://docs.unity3d.com/ScriptReference/Texture2DArray-ctor.html
     
    blackshtormx likes this.
  5. blackshtormx

    blackshtormx

    Joined:
    Oct 15, 2013
    Posts:
    15
    That worked, but non-sRGB looks very ugly and light on albedo texture, so if I wanted to use sRGB texture array together with Normal maps, I'd have to convert sRGB to non-sRGB in my shader graph( just for normal map) right?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,242
    Albedo and normal textures are fundamentally incompatible. You really want to use two separate texture arrays and not combine them into one. If you convert either the albedo to linear, or the normal to sRGB, means you'll see significant banding in the data. If you're really intent on doing it that way, keep the texture array linear and convert the albedo in the shader. You can use Unity's built in
    GammaToLinearSpace(col.rgb)
    function, or you can approximate it cheaply with
    pow(col.rgb, 2.0)
    .
     
  7. blackshtormx

    blackshtormx

    Joined:
    Oct 15, 2013
    Posts:
    15
    Thanks! I'll stick to using 2 texture arrays, but the second way is nice to know too!
     
  8. claudiocho

    claudiocho

    Joined:
    Aug 19, 2019
    Posts:
    6
    @blackshtormx

    Case you don´t solve your problem, I made this to extract the normal map from a array. Connect the texture2DArray to SampleTexture2DArray(one for each of texture in array using index) and then for each one, multiply by 2, and subtract 0,5. Now link to NormalReconstructZ node and link this to NormalStrenght node. create a float property to use as strenght and add all normals and connect to normal at master stack.
     

    Attached Files: