// Fill SurfaceData/Builtin data function
//-------------------------------------------------------------------------------------
#include "../Lit/LitData.hlsl"
#include "CoreRP/ShaderLibrary/Sampling/SampleUVMapping.hlsl"
#include "../MaterialUtilities.hlsl"
#include "../Lit/LitBuiltinData.hlsl"
#include "../Decal/DecalUtilities.hlsl"
#if defined(_TERRAINLIT_4_LAYERS)
#if defined(_TERRAINLIT_8_LAYERS)
#define _LAYER_COUNT 8
#elif defined(_TERRAINLIT_7_LAYERS)
#define _LAYER_COUNT 7
#elif defined(_TERRAINLIT_6_LAYERS)
#define _LAYER_COUNT 6
#elif defined(_TERRAINLIT_5_LAYERS)
#define _LAYER_COUNT 5
#elif defined(_TERRAINLIT_4_LAYERS)
#define _LAYER_COUNT 4
#elif defined(_TERRAINLIT_3_LAYERS)
#define _LAYER_COUNT 3
#define _LAYER_COUNT 1
#endif
#define LAYERS_HEIGHTMAP_ENABLE (defined(_HEIGHTMAP0) || defined(_HEIGHTMAP1) || (_LAYER_COUNT > 2 && defined(_HEIGHTMAP2)) || (_LAYER_COUNT > 3 && defined(_HEIGHTMAP3)))
float _Metallic0;
float _Smoothness0;
float _Metallic1;
float _Smoothness1;
float _Metallic2;
float _Smoothness2;
SAMPLER(sampler_Splat0);
float _Metallic3;
float _Smoothness3;
#define _BaseColor0 float4(1,1,1,1)
#define _BaseColor1 float4(1,1,1,1)
#define _BaseColor2 float4(1,1,1,1)
#define _BaseColor3 float4(1,1,1,1)
#define _NormalScale0 1
#define _NormalScale1 1
#define _NormalScale2 1
#define _NormalScale3 1
TEXTURE2D(_Splat4);
TEXTURE2D(_Normal4);
float4 _Splat4_ST;
float _Metallic4;
float _Smoothness4;
// Number of sampler are limited, we need to share sampler as much as possible with lit material
// for this we put the constraint that the sampler are the same in a layered material for all textures of the same type
// then we take the sampler matching the first textures use of this type
#define SAMPLER_NORMALMAP_IDX sampler_Splat0
TEXTURE2D(_Splat5);
TEXTURE2D(_Normal5);
float4 _Splat5_ST;
float _Metallic5;
float _Smoothness5;
#if defined(_MASKMAP0)
#define SAMPLER_MASKMAP_IDX sampler_MaskMap0
#elif defined(_MASKMAP1)
#define SAMPLER_MASKMAP_IDX sampler_MaskMap1
#elif defined(_MASKMAP2)
#define SAMPLER_MASKMAP_IDX sampler_MaskMap2
#else
#define SAMPLER_MASKMAP_IDX sampler_MaskMap3
#endif
TEXTURE2D(_Splat6);
TEXTURE2D(_Normal6);
float4 _Splat6_ST;
float _Metallic6;
float _Smoothness6;
#if defined(_HEIGHTMAP0)
#define SAMPLER_HEIGHTMAP_IDX sampler_HeightMap0
#elif defined(_HEIGHTMAP1)
#define SAMPLER_HEIGHTMAP_IDX sampler_HeightMap1
#elif defined(_HEIGHTMAP2)
#define SAMPLER_HEIGHTMAP_IDX sampler_HeightMap2
#elif defined(_HEIGHTMAP3)
#define SAMPLER_HEIGHTMAP_IDX sampler_HeightMap3
#endif
TEXTURE2D(_Splat7);
TEXTURE2D(_Normal7);
float4 _Splat7_ST;
float _Metallic7;
float _Smoothness7;
// Define a helper macro
TEXTURE2D(_Control0);
TEXTURE2D(_Control1);
#define ADD_ZERO_IDX(Name) Name##0
#define _BaseColorMap _Splat
#define sampler_BaseColorMap sampler_Splat
#define _NormalMap _Normal
#define ALPHA_USED_AS_SMOOTHNESS
#define _NORMALMAP_TANGENT_SPACE_IDX
SAMPLER(sampler_Splat0);
SAMPLER(sampler_Control0);
// include LitDataInternal multiple time to define the variation of GetSurfaceData for each layer
#define LAYER_INDEX 0
#define ADD_IDX(Name) Name##0
#ifdef _NORMALMAP0
#define _NORMALMAP_IDX
float GetMaxHeight(float4 heights0
#if _LAYER_COUNT > 4
, float4 heights1
#ifdef _MASKMAP0
#define _MASKMAP_IDX
)
{
float maxHeight = heights0.r;
#if _LAYER_COUNT > 1
maxHeight = max(maxHeight, heights0.g);
#include "../Lit/LitDataIndividualLayer.hlsl"
#undef LAYER_INDEX
#undef ADD_IDX
#undef _NORMALMAP_IDX
#undef _MASKMAP_IDX
#define LAYER_INDEX 1
#define ADD_IDX(Name) Name##1
#ifdef _NORMALMAP1
#define _NORMALMAP_IDX
#if _LAYER_COUNT > 2
maxHeight = max(maxHeight, heights0.b);
#ifdef _MASKMAP1
#define _MASKMAP_IDX
#if _LAYER_COUNT > 3
maxHeight = max(maxHeight, heights0.a);
#include "../Lit/LitDataIndividualLayer.hlsl"
#undef LAYER_INDEX
#undef ADD_IDX
#undef _NORMALMAP_IDX
#undef _MASKMAP_IDX
#define LAYER_INDEX 2
#define ADD_IDX(Name) Name##2
#ifdef _NORMALMAP2
#define _NORMALMAP_IDX
#if _LAYER_COUNT > 4
maxHeight = max(maxHeight, heights1.r);
#ifdef _MASKMAP2
#define _MASKMAP_IDX
#if _LAYER_COUNT > 5
maxHeight = max(maxHeight, heights1.g);
#include "../Lit/LitDataIndividualLayer.hlsl"
#undef LAYER_INDEX
#undef ADD_IDX
#undef _NORMALMAP_IDX
#undef _MASKMAP_IDX
#define LAYER_INDEX 3
#define ADD_IDX(Name) Name##3
#ifdef _NORMALMAP3
#define _NORMALMAP_IDX
#if _LAYER_COUNT > 6
maxHeight = max(maxHeight, heights1.b);
#ifdef _MASKMAP3
#define _MASKMAP_IDX
#if _LAYER_COUNT > 7
maxHeight = max(maxHeight, heights1.a);
#include "../Lit/LitDataIndividualLayer.hlsl"
#undef LAYER_INDEX
#undef ADD_IDX
#undef _NORMALMAP_IDX
#undef _MASKMAP_IDX
return maxHeight;
}
#undef ADD_ZERO_IDX
#undef _BaseColorMap
#undef sampler_BaseColorMap
#undef _NormalMap
#undef ALPHA_USED_AS_SMOOTHNESS
#undef _NORMALMAP_TANGENT_SPACE_IDX
float _HeightTransition;
float3 BlendLayeredVector3(float3 x0, float3 x1, float3 x2, float3 x3, float weight[4])
// Returns layering blend mask after application of height based blend.
void ApplyHeightBlend(float4 heights0, float4 heights1, inout float4 blendMasks0, inout float4 blendMasks1)
float3 result = x0;
#if _LAYER_COUNT >= 2
result = result * weight[0] + x1 * weight[1];
// We need to mask out inactive layers so that their height does not impact the result.
float4 maskedHeights0 = heights0 * blendMasks0;
#if _LAYER_COUNT > 4
float4 maskedHeights1 = heights1 * blendMasks1;
#if _LAYER_COUNT >= 3
result += (x2 * weight[2]);
float maxHeight = GetMaxHeight(maskedHeights0
#if _LAYER_COUNT > 4
, maskedHeights1
#if _LAYER_COUNT >= 4
result += x3 * weight[3];
#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);
return result;
}
// 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.
maskedHeights0 = maskedHeights0 - 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.
maskedHeights0 = (max(0, maskedHeights0 + transition) + 1e-6) * blendMasks0;
float BlendLayeredScalar(float x0, float x1, float x2, float x3, float weight[4])
{
float result = x0;
#if _LAYER_COUNT >= 2
result = result * weight[0] + x1 * weight[1];
#if _LAYER_COUNT > 4
maskedHeights1 = maskedHeights1 - maxHeight.xxxx;
maskedHeights1 = (max(0, maskedHeights1 + transition) + 1e-6) * blendMasks1;
#if _LAYER_COUNT >= 3
result += x2 * weight[2];
// Normalize
maxHeight = GetMaxHeight(maskedHeights0
#if _LAYER_COUNT > 4
, maskedHeights1
#if _LAYER_COUNT >= 4
result += x3 * weight[3];
);
blendMasks0 = maskedHeights0 / maxHeight.xxxx;
#if _LAYER_COUNT > 4
blendMasks1 = maskedHeights1 / maxHeight.xxxx;
return result;
TEXTURE2D(_Control);
SAMPLER(sampler_Control);
#define SURFACEDATA_BLEND_VECTOR3(surfaceData, name, mask) BlendLayeredVector3(MERGE_NAME(surfaceData, 0) MERGE_NAME(., name), MERGE_NAME(surfaceData, 1) MERGE_NAME(., name), MERGE_NAME(surfaceData, 2) MERGE_NAME(., name), MERGE_NAME(surfaceData, 3) MERGE_NAME(., name), mask);
#define SURFACEDATA_BLEND_SCALAR(surfaceData, name, mask) BlendLayeredScalar(MERGE_NAME(surfaceData, 0) MERGE_NAME(., name), MERGE_NAME(surfaceData, 1) MERGE_NAME(., name), MERGE_NAME(surfaceData, 2) MERGE_NAME(., name), MERGE_NAME(surfaceData, 3) MERGE_NAME(., name), mask);
#define PROP_BLEND_SCALAR(name, mask) BlendLayeredScalar(name##0, name##1, name##2, name##3, mask);
void GetLayerTexCoord(float2 texCoord0, float2 texCoord1, float2 texCoord2, float2 texCoord3,
float3 positionWS, float3 vertexNormalWS, inout LayerTexCoord layerTexCoord)
void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
layerTexCoord.vertexNormalWS = vertexNormalWS;
layerTexCoord.triplanarWeights = ComputeTriplanarWeights(vertexNormalWS);
// Note: Blend mask have its dedicated mapping and tiling.
// To share code, we simply call the regular code from the main layer for it then save the result, then do regular call for all layers.
ComputeLayerTexCoord0( texCoord0, texCoord1, texCoord2, texCoord3, float4(1, 0, 0, 0), float4(0, 0, 0, 0),
float2(1, 1), float2(0, 0), float2(0, 0), float2(0, 0), 1.0, false,
positionWS, 1,
UV_MAPPING_UVSET, layerTexCoord);
// terrain lightmap uvs are always taken from uv0
input.texCoord1 = input.texCoord2 = input.texCoord0;
layerTexCoord.blendMask = layerTexCoord.base0;
ApplyDoubleSidedFlipOrMirror(input); // Apply double sided flip on the vertex normal
// On all layers (but not on blend mask) we can scale the tiling with object scale (only uniform supported)
// Note: the object scale doesn't affect planar/triplanar mapping as they already handle the object scale.
float tileObjectScale = 1.0;
// TODO: triplanar and SURFACE_GRADIENT?
// TODO: POM
int mappingType = UV_MAPPING_UVSET;
#if defined(_LAYER_MAPPING_PLANAR0)
mappingType = UV_MAPPING_PLANAR;
#elif defined(_LAYER_MAPPING_TRIPLANAR0)
mappingType = UV_MAPPING_TRIPLANAR;
float2 uv = input.texCoord0;
float2 uvSplats[_MAX_LAYER];
uvSplats[0] = uv * _Splat0_ST.xy + _Splat0_ST.zw;
#if _LAYER_COUNT > 1
uvSplats[1] = uv * _Splat1_ST.xy + _Splat1_ST.zw;
#endif
#if _LAYER_COUNT > 2
uvSplats[2] = uv * _Splat2_ST.xy + _Splat2_ST.zw;
#endif
#if _LAYER_COUNT > 3
uvSplats[3] = uv * _Splat3_ST.xy + _Splat3_ST.zw;
#endif
#if _LAYER_COUNT > 4
uvSplats[4] = uv * _Splat4_ST.xy + _Splat4_ST.zw;
ComputeLayerTexCoord0( texCoord0, texCoord1, texCoord2, texCoord3, float4(1, 0, 0, 0), float4(0, 0, 0, 0),
_Splat0_ST.xy, _Splat0_ST.zw, float2(0, 0), float2(0, 0), tileObjectScale, 0,
positionWS, _TexWorldScale0,
mappingType, layerTexCoord);
#if _LAYER_COUNT > 5
uvSplats[5] = uv * _Splat5_ST.xy + _Splat5_ST.zw;
#endif
#if _LAYER_COUNT > 6
uvSplats[6] = uv * _Splat6_ST.xy + _Splat6_ST.zw;
#endif
#if _LAYER_COUNT > 7
uvSplats[7] = uv * _Splat7_ST.xy + _Splat7_ST.zw;
#endif
mappingType = UV_MAPPING_UVSET;
#if defined(_LAYER_MAPPING_PLANAR1)
mappingType = UV_MAPPING_PLANAR;
#elif defined(_LAYER_MAPPING_TRIPLANAR1)
mappingType = UV_MAPPING_TRIPLANAR;
// sample weights from Control maps
float4 blendMasks0 = SAMPLE_TEXTURE2D(_Control0, sampler_Control0, uv);
#if _LAYER_COUNT > 4
float4 blendMasks1 = SAMPLE_TEXTURE2D(_Control1, sampler_Control0, uv);
#else
float4 blendMasks1 = float4(0, 0, 0, 0);
ComputeLayerTexCoord1( texCoord0, texCoord1, texCoord2, texCoord3, float4(1, 0, 0, 0), float4(0, 0, 0, 0),
_Splat1_ST.xy, _Splat1_ST.zw, float2(0, 0), float2(0, 0), tileObjectScale, 0,
positionWS, _TexWorldScale1,
mappingType, layerTexCoord);
mappingType = UV_MAPPING_UVSET;
#if defined(_LAYER_MAPPING_PLANAR2)
mappingType = UV_MAPPING_PLANAR;
#elif defined(_LAYER_MAPPING_TRIPLANAR2)
mappingType = UV_MAPPING_TRIPLANAR;
// adjust weights for density mode
#ifdef _DENSITY_MODE
// 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
float4 opacityAsDensity0 = saturate((float4(1.0, 1.0, 1.0, 1.0) - (float4(1.0, 1.0, 1.0, 1.0) - blendMasks0)) * 20.0);
float4 useOpacityAsDensityParam0 = float4(_OpacityAsDensity0, _OpacityAsDensity1, _OpacityAsDensity2, _OpacityAsDensity3);
blendMasks0 = lerp(blendMasks0, opacityAsDensity0, useOpacityAsDensityParam0);
#if _LAYER_COUNT > 4
float4 opacityAsDensity1 = saturate((float4(1.0, 1.0, 1.0, 1.0) - (float4(1.0, 1.0, 1.0, 1.0) - blendMasks0)) * 20.0);
float4 useOpacityAsDensityParam1 = float4(_OpacityAsDensity4, _OpacityAsDensity5, _OpacityAsDensity6, _OpacityAsDensity7);
blendMasks1 = lerp(blendMasks1, opacityAsDensity1, useOpacityAsDensityParam1);
#endif
ComputeLayerTexCoord2( texCoord0, texCoord1, texCoord2, texCoord3, float4(1, 0, 0, 0), float4(0, 0, 0, 0),
_Splat2_ST.xy, _Splat2_ST.zw, float2(0, 0), float2(0, 0), tileObjectScale, 0,
positionWS, _TexWorldScale2,
mappingType, layerTexCoord);
mappingType = UV_MAPPING_UVSET;
#if defined(_LAYER_MAPPING_PLANAR3)
mappingType = UV_MAPPING_PLANAR;
#elif defined(_LAYER_MAPPING_TRIPLANAR3)
mappingType = UV_MAPPING_TRIPLANAR;
#endif
ComputeLayerTexCoord3( texCoord0, texCoord1, texCoord2, texCoord3, float4(1, 0, 0, 0), float4(0, 0, 0, 0),
_Splat3_ST.xy, _Splat3_ST.zw, float2(0, 0), float2(0, 0), tileObjectScale, 0,
positionWS, _TexWorldScale3,
mappingType, layerTexCoord);
}
// adjust weights for height based blending
#ifdef _TERRAIN_HEIGHT_MAP
float4 heights0 = float4(0, 0, 0, 0);
float4 heights1 = float4(0, 0, 0, 0);
heights0.r = (SAMPLE_TEXTURE2D(_HeightMap0, sampler_Splat0, uvSplats[0]).r - _HeightCenter0) * _HeightAmplitude0;
#if _LAYER_COUNT > 1
heights0.g = (SAMPLE_TEXTURE2D(_HeightMap1, sampler_Splat0, uvSplats[1]).r - _HeightCenter1) * _HeightAmplitude1;
#endif
#if _LAYER_COUNT > 2
heights0.b = (SAMPLE_TEXTURE2D(_HeightMap2, sampler_Splat0, uvSplats[2]).r - _HeightCenter2) * _HeightAmplitude2;
#endif
#if _LAYER_COUNT > 3
heights0.a = (SAMPLE_TEXTURE2D(_HeightMap3, sampler_Splat0, uvSplats[3]).r - _HeightCenter3) * _HeightAmplitude3;
#endif
#if _LAYER_COUNT > 4
heights1.r = (SAMPLE_TEXTURE2D(_HeightMap3, sampler_Splat0, uvSplats[4]).r - _HeightCenter4) * _HeightAmplitude4;
#endif
#if _LAYER_COUNT > 5
heights1.g = (SAMPLE_TEXTURE2D(_HeightMap3, sampler_Splat0, uvSplats[5]).r - _HeightCenter5) * _HeightAmplitude5;
#endif
#if _LAYER_COUNT > 6
heights1.b = (SAMPLE_TEXTURE2D(_HeightMap3, sampler_Splat0, uvSplats[6]).r - _HeightCenter6) * _HeightAmplitude6;
#endif
#if _LAYER_COUNT > 7
heights1.a = (SAMPLE_TEXTURE2D(_HeightMap3, sampler_Splat0, uvSplats[7]).r - _HeightCenter7) * _HeightAmplitude7;
#endif
// This is call only in this file
// layerTexCoord must have been initialize to 0 outside of this function
void GetLayerTexCoord(FragInputs input, inout LayerTexCoord layerTexCoord)
{
#ifdef SURFACE_GRADIENT
GenerateLayerTexCoordBasisTB(input, layerTexCoord);
// Modify blendMask to take into account the height of the layer. Higher height should be more visible.
ApplyHeightBlend(heights0, heights1, blendMasks0, blendMasks1);
GetLayerTexCoord( input.texCoord0, input.texCoord1, input.texCoord2, input.texCoord3,
input.positionWS, input.worldToTangent[2].xyz, layerTexCoord);
}
// This function is just syntaxic sugar to nullify height not used based on heightmap avaibility and layer
void SetEnabledHeightByLayer(inout float height0, inout float height1, inout float height2, inout float height3)
{
#ifndef _HEIGHTMAP0
height0 = 0.0;
float weights[_MAX_LAYER];
ZERO_INITIALIZE_ARRAY(float, weights, _MAX_LAYER);
// calculate weight of each layers
// Algorithm is like this:
// Top layer have priority on others layers
// If a top layer doesn't use the full weight, the remaining can be use by the following layer.
float weightsSum = 0.0f;
#if _LAYER_COUNT > 7
weights[7] = min(blendMasks1.a, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[7]);
#ifndef _HEIGHTMAP1
height1 = 0.0;
#if _LAYER_COUNT > 6
weights[6] = min(blendMasks1.b, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[6]);
#ifndef _HEIGHTMAP2
height2 = 0.0;
#if _LAYER_COUNT > 5
weights[5] = min(blendMasks1.g, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[5]);
#ifndef _HEIGHTMAP3
height3 = 0.0;
#if _LAYER_COUNT > 4
weights[4] = min(blendMasks1.r, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[4]);
#if _LAYER_COUNT < 4
height3 = 0.0;
#if _LAYER_COUNT > 3
weights[3] = min(blendMasks0.a, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[3]);
#if _LAYER_COUNT < 3
height2 = 0.0;
#if _LAYER_COUNT > 2
weights[2] = min(blendMasks0.b, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[2]);
#if _LAYER_COUNT < 2
height1 = 0.0;
#if _LAYER_COUNT > 1
weights[1] = min(blendMasks0.g, (1.0f - weightsSum));
weightsSum = saturate(weightsSum + weights[1]);
}
weights[0] = min(blendMasks0.r, (1.0f - weightsSum));
void ComputeMaskWeights(float4 inputMasks, out float outWeights[_MAX_LAYER])
{
ZERO_INITIALIZE_ARRAY(float, outWeights, _MAX_LAYER);
outWeights[0] = inputMasks.r;
#if _LAYER_COUNT >= 2
outWeights[1] = inputMasks.g;
// TODO: conditional samplings
surfaceData.baseColor = SAMPLE_TEXTURE2D(_Splat0, sampler_Splat0, uvSplats[0]).rgb * weights[0];
surfaceData.perceptualSmoothness = SAMPLE_TEXTURE2D(_Splat0, sampler_Splat0, uvSplats[0]).a * _Smoothness0 * weights[0];
surfaceData.metallic = _Metallic0 * weights[0];
#if _LAYER_COUNT > 1
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat1, sampler_Splat0, uvSplats[1]).rgb * weights[1];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat1, sampler_Splat0, uvSplats[1]).a * _Smoothness1 * weights[1];
surfaceData.metallic += _Metallic1 * weights[1];
#if _LAYER_COUNT >= 3
outWeights[2] = inputMasks.b;
#if _LAYER_COUNT > 2
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat2, sampler_Splat0, uvSplats[2]).rgb * weights[2];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat2, sampler_Splat0, uvSplats[2]).a * _Smoothness2 * weights[2];
surfaceData.metallic += _Metallic2 * weights[2];
#if _LAYER_COUNT >= 4
outWeights[3] = inputMasks.a;
#if _LAYER_COUNT > 3
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat3, sampler_Splat0, uvSplats[3]).rgb * weights[3];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat3, sampler_Splat0, uvSplats[3]).a * _Smoothness3 * weights[3];
surfaceData.metallic += _Metallic3 * weights[3];
}
float4 GetBlendMask(LayerTexCoord layerTexCoord, float4 vertexColor)
{
return SAMPLE_UVMAPPING_TEXTURE2D(_Control, sampler_Control, layerTexCoord.blendMask);
}
float GetInfluenceMask(LayerTexCoord layerTexCoord, bool useLodSampling = false, float lod = 0)
{
// Sample influence mask with same mapping as Main layer
return useLodSampling ? SAMPLE_UVMAPPING_TEXTURE2D_LOD(_LayerInfluenceMaskMap, sampler_LayerInfluenceMaskMap, layerTexCoord.base0, lod).r : SAMPLE_UVMAPPING_TEXTURE2D(_LayerInfluenceMaskMap, sampler_LayerInfluenceMaskMap, layerTexCoord.base0).r;
}
float GetMaxHeight(float4 heights)
{
float maxHeight;
#if _LAYER_COUNT >= 4
maxHeight = max(Max3(heights.r, heights.g, heights.b), heights.a);
#elif _LAYER_COUNT >= 3
maxHeight = Max3(heights.r, heights.g, heights.b);
#elif _LAYER_COUNT >= 2
maxHeight = max(heights.r, heights.g);
#else
maxHeight = heights.r;
#if _LAYER_COUNT > 4
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat4, sampler_Splat0, uvSplats[4]).rgb * weights[4];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat4, sampler_Splat0, uvSplats[4]).a * _Smoothness4 * weights[4];
surfaceData.metallic += _Metallic4 * weights[4];
return maxHeight;
}
// Returns layering blend mask after application of height based blend.
float4 ApplyHeightBlend(float4 heights, float4 blendMask)
{
// We need to mask out inactive layers so that their height does not impact the result.
float4 maskedHeights = heights * blendMask.argb;
float maxHeight = GetMaxHeight(maskedHeights);
// 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.
maskedHeights = maskedHeights - 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.
maskedHeights = (max(0, maskedHeights + transition) + 1e-6) * blendMask.argb;
// Normalize
maxHeight = GetMaxHeight(maskedHeights);
maskedHeights = maskedHeights / maxHeight.xxxx;
return maskedHeights.yzwx;
}
// Calculate weights to apply to each layer
// Caution: This function must not be use for per vertex/pixel displacement, there is a dedicated function for them.
// This function handle triplanar
void ComputeLayerWeights(FragInputs input, LayerTexCoord layerTexCoord, float4 inputAlphaMask, float4 blendMasks, out float outWeights[_MAX_LAYER])
{
#if defined(_DENSITY_MODE)
float4 opacityAsDensity = saturate((inputAlphaMask - (float4(1.0, 1.0, 1.0, 1.0) - blendMasks)) * 20.0); // 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
float4 useOpacityAsDensityParam = float4(_OpacityAsDensity0, _OpacityAsDensity1, _OpacityAsDensity2, _OpacityAsDensity3);
blendMasks = lerp(blendMasks, opacityAsDensity, useOpacityAsDensityParam);
#if _LAYER_COUNT > 5
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat5, sampler_Splat0, uvSplats[5]).rgb * weights[5];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat5, sampler_Splat0, uvSplats[5]).a * _Smoothness5 * weights[5];
surfaceData.metallic += _Metallic5 * weights[5];
#endif
#if _LAYER_COUNT > 6
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat6, sampler_Splat0, uvSplats[6]).rgb * weights[6];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat6, sampler_Splat0, uvSplats[6]).a * _Smoothness6 * weights[6];
surfaceData.metallic += _Metallic6 * weights[6];
#endif
#if _LAYER_COUNT > 7
surfaceData.baseColor += SAMPLE_TEXTURE2D(_Splat7, sampler_Splat0, uvSplats[7]).rgb * weights[7];
surfaceData.perceptualSmoothness += SAMPLE_TEXTURE2D(_Splat7, sampler_Splat0, uvSplats[7]).a * _Smoothness7 * weights[7];
surfaceData.metallic += _Metallic7 * weights[7];
#if LAYERS_HEIGHTMAP_ENABLE
float height0 = (SAMPLE_UVMAPPING_TEXTURE2D(_HeightMap0, SAMPLER_HEIGHTMAP_IDX, layerTexCoord.base0).r - _HeightCenter0) * _HeightAmplitude0;
float height1 = (SAMPLE_UVMAPPING_TEXTURE2D(_HeightMap1, SAMPLER_HEIGHTMAP_IDX, layerTexCoord.base1).r - _HeightCenter1) * _HeightAmplitude1;
float height2 = (SAMPLE_UVMAPPING_TEXTURE2D(_HeightMap2, SAMPLER_HEIGHTMAP_IDX, layerTexCoord.base2).r - _HeightCenter2) * _HeightAmplitude2;
float height3 = (SAMPLE_UVMAPPING_TEXTURE2D(_HeightMap3, SAMPLER_HEIGHTMAP_IDX, layerTexCoord.base3).r - _HeightCenter3) * _HeightAmplitude3;
// Height is affected by tiling property and by object scale (depends on option).
// Apply scaling from tiling properties (TexWorldScale and tiling from BaseColor)
ApplyDisplacementTileScale(height0, height1, height2, height3);
// Nullify height that are not used, so compiler can remove unused case
SetEnabledHeightByLayer(height0, height1, height2, height3);
// Reminder: _MAIN_LAYER_INFLUENCE_MODE is a purely visual mode, it is not take into account for the blendMasks
// As it is purely visual, it is not apply in ComputeLayerWeights
#if defined(_HEIGHT_BASED_BLEND)
// Modify blendMask to take into account the height of the layer. Higher height should be more visible.
blendMasks = ApplyHeightBlend(float4(height0, height1, height2, height3), blendMasks);
#if defined(_TERRAIN_NORMAL_MAP)
UVMapping normalUV;
normalUV.mappingType = UV_MAPPING_UVSET;
#ifdef SURFACE_GRADIENT
normalUV.tangentWS = input.worldToTangent[0];
normalUV.bitangentWS = input.worldToTangent[1];
#endif
normalUV.uv = uvSplats[0];
float3 normalTS = SAMPLE_UVMAPPING_NORMALMAP(_Normal0, sampler_Splat0, normalUV, 1) * weights[0];
#if _LAYER_COUNT > 1
normalUV.uv = uvSplats[1];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal1, sampler_Splat0, normalUV, 1) * weights[1];
#if _LAYER_COUNT > 2
normalUV.uv = uvSplats[2];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal2, sampler_Splat0, normalUV, 1) * weights[2];
#endif
#if _LAYER_COUNT > 3
normalUV.uv = uvSplats[3];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal3, sampler_Splat0, normalUV, 1) * weights[3];
#endif
#if _LAYER_COUNT > 4
normalUV.uv = uvSplats[4];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal4, sampler_Splat0, normalUV, 1) * weights[4];
#endif
#if _LAYER_COUNT > 5
normalUV.uv = uvSplats[5];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal5, sampler_Splat0, normalUV, 1) * weights[5];
#endif
#if _LAYER_COUNT > 6
normalUV.uv = uvSplats[6];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal6, sampler_Splat0, normalUV, 1) * weights[6];
#endif
#if _LAYER_COUNT > 7
normalUV.uv = uvSplats[7];
normalTS += SAMPLE_UVMAPPING_NORMALMAP(_Normal7, sampler_Splat0, normalUV, 1) * weights[7];
#endif
#elif defined(SURFACE_GRADIENT)
float3 normalTS = float3(0.0, 0.0, 0.0); // No gradient
#else
float3 normalTS = float3(0.0, 0.0, 1.0);
ComputeMaskWeights(blendMasks, outWeights);
}
#include "../LayeredLit/LayeredLitDataDisplacement.hlsl"
#include "../Lit/LitBuiltinData.hlsl"
void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
// terrain lightmap uvs are always taken from uv0
input.texCoord1 = input.texCoord2 = input.texCoord0;
ApplyDoubleSidedFlipOrMirror(input); // Apply double sided flip on the vertex normal
LayerTexCoord layerTexCoord;
ZERO_INITIALIZE(LayerTexCoord, layerTexCoord);
GetLayerTexCoord(input, layerTexCoord);
float4 blendMasks = GetBlendMask(layerTexCoord, input.color);
SurfaceData surfaceData0, surfaceData1, surfaceData2, surfaceData3;
float3 normalTS0, normalTS1, normalTS2, normalTS3;
float3 bentNormalTS0, bentNormalTS1, bentNormalTS2, bentNormalTS3;
GetSurfaceData0(input, layerTexCoord, surfaceData0, normalTS0, bentNormalTS0);
GetSurfaceData1(input, layerTexCoord, surfaceData1, normalTS1, bentNormalTS1);
GetSurfaceData2(input, layerTexCoord, surfaceData2, normalTS2, bentNormalTS2);
GetSurfaceData3(input, layerTexCoord, surfaceData3, normalTS3, bentNormalTS3);
// Note: If per pixel displacement is enabled it mean we will fetch again the various heightmaps at the intersection location. Not sure the compiler can optimize.
float weights[_MAX_LAYER];
ComputeLayerWeights(input, layerTexCoord, float4(1, 1, 1, 1), blendMasks, weights);
float3 normalTS;
float3 bentNormalWS;
surfaceData.baseColor = SURFACEDATA_BLEND_VECTOR3(surfaceData, baseColor, weights);
normalTS = BlendLayeredVector3(normalTS0, normalTS1, normalTS2, normalTS3, weights);
surfaceData.perceptualSmoothness = SURFACEDATA_BLEND_SCALAR(surfaceData, perceptualSmoothness, weights);
surfaceData.ambientOcclusion = SURFACEDATA_BLEND_SCALAR(surfaceData, ambientOcclusion, weights);
surfaceData.metallic = SURFACEDATA_BLEND_SCALAR(surfaceData, metallic, weights);
surfaceData.ambientOcclusion = 1;
surfaceData.tangentWS = normalize(input.worldToTangent[0].xyz); // The tangent is not normalize in worldToTangent for mikkt. Tag: SURFACE_GRADIENT
surfaceData.subsurfaceMask = 0;
surfaceData.thickness = 1;
surfaceData.transmittanceMask = 0.0;
GetNormalWS(input, V, normalTS, surfaceData.normalWS);
bentNormalWS = surfaceData.normalWS;
float3 bentNormalWS = surfaceData.normalWS;
#if defined(_MASKMAP0) || defined(_MASKMAP1) || defined(_MASKMAP2) || defined(_MASKMAP3)
surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(dot(surfaceData.normalWS, V), surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
#else
// #if defined(_MASKMAP0) || defined(_MASKMAP1) || defined(_MASKMAP2) || defined(_MASKMAP3)
// surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(dot(surfaceData.normalWS, V), surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
// #else
#endif
// #endif
#ifndef _DISABLE_DBUFFER
float alpha = 1;