# Trouble with normal mapping on tri-planar terrain shader

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

1. ### dudester

Joined:
Oct 25, 2014
Posts:
323
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

Joined:
Dec 7, 2012
Posts:
9,287
dudester likes this.
3. ### dudester

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

4. ### dudester

Joined:
Oct 25, 2014
Posts:
323
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

Joined:
Oct 25, 2014
Posts:
323
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

Joined:
Dec 7, 2012
Posts:
9,287
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

Joined:
Oct 25, 2014
Posts:
323
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

Joined:
Oct 25, 2014
Posts:
323
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

Game view of the same spot

9. ### bgolus

Joined:
Dec 7, 2012
Posts:
9,287
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

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

unityunity