Search Unity

Trouble with normal mapping on tri-planar terrain shader

Discussion in 'Shaders' started by dudester, Aug 15, 2020.

  1. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    good day everyone , I'm hoping someone can help with my normal mapping using tri planar terrain ,
    The normals are correct if I set the offset to be high by adding float3 (2,0,0) + normal
    this seems like a hack and doesnt work on cubes ,
    Heres my code
    Code (CSharp):
    1.  
    2. float3 rnmBlendUnpacked(float3 n1, float3 n2)
    3.         {
    4.             n1 += float3(0, 0, 1);
    5.             n2 *= float3(-1, -1, 1);
    6.             return n1 * dot(n1, n2) / n1.z - n2;
    7.         }
    8.           half3 TriplanarNormal(int tex, half3 worldPosition, half3 projNormal, half scale)
    9.         {
    10.               half4 worldpos = 0;
    11.               worldpos.xyz = worldPosition.xyz;
    12.  
    13.               worldpos.w = tex;
    14.  
    15.  
    16.               half3 absVertNormal = abs(projNormal);
    17.               float3 blendWeight = pow(saturate(abs(projNormal) - BlendOff), HeightIntensity);
    18.  
    19.               blendWeight /= (blendWeight.x + blendWeight.y + blendWeight.z);
    20.               half3 X = 0;
    21.               worldpos.zy *= scale;
    22.               if(blendWeight.x>0)
    23.               X = UnpackNormal(UNITY_SAMPLE_TEX2DARRAY(_MainTexNorm, (worldpos.zyw)));
    24.               worldpos.xyz = worldPosition.xyz;
    25.               worldpos.xz *= scale;
    26.               half3 Y = 0;
    27.               if (blendWeight.y > 0)
    28.                   Y = UnpackNormal(UNITY_SAMPLE_TEX2DARRAY(_MainTexNorm, (worldpos.xzw))) ;
    29.               worldpos.xyz = worldPosition.xyz;
    30.               worldpos.xy *= scale;
    31.               half3 Z = 0;
    32.               if (blendWeight.z > 0)
    33.               Z = UnpackNormal(UNITY_SAMPLE_TEX2DARRAY(_MainTexNorm, (worldpos.xyw)));
    34.  
    35.              
    36.              
    37.              
    38.               // Swizzle world normals to match tangent space and apply RNM blend
    39.               X = rnmBlendUnpacked(half3(projNormal.zy, absVertNormal.x), X);
    40.               Y = rnmBlendUnpacked(half3(projNormal.xz, absVertNormal.y), Y);
    41.               Z = rnmBlendUnpacked(half3(projNormal.xy, absVertNormal.z), Z);
    42.               // Get the sign (-1 or 1) of the surface normal
    43.               half3 axisSign = sign(projNormal);
    44.               // Reapply sign to Z
    45.  
    46.               X.z *= axisSign.x;
    47.  
    48.               Y.z *= axisSign.y;
    49.                
    50.               Z.z *= axisSign.z;
    51.  
    52.  
    53.    
    54.  
    55.               return ((X * blendWeight.x + Y * blendWeight.y + Z * blendWeight.z ) + projNormal);
    56.  
    57.         }
    Adding to o.normal, here I add an offset which helps bring the normal to the correct position kinda
    Code (CSharp):
    1.             //_OffSet is usually 2 - 8
    2.             o.Normal = normalize(half3(0, 0, _OffSet) +
    3.                 (float3(
    4.                     dot(colN, IN.worldToTangent0.xyz) ,
    5.                     dot(colN, IN.worldToTangent1.xyz) ,
    6.                     dot(colN, IN.worldToTangent2.xyz)
    7.                     ) * normalIntensity)
    8.             );
    I tried removing swizzling the values but maybe im incorrect in doing so

    Thanks for your help much appreciated .
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    dudester likes this.
  3. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    Oh wow thanks so much , that works perfectly . Been trying for a couple days now .
     
  4. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    Wow that's awesome didn't even realise that was you , it helped so much I just checked in the article and found that link in there , I should've read on further, thanks , you should get a badge for all the help you've been giving , found so much useful info from your posts
     
  5. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    Also something else that maybe you could help with is there a tex2darraygrad sampler for mipmapping cause as of now my shader has no mipmapping , I know you can use tex2dgrad in combination with ddx and ddy to sample the texture at different mipmap levels but how would you do this in texture arrays . thanks again
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If there’s no mip mapping, it’ll be because your
    Texture2DArray
    is missing mip maps, not because you’re not using a sampling function that takes gradients.

    The default sampler functions will automatically pick the appropriate mip level based on the screen derivatives & sampler state (either inline or set on the texture asset). Gradient based mip mapping is mostly useful for overriding the derivatives in cases where you have discontinuities in the UVs from doing things like
    frac()
    or calculating UVs from trig functions.

    When creating your
    Texture2DArray
    you’ll need to create the asset object with mip maps enabled and then copy each mip individually for each layer.
    CopyTexture
    otherwise only copies the top mip level.
     
  7. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    ok well then thats strange cause I do apply mip maps but maybe I'm just doing doing it wrong , when i check the array I can select different mipmap levels , but in the game view under display mipmaps it doesnt show , the grass and trees show different mipmaps , heres the code i use
    Code (CSharp):
    1.  Texture2DArray textureArray = new Texture2DArray(1024, 1024, numberOfTextures, format, true,false);
    2.             for (int i = 0; i < textures.Length; i++)
    3.    {
    4.    
    5.     //textureArray.SetPixels(textures[i].GetPixels(0), i, 0);
    6.     for(int m =0;m < textures[i].mipmapCount;m++)
    7.                 Graphics.CopyTexture(textures[i], 0,m, textureArray, i,m);
    8.    }
    9.    textureArray.Apply();
     
  8. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    is it possible unity uses UVs for mipmapping , cause I use UVs for determining the biome in shader , and drawing a texture on the mesh, basically I set uvs as vector2(0,biome);
    reason I say this is The mip maps work at the boundary between biomes but not on them.

    here is scene view of mipmaps

    sceneview - Copy.jpg
    Game view of the same spot
    sceneview.jpg
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The Mipmaps view knows nothing of what your shader is doing. Do not use it as a litmus test for if mipmaps are working or not. It's using it's own shader that uses the mesh's first UV set, the texture assigned to
    _MainTex
    , and any scaling that texture property might have in the material properties. If your shader doesn't use _MainTex, or is completely ignoring the mesh UVs, like in a world space UV or triplanar UV shader, then it's useless.

    Just from looking at that second image I can tell mip mapping is working perfectly. The textures in the distance are getting slightly blurry rather than horribly aliased, which means it's switching to a lower mip level as the texture is being displayed smaller, as it should.
     
    dudester likes this.
  10. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    371
    OK i see now well that's great news , thanks so much for all the help .