You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
16 KiB
HLSL
304 lines
16 KiB
HLSL
|
3 years ago
|
#define DECLARE_TERRAIN_LAYER_TEXS(n) \
|
||
|
|
TEXTURE2D(_Splat##n); \
|
||
|
|
TEXTURE2D(_Normal##n); \
|
||
|
|
TEXTURE2D(_Mask##n)
|
||
|
|
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(0);
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(1);
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(2);
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(3);
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(4);
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(5);
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(6);
|
||
|
|
DECLARE_TERRAIN_LAYER_TEXS(7);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#undef DECLARE_TERRAIN_LAYER_TEXS
|
||
|
|
|
||
|
|
SAMPLER(sampler_Splat0);
|
||
|
|
|
||
|
|
float GetSumHeight(float4 heights0, float4 heights1)
|
||
|
|
{
|
||
|
|
float sumHeight = heights0.x;
|
||
|
|
sumHeight += heights0.y;
|
||
|
|
sumHeight += heights0.z;
|
||
|
|
sumHeight += heights0.w;
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
sumHeight += heights1.x;
|
||
|
|
sumHeight += heights1.y;
|
||
|
|
sumHeight += heights1.z;
|
||
|
|
sumHeight += heights1.w;
|
||
|
|
#endif
|
||
|
|
return sumHeight;
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 SampleNormalGrad(TEXTURE2D_PARAM(textureName, samplerName), float2 uv, float2 dxuv, float2 dyuv, float scale)
|
||
|
|
{
|
||
|
|
float4 nrm = SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, uv, dxuv, dyuv);
|
||
|
|
#ifdef SURFACE_GRADIENT
|
||
|
|
#ifdef UNITY_NO_DXT5nm
|
||
|
|
return float3(UnpackDerivativeNormalRGB(nrm, scale), 0);
|
||
|
|
#else
|
||
|
|
return float3(UnpackDerivativeNormalRGorAG(nrm, scale), 0);
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
#ifdef UNITY_NO_DXT5nm
|
||
|
|
return UnpackNormalRGB(nrm, scale);
|
||
|
|
#else
|
||
|
|
return UnpackNormalmapRGorAG(nrm, scale);
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 UnpackNormalDigger(float4 nrm, float scale)
|
||
|
|
{
|
||
|
|
#ifdef SURFACE_GRADIENT
|
||
|
|
#ifdef UNITY_NO_DXT5nm
|
||
|
|
return float3(UnpackDerivativeNormalRGB(nrm, scale), 0);
|
||
|
|
#else
|
||
|
|
return float3(UnpackDerivativeNormalRGorAG(nrm, scale), 0);
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
#ifdef UNITY_NO_DXT5nm
|
||
|
|
return UnpackNormalRGB(nrm, scale);
|
||
|
|
#else
|
||
|
|
return UnpackNormalmapRGorAG(nrm, scale);
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
float4 RemapMasks(float4 masks, float blendMask, float4 remapOffset, float4 remapScale)
|
||
|
|
{
|
||
|
|
float4 ret = masks;
|
||
|
|
ret.b *= blendMask; // height needs to be weighted before remapping
|
||
|
|
ret = ret * remapScale + remapOffset;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef OVERRIDE_SPLAT_SAMPLER_NAME
|
||
|
|
#define sampler_Splat0 OVERRIDE_SPLAT_SAMPLER_NAME
|
||
|
|
SAMPLER(OVERRIDE_SPLAT_SAMPLER_NAME);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void TerrainSplatBlend(FragInputs input, inout TerrainLitSurfaceData surfaceData, PositionInputs posInput)
|
||
|
|
{
|
||
|
|
float2 controlUV = input.texCoord0.xy;
|
||
|
|
float2 splatBaseUV = controlUV;
|
||
|
|
|
||
|
|
float4 albedo[_LAYER_COUNT];
|
||
|
|
float3 normal[_LAYER_COUNT];
|
||
|
|
float4 masks[_LAYER_COUNT];
|
||
|
|
|
||
|
|
#ifdef _NORMALMAP
|
||
|
|
#define SampleNormal(i) SampleNormalGrad(_Normal##i, sampler_Splat0, splatuv, splatdxuv, splatdyuv, _NormalScale##i)
|
||
|
|
#define SampleNormalTriplanar(i) \
|
||
|
|
float4 cXn = SAMPLE_TEXTURE2D(_Normal##i, sampler_Splat0, splatuvY); \
|
||
|
|
float4 cYn = SAMPLE_TEXTURE2D(_Normal##i, sampler_Splat0, splatuvX); \
|
||
|
|
float4 cZn = SAMPLE_TEXTURE2D(_Normal##i, sampler_Splat0, splatuvZ); \
|
||
|
|
float4 sideN = lerp(cXn, cZn, abs(normalWS.z)); \
|
||
|
|
float4 topN = lerp(sideN, cYn, abs(normalWS.y)); \
|
||
|
|
normal[i] = UnpackNormalDigger(topN, _NormalScale##i);
|
||
|
|
#else
|
||
|
|
#define SampleNormal(i) float3(0, 0, 0)
|
||
|
|
#define SampleNormalTriplanar(i) normal[i] = float3(0, 0, 0)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define DefaultMask(i) float4(_Metallic##i, _MaskMapRemapOffset##i.y + _MaskMapRemapScale##i.y, _MaskMapRemapOffset##i.z + 0.5 * _MaskMapRemapScale##i.z, albedo[i].a * _Smoothness##i)
|
||
|
|
|
||
|
|
#ifdef _MASKMAP
|
||
|
|
#define MaskModeMasks(i, blendMask) RemapMasks(SAMPLE_TEXTURE2D_GRAD(_Mask##i, sampler_Splat0, splatuv, splatdxuv, splatdyuv), blendMask, _MaskMapRemapOffset##i, _MaskMapRemapScale##i)
|
||
|
|
#define SampleMasks(i, blendMask) lerp(DefaultMask(i), MaskModeMasks(i, blendMask), _LayerHasMask##i)
|
||
|
|
#define NullMask(i) float4(0, 1, _MaskMapRemapOffset##i.z, 0) // only height matters when weight is zero.
|
||
|
|
|
||
|
|
#define SampleMasksTriplanar(i, blendMask) \
|
||
|
|
float4 cXm = SAMPLE_TEXTURE2D(_Mask##i, sampler_Splat0, splatuvY); \
|
||
|
|
float4 cYm = SAMPLE_TEXTURE2D(_Mask##i, sampler_Splat0, splatuvX); \
|
||
|
|
float4 cZm = SAMPLE_TEXTURE2D(_Mask##i, sampler_Splat0, splatuvZ); \
|
||
|
|
float4 sideM = lerp(cXm, cZm, abs(normalWS.z)); \
|
||
|
|
float4 topM = lerp(sideM, cYm, abs(normalWS.y)); \
|
||
|
|
float4 maskModeMasksTriplanar = RemapMasks(topM, blendMask, _MaskMapRemapOffset##i, _MaskMapRemapScale##i); \
|
||
|
|
masks[i] = lerp(DefaultMask(i), maskModeMasksTriplanar, _LayerHasMask##i);
|
||
|
|
|
||
|
|
#else
|
||
|
|
#define SampleMasks(i, blendMask) DefaultMask(i)
|
||
|
|
#define NullMask(i) float4(0, 1, 0, 0)
|
||
|
|
#define SampleMasksTriplanar(i, blendMask) masks[i] = DefaultMask(i)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define SampleResults(i, mask) \
|
||
|
|
UNITY_BRANCH if (mask > 0) \
|
||
|
|
{ \
|
||
|
|
float2 tile = _Splat##i##_ST.xy; \
|
||
|
|
float2 splatuvY = positionWS.zy * tile + _Splat##i##_ST.zw; \
|
||
|
|
float2 splatuvX = positionWS.xz * tile + _Splat##i##_ST.zw; \
|
||
|
|
float2 splatuvZ = positionWS.xy * tile + _Splat##i##_ST.zw; \
|
||
|
|
float2 splatuv = splatBaseUV * _Splat##i##_ST.xy + _Splat##i##_ST.zw; \
|
||
|
|
float2 splatdxuv = dxuv; \
|
||
|
|
float2 splatdyuv = dyuv; \
|
||
|
|
albedo[i] = SAMPLE_TEXTURE2D_GRAD(_Splat##i, sampler_Splat0, splatuv, splatdxuv, splatdyuv); \
|
||
|
|
albedo[i].rgb *= _DiffuseRemapScale##i.xyz; \
|
||
|
|
normal[i] = SampleNormal(i); \
|
||
|
|
masks[i] = SampleMasks(i, mask); \
|
||
|
|
} \
|
||
|
|
else \
|
||
|
|
{ \
|
||
|
|
albedo[i] = float4(0, 0, 0, 0); \
|
||
|
|
normal[i] = float3(0, 0, 0); \
|
||
|
|
masks[i] = NullMask(i); \
|
||
|
|
}
|
||
|
|
|
||
|
|
#define SampleResultsTriplanar(i, mask) \
|
||
|
|
UNITY_BRANCH if (mask > 0) \
|
||
|
|
{ \
|
||
|
|
float2 splatdxuv = dxuv * _Splat##i##_ST.x; \
|
||
|
|
float2 splatdyuv = dyuv * _Splat##i##_ST.y; \
|
||
|
|
float2 tile = _Splat##i##_ST.xy; \
|
||
|
|
float2 splatuvY = positionWS.zy * tile + _Splat##i##_ST.zw; \
|
||
|
|
float2 splatuvX = positionWS.xz * tile + _Splat##i##_ST.zw; \
|
||
|
|
float2 splatuvZ = positionWS.xy * tile + _Splat##i##_ST.zw; \
|
||
|
|
float4 cX = SAMPLE_TEXTURE2D(_Splat##i, sampler_Splat0, splatuvY); \
|
||
|
|
float4 cY = SAMPLE_TEXTURE2D(_Splat##i, sampler_Splat0, splatuvX); \
|
||
|
|
float4 cZ = SAMPLE_TEXTURE2D(_Splat##i, sampler_Splat0, splatuvZ); \
|
||
|
|
float4 side = lerp(cX, cZ, abs(normalWS.z)); \
|
||
|
|
float4 top = lerp(side, cY, abs(normalWS.y)); \
|
||
|
|
albedo[i] = top; \
|
||
|
|
albedo[i].rgb *= _DiffuseRemapScale##i.xyz; \
|
||
|
|
SampleNormalTriplanar(i); \
|
||
|
|
SampleMasksTriplanar(i, mask); \
|
||
|
|
} \
|
||
|
|
else \
|
||
|
|
{ \
|
||
|
|
albedo[i] = float4(0, 0, 0, 0); \
|
||
|
|
normal[i] = float3(0, 0, 0); \
|
||
|
|
masks[i] = NullMask(i); \
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 positionWS = GetAbsolutePositionWS(input.positionRWS);
|
||
|
|
float3 normalWS = input.tangentToWorld[2].xyz;
|
||
|
|
|
||
|
|
float2 dxuv = ddx(splatBaseUV);
|
||
|
|
float2 dyuv = ddy(splatBaseUV);
|
||
|
|
|
||
|
|
float4 blendMasks0 = input.color;
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
float4 blendMasks1 = float4(0, 0, 0, 0);
|
||
|
|
blendMasks1.xy = input.texCoord2.xy;
|
||
|
|
blendMasks1.zw = input.texCoord3.xy;
|
||
|
|
#else
|
||
|
|
float4 blendMasks1 = float4(0, 0, 0, 0);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
SampleResultsTriplanar(0, blendMasks0.x);
|
||
|
|
SampleResultsTriplanar(1, blendMasks0.y);
|
||
|
|
SampleResultsTriplanar(2, blendMasks0.z);
|
||
|
|
SampleResultsTriplanar(3, blendMasks0.w);
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
SampleResultsTriplanar(4, blendMasks1.x);
|
||
|
|
SampleResultsTriplanar(5, blendMasks1.y);
|
||
|
|
SampleResultsTriplanar(6, blendMasks1.z);
|
||
|
|
SampleResultsTriplanar(7, blendMasks1.w);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#undef SampleNormal
|
||
|
|
#undef SampleNormalTriplanar
|
||
|
|
#undef SampleMasks
|
||
|
|
#undef SampleMasksTriplanar
|
||
|
|
#undef SampleResults
|
||
|
|
#undef SampleResultsTriplanar
|
||
|
|
|
||
|
|
float weights[_LAYER_COUNT];
|
||
|
|
ZERO_INITIALIZE_ARRAY(float, weights, _LAYER_COUNT);
|
||
|
|
|
||
|
|
#ifdef _MASKMAP
|
||
|
|
#if defined(_TERRAIN_BLEND_HEIGHT)
|
||
|
|
// Modify blendMask to take into account the height of the layer. Higher height should be more visible.
|
||
|
|
float maxHeight = masks[0].z;
|
||
|
|
maxHeight = max(maxHeight, masks[1].z);
|
||
|
|
maxHeight = max(maxHeight, masks[2].z);
|
||
|
|
maxHeight = max(maxHeight, masks[3].z);
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
maxHeight = max(maxHeight, masks[4].z);
|
||
|
|
maxHeight = max(maxHeight, masks[5].z);
|
||
|
|
maxHeight = max(maxHeight, masks[6].z);
|
||
|
|
maxHeight = max(maxHeight, masks[7].z);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Make sure that transition is not zero otherwise the next computation will be wrong.
|
||
|
|
// The epsilon here also has to be bigger than the epsilon in the next computation.
|
||
|
|
float transition = max(_HeightTransition, 1e-5);
|
||
|
|
|
||
|
|
// The goal here is to have all but the highest layer at negative heights, then we add the transition so that if the next highest layer is near transition it will have a positive value.
|
||
|
|
// Then we clamp this to zero and normalize everything so that highest layer has a value of 1.
|
||
|
|
float4 weightedHeights0 = { masks[0].z, masks[1].z, masks[2].z, masks[3].z };
|
||
|
|
weightedHeights0 = weightedHeights0 - maxHeight.xxxx;
|
||
|
|
// We need to add an epsilon here for active layers (hence the blendMask again) so that at least a layer shows up if everything's too low.
|
||
|
|
weightedHeights0 = (max(0, weightedHeights0 + transition) + 1e-6) * blendMasks0;
|
||
|
|
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
float4 weightedHeights1 = { masks[4].z, masks[5].z, masks[6].z, masks[7].z };
|
||
|
|
weightedHeights1 = weightedHeights1 - maxHeight.xxxx;
|
||
|
|
weightedHeights1 = (max(0, weightedHeights1 + transition) + 1e-6) * blendMasks1;
|
||
|
|
#else
|
||
|
|
float4 weightedHeights1 = { 0, 0, 0, 0 };
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Normalize
|
||
|
|
float sumHeight = GetSumHeight(weightedHeights0, weightedHeights1);
|
||
|
|
blendMasks0 = weightedHeights0 / sumHeight.xxxx;
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
blendMasks1 = weightedHeights1 / sumHeight.xxxx;
|
||
|
|
#endif
|
||
|
|
#elif defined(_TERRAIN_BLEND_DENSITY)
|
||
|
|
// Denser layers are more visible.
|
||
|
|
float4 opacityAsDensity0 = saturate((float4(albedo[0].a, albedo[1].a, albedo[2].a, albedo[3].a) - (float4(1.0, 1.0, 1.0, 1.0) - blendMasks0)) * 20.0); // 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
|
||
|
|
opacityAsDensity0 += 0.001f * blendMasks0; // if all weights are zero, default to what the blend mask says
|
||
|
|
float4 useOpacityAsDensityParam0 = { _DiffuseRemapScale0.w, _DiffuseRemapScale1.w, _DiffuseRemapScale2.w, _DiffuseRemapScale3.w }; // 1 is off
|
||
|
|
blendMasks0 = lerp(opacityAsDensity0, blendMasks0, useOpacityAsDensityParam0);
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
float4 opacityAsDensity1 = saturate((float4(albedo[4].a, albedo[5].a, albedo[6].a, albedo[7].a) - (float4(1.0, 1.0, 1.0, 1.0) - blendMasks1)) * 20.0); // 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
|
||
|
|
opacityAsDensity1 += 0.001f * blendMasks1; // if all weights are zero, default to what the blend mask says
|
||
|
|
float4 useOpacityAsDensityParam1 = { _DiffuseRemapScale4.w, _DiffuseRemapScale5.w, _DiffuseRemapScale6.w, _DiffuseRemapScale7.w };
|
||
|
|
blendMasks1 = lerp(opacityAsDensity1, blendMasks1, useOpacityAsDensityParam1);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Normalize
|
||
|
|
float sumHeight = GetSumHeight(blendMasks0, blendMasks1);
|
||
|
|
blendMasks0 = blendMasks0 / sumHeight.xxxx;
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
blendMasks1 = blendMasks1 / sumHeight.xxxx;
|
||
|
|
#endif
|
||
|
|
#endif // if _TERRAIN_BLEND_HEIGHT
|
||
|
|
#endif // if _MASKMAP
|
||
|
|
|
||
|
|
weights[0] = blendMasks0.x;
|
||
|
|
weights[1] = blendMasks0.y;
|
||
|
|
weights[2] = blendMasks0.z;
|
||
|
|
weights[3] = blendMasks0.w;
|
||
|
|
#ifdef _TERRAIN_8_LAYERS
|
||
|
|
weights[4] = blendMasks1.x;
|
||
|
|
weights[5] = blendMasks1.y;
|
||
|
|
weights[6] = blendMasks1.z;
|
||
|
|
weights[7] = blendMasks1.w;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
surfaceData.albedo = 0;
|
||
|
|
surfaceData.normalData = 0;
|
||
|
|
float3 outMasks = 0;
|
||
|
|
UNITY_UNROLL for (int i = 0; i < _LAYER_COUNT; ++i)
|
||
|
|
{
|
||
|
|
surfaceData.albedo += albedo[i].rgb * weights[i];
|
||
|
|
surfaceData.normalData += normal[i].rgb * weights[i]; // no need to normalize
|
||
|
|
outMasks += masks[i].xyw * weights[i];
|
||
|
|
}
|
||
|
|
surfaceData.smoothness = outMasks.z;
|
||
|
|
surfaceData.metallic = outMasks.x;
|
||
|
|
surfaceData.ao = outMasks.y;
|
||
|
|
}
|
||
|
|
|
||
|
|
void TerrainLitShade(FragInputs input, inout TerrainLitSurfaceData surfaceData, PositionInputs posInput)
|
||
|
|
{
|
||
|
|
TerrainSplatBlend(input, surfaceData, posInput);
|
||
|
|
}
|
||
|
|
|