void TriplanarPrepSpace(inout float3 worldPos, inout float3 n) { #if _TRIPLANARLOCALSPACE && !_FORCELOCALSPACE worldPos = mul(unity_WorldToObject, float4(worldPos, 1)); n = mul((float3x3)unity_WorldToObject, n).xyz; #endif } float3 TerrainBarycentric(float2 p, float2 a, float2 b, float2 c) { float2 v0 = b - a; float2 v1 = c - a; float2 v2 = p - a; float d00 = dot(v0, v0); float d01 = dot(v0, v1); float d11 = dot(v1, v1); float d20 = dot(v2, v0); float d21 = dot(v2, v1); float denom = d00 * d11 - d01 * d01; float v = (d11 * d20 - d01 * d21) / denom; float w = (d00 * d21 - d01 * d20) / denom; float u = 1.0f - v - w; return float3(u, v, w); } float3 GetTerrainBary(float2 uv) { #if _CUSTOMSPLATTEXTURES float2 texSize = _CustomControl0_TexelSize.zw; float2 stp = _CustomControl0_TexelSize.xy; #else float2 texSize = _Control0_TexelSize.zw; float2 stp = _Control0_TexelSize.xy; #endif // scale coords so we can take floor/frac to construct a cell float2 stepped = uv * texSize; float2 uvBottom = floor(stepped); float2 uvFrac = frac(stepped); uvBottom /= texSize; float2 center = stp * 0.5; uvBottom += center; // construct uv/positions of triangle based on our interpolation point float2 cuv0, cuv1, cuv2; // make virtual triangle if (uvFrac.x > uvFrac.y) { cuv0 = uvBottom; cuv1 = uvBottom + float2(stp.x, 0); cuv2 = uvBottom + float2(stp.x, stp.y); } else { cuv0 = uvBottom; cuv1 = uvBottom + float2(0, stp.y); cuv2 = uvBottom + float2(stp.x, stp.y); } float2 uvBaryFrac = uvFrac * stp + uvBottom; return TerrainBarycentric(uvBaryFrac, cuv0, cuv1, cuv2); } void TriplanarPrepSurfaceNormals(float4 uv0, inout float3 worldPos, inout float3 n) { #if _TRIPLANARUSEFACENORMALS float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 flatNormal = normalize(cross(dy, dx)); float3 bary = GetTerrainBary(uv0.xy); #if _MICROTERRAIN float mb = min(bary.x, min(bary.y, bary.z)); n = lerp(n, flatNormal, saturate(mb * _TriplanarFaceBlend * 20)); #else n = lerp(n, flatNormal, _TriplanarFaceBlend); #endif #endif } void DoPrepTriplanar(float4 uv0, float3 n, float3 worldPos, Config c, inout TriplanarConfig tc, half4 weights, inout MIPFORMAT albedoLOD, inout MIPFORMAT normalLOD, inout MIPFORMAT emisLOD, inout MIPFORMAT origAlbedoLOD) { n = normalize(n); tc.pN = pow(abs(n), abs(_TriplanarContrast)); tc.pN = TotalOne(tc.pN); // Get the sign (-1 or 1) of the surface normal half3 axisSign = n < 0 ? -1 : 1; axisSign.z *= -1; tc.axisSign = axisSign; tc.uv0 = float3x3(c.uv0, c.uv0, c.uv0); tc.uv1 = float3x3(c.uv1, c.uv1, c.uv1); tc.uv2 = float3x3(c.uv2, c.uv2, c.uv2); tc.uv3 = float3x3(c.uv3, c.uv3, c.uv3); tc.pN0 = tc.pN; tc.pN1 = tc.pN; tc.pN2 = tc.pN; tc.pN3 = tc.pN; float2 uscale = 0.1 * _TriplanarUVScale.xy; // this was a mistake, but too late to undo. float4 triScale = _TriplanarUVScale; #if _TERRAINBLENDABLESHADER && _TERRAINBLENDMATCHWORLDUV && _WORLDUV uscale = _UVScale.xy; triScale.zw = _UVScale.zw; #endif tc.uv0[0].xy = (worldPos.zy * uscale + triScale.zw); tc.uv0[1].xy = (worldPos.xz * float2(-1,1) * uscale + triScale.zw); tc.uv0[2].xy = (worldPos.xy * uscale + triScale.zw); #if !_SURFACENORMALS tc.uv0[0].x *= axisSign.x; tc.uv0[1].x *= axisSign.y; tc.uv0[2].x *= axisSign.z; #endif tc.uv1[0].xy = tc.uv0[0].xy; tc.uv1[1].xy = tc.uv0[1].xy; tc.uv1[2].xy = tc.uv0[2].xy; tc.uv2[0].xy = tc.uv0[0].xy; tc.uv2[1].xy = tc.uv0[1].xy; tc.uv2[2].xy = tc.uv0[2].xy; tc.uv3[0].xy = tc.uv0[0].xy; tc.uv3[1].xy = tc.uv0[1].xy; tc.uv3[2].xy = tc.uv0[2].xy; #if _USEGRADMIP albedoLOD.d0 = float4(ddx(tc.uv0[0].xy), ddy(tc.uv0[0].xy)); albedoLOD.d1 = float4(ddx(tc.uv0[1].xy), ddy(tc.uv0[1].xy)); albedoLOD.d2 = float4(ddx(tc.uv0[2].xy), ddy(tc.uv0[2].xy)); normalLOD = albedoLOD; emisLOD = albedoLOD; #elif _USELODMIP albedoLOD.x = ComputeMipLevel(tc.uv0[0].xy, _Diffuse_TexelSize.zw); albedoLOD.y = ComputeMipLevel(tc.uv0[1].xy, _Diffuse_TexelSize.zw); albedoLOD.z = ComputeMipLevel(tc.uv0[2].xy, _Diffuse_TexelSize.zw); normalLOD = albedoLOD; emisLOD = albedoLOD; #endif origAlbedoLOD = albedoLOD; #if _PERTEXUVSCALEOFFSET SAMPLE_PER_TEX(ptUVScale, 0.5, c, half4(1,1,0,0)); tc.uv0[0].xy = tc.uv0[0].xy * ptUVScale0.xy + ptUVScale0.zw; tc.uv0[1].xy = tc.uv0[1].xy * ptUVScale0.xy + ptUVScale0.zw; tc.uv0[2].xy = tc.uv0[2].xy * ptUVScale0.xy + ptUVScale0.zw; tc.uv1[0].xy = tc.uv1[0].xy * ptUVScale1.xy + ptUVScale1.zw; tc.uv1[1].xy = tc.uv1[1].xy * ptUVScale1.xy + ptUVScale1.zw; tc.uv1[2].xy = tc.uv1[2].xy * ptUVScale1.xy + ptUVScale1.zw; #if !_MAX2LAYER tc.uv2[0].xy = tc.uv2[0].xy * ptUVScale2.xy + ptUVScale2.zw; tc.uv2[1].xy = tc.uv2[1].xy * ptUVScale2.xy + ptUVScale2.zw; tc.uv2[2].xy = tc.uv2[2].xy * ptUVScale2.xy + ptUVScale2.zw; #endif #if !_MAX3LAYER || !_MAX2LAYER tc.uv3[0].xy = tc.uv3[0].xy * ptUVScale3.xy + ptUVScale3.zw; tc.uv3[1].xy = tc.uv3[1].xy * ptUVScale3.xy + ptUVScale3.zw; tc.uv3[2].xy = tc.uv3[2].xy * ptUVScale3.xy + ptUVScale3.zw; #endif #if _USEGRADMIP albedoLOD.d0 = albedoLOD.d0 * ptUVScale0.xyxy * weights.x + albedoLOD.d0 * ptUVScale1.xyxy * weights.y + albedoLOD.d0 * ptUVScale2.xyxy * weights.z + albedoLOD.d0 * ptUVScale3.xyxy * weights.w; albedoLOD.d1 = albedoLOD.d1 * ptUVScale0.xyxy * weights.x + albedoLOD.d1 * ptUVScale1.xyxy * weights.y + albedoLOD.d1 * ptUVScale2.xyxy * weights.z + albedoLOD.d1 * ptUVScale3.xyxy * weights.w; albedoLOD.d2 = albedoLOD.d2 * ptUVScale0.xyxy * weights.x + albedoLOD.d2 * ptUVScale1.xyxy * weights.y + albedoLOD.d2 * ptUVScale2.xyxy * weights.z + albedoLOD.d2 * ptUVScale3.xyxy * weights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif #else #if _USEGRADMIP albedoLOD.d0 = albedoLOD.d0 * weights.x + albedoLOD.d0 * weights.y + albedoLOD.d0 * weights.z + albedoLOD.d0 * weights.w; albedoLOD.d1 = albedoLOD.d1 * weights.x + albedoLOD.d1 * weights.y + albedoLOD.d1 * weights.z + albedoLOD.d1 * weights.w; albedoLOD.d2 = albedoLOD.d2 * weights.x + albedoLOD.d2 * weights.y + albedoLOD.d2 * weights.z + albedoLOD.d2 * weights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif #endif #if _PERTEXUVROTATION SAMPLE_PER_TEX(ptUVRot, 16.5, c, half4(0,0,0,0)); tc.uv0[0].xy = RotateUV(tc.uv0[0].xy, ptUVRot0.x); tc.uv0[1].xy = RotateUV(tc.uv0[1].xy, ptUVRot0.y); tc.uv0[2].xy = RotateUV(tc.uv0[2].xy, ptUVRot0.z); tc.uv1[0].xy = RotateUV(tc.uv1[0].xy, ptUVRot1.x); tc.uv1[1].xy = RotateUV(tc.uv1[1].xy, ptUVRot1.y); tc.uv1[2].xy = RotateUV(tc.uv1[2].xy, ptUVRot1.z); #if !_MAX2LAYER tc.uv2[0].xy = RotateUV(tc.uv2[0].xy, ptUVRot2.x); tc.uv2[1].xy = RotateUV(tc.uv2[1].xy, ptUVRot2.y); tc.uv2[2].xy = RotateUV(tc.uv2[2].xy, ptUVRot2.z); #endif #if !_MAX3LAYER || !_MAX2LAYER tc.uv3[0].xy = RotateUV(tc.uv3[0].xy, ptUVRot3.x); tc.uv3[1].xy = RotateUV(tc.uv3[1].xy, ptUVRot3.y); tc.uv3[2].xy = RotateUV(tc.uv3[2].xy, ptUVRot3.z); #endif #endif } void PrepTriplanar(float4 uv0, float3 n, float3 worldPos, Config c, inout TriplanarConfig tc, half4 weights, inout MIPFORMAT albedoLOD, inout MIPFORMAT normalLOD, inout MIPFORMAT emisLOD, inout MIPFORMAT origAlbedoLOD) { TriplanarPrepSpace(worldPos, n); TriplanarPrepSurfaceNormals(uv0, worldPos, n); DoPrepTriplanar(uv0, n, worldPos, c, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); } void PrepTriplanarDisplace(float4 uv0, float3 n, float3 worldPos, Config c, inout TriplanarConfig tc, half4 weights, inout MIPFORMAT albedoLOD, inout MIPFORMAT normalLOD, inout MIPFORMAT emisLOD, inout MIPFORMAT origAlbedoLOD) { TriplanarPrepSpace(worldPos, n); DoPrepTriplanar(uv0, n, worldPos, c, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); }