I’ve been working on combining various code bits I’ve found online of triplanar and stochastic HLSL shaders into one custom function node for URP. I have the regular color textures working, but I just can’t get the normals to work properly. There’s history on this forum of people trying similar things, but my issue stems from combining the two which I can’t find anything for. I’ve poured over my code, tried lots of different random things, and I can get the normals close (not pictured) but just not perfect. Any advice is appreciated.

```
float2 hash2D2D (float2 s) {
//magic numbers
return frac(sin(fmod(float2(dot(s, float2(127.1,311.7)), dot(s, float2(269.5,183.3))), 3.14159))*43758.5453);
}
float4x3 BW_vxFunc (float2 skewUV) {
//vertex IDs and barycentric coords
float2 vxID = float2 (floor(skewUV));
float3 barry = float3 (frac(skewUV), 0);
barry.z = 1.0-barry.x-barry.y;
float4x3 BW_vx;
BW_vx = ((barry.z>0) ?
float4x3(float3(vxID, 0), float3(vxID + float2(0, 1), 0), float3(vxID + float2(1, 0), 0), barry.zyx) :
float4x3(float3(vxID + float2 (1, 1), 0), float3(vxID + float2 (1, 0), 0), float3(vxID + float2 (0, 1), 0), float3(-barry.z, 1.0-barry.y, 1.0-barry.x)));
return BW_vx;
}
void TriplanarStochastic_float(UnityTexture2D TerrainTex, UnityTexture2D NormalTex, UnitySamplerState Sampler, float3 Position, float Tile, float3 Normal, float3 Tangent, float Blend, out float4 terrainCol, out float4 normalMap)
#pragma exclude_renderers gles
{
float3 Node_UV = Position * Tile;
//uv transformed into triangular grid space with UV scaled by approximation of 2*sqrt(3)
float4x3 BW_vxZY = BW_vxFunc(mul(float2x2 (1.0 , 0.0 , -0.57735027 , 1.15470054), Node_UV.zy * 3.464));
float4x3 BW_vxXZ = BW_vxFunc(mul(float2x2 (1.0 , 0.0 , -0.57735027 , 1.15470054), Node_UV.xz * 3.464));
float4x3 BW_vxXY = BW_vxFunc(mul(float2x2 (1.0 , 0.0 , -0.57735027 , 1.15470054), Node_UV.xy * 3.464));
//calculate derivatives to avoid triangular grid artifacts
float2 dx = ddx(Node_UV);
float2 dy = ddy(Node_UV);
float2 dxZY = ddx(Node_UV.zy);
float2 dyZY = ddy(Node_UV.zy);
float2 dxXZ = ddx(Node_UV.xz);
float2 dyXZ = ddy(Node_UV.xz);
float2 dxXY = ddx(Node_UV.xy);
float2 dyXY = ddy(Node_UV.xy);
float3 Node_Blend = pow(abs(Normal), Blend);
float3 Node_Blend_N = max(Node_Blend, 0);
Node_Blend /= dot(Node_Blend, 1.0);
Node_Blend_N /= (Node_Blend_N.x + Node_Blend_N.y + Node_Blend_N.z ).xxx;
//blend samples with calculated weights
terrainCol= mul(TerrainTex.SampleGrad(Sampler, Node_UV.zy + hash2D2D(BW_vxZY[0].xy), dxZY, dyZY), BW_vxZY[3].x)*Node_Blend.x +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.zy + hash2D2D(BW_vxZY[1].xy), dxZY, dyZY), BW_vxZY[3].y)*Node_Blend.x +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.zy + hash2D2D(BW_vxZY[2].xy), dxZY, dyZY), BW_vxZY[3].z)*Node_Blend.x +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.xz + hash2D2D(BW_vxXZ[0].xy), dxXZ, dyXZ), BW_vxXZ[3].x)*Node_Blend.y +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.xz + hash2D2D(BW_vxXZ[1].xy), dxXZ, dyXZ), BW_vxXZ[3].y)*Node_Blend.y +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.xz + hash2D2D(BW_vxXZ[2].xy), dxXZ, dyXZ), BW_vxXZ[3].z)*Node_Blend.y +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.xy + hash2D2D(BW_vxXY[0].xy), dxXY, dyXY), BW_vxXY[3].x)*Node_Blend.z +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.xy + hash2D2D(BW_vxXY[1].xy), dxXY, dyXY), BW_vxXY[3].y)*Node_Blend.z +
mul(TerrainTex.SampleGrad(Sampler, Node_UV.xy + hash2D2D(BW_vxXY[2].xy), dxXY, dyXY), BW_vxXY[3].z)*Node_Blend.z;
float3 a = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.zy + hash2D2D(BW_vxZY[0].xy), dxZY, dyZY), BW_vxZY[3].x));
float3 b = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.zy + hash2D2D(BW_vxZY[1].xy), dxZY, dyZY), BW_vxZY[3].y));
float3 c = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.zy + hash2D2D(BW_vxZY[2].xy), dxZY, dyZY), BW_vxZY[3].z));
a = float3(a.xy + Normal.zy, abs(a.z) * Normal.x);
b = float3(b.xy + Normal.zy, abs(b.z) * Normal.x);
c = float3(c.xy + Normal.zy, abs(c.z) * Normal.x);
float3 d = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.xz + hash2D2D(BW_vxXZ[0].xy), dxXZ, dyXZ), BW_vxXZ[3].x));
float3 e = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.xz + hash2D2D(BW_vxXZ[1].xy), dxXZ, dyXZ), BW_vxXZ[3].y));
float3 f = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.xz + hash2D2D(BW_vxXZ[2].xy), dxXZ, dyXZ), BW_vxXZ[3].z));
d = float3(d.xy + Normal.xz, abs(d.z) * Normal.y);
e = float3(e.xy + Normal.xz, abs(e.z) * Normal.y);
f = float3(f.xy + Normal.xz, abs(f.z) * Normal.y);
float3 g = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.xy + hash2D2D(BW_vxXY[0].xy), dxXY, dyXY), BW_vxXY[3].x));
float3 h = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.xy + hash2D2D(BW_vxXY[1].xy), dxXY, dyXY), BW_vxXY[3].y));
float3 i = UnpackNormalmapRGorAG(mul(NormalTex.SampleGrad(Sampler, Node_UV.xy + hash2D2D(BW_vxXY[2].xy), dxXY, dyXY), BW_vxXY[3].z));
g = float3(g.xy + Normal.xy, abs(g.z) * Normal.z);
h = float3(h.xy + Normal.xy, abs(h.z) * Normal.z);
i = float3(i.xy + Normal.xy, abs(i.z) * Normal.z);
normalMap= float4(normalize(a.zyx*Node_Blend_N.x +
b.zyx*Node_Blend_N.x +
c.zyx*Node_Blend_N.x +
d.xzy*Node_Blend_N.y +
e.xzy*Node_Blend_N.y +
f.xzy*Node_Blend_N.y +
g.xyz*Node_Blend_N.z +
h.xyz*Node_Blend_N.z +
i.xyz*Node_Blend_N.z ), 1);
float3 biTangentWS = normalize(cross(Normal, Tangent));
float3x3 Node_Transform = float3x3(Tangent, biTangentWS, Normal);
normalMap.rgb = TransformWorldToTangent(normalMap.rgb, Node_Transform);
}
```