1320 lines
52 KiB
HLSL
1320 lines
52 KiB
HLSL
#ifndef TERRIAN_TWO_SAMPLE_INCLUDED
|
|
#define TERRIAN_TWO_SAMPLE_INCLUDED
|
|
|
|
//Return random value from float2
|
|
float2 hash(float2 p)
|
|
{
|
|
return frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), p)) * 43758.5453);
|
|
}
|
|
|
|
//Returns terrain uv in world space
|
|
void getWorldPosTerrainUV_float(float3 worldPos, float2 terrainPosition, float2 terrainSize, out float2 uv)
|
|
{
|
|
float2 pos = worldPos.xz - terrainPosition;
|
|
uv = pos / terrainSize;
|
|
}
|
|
|
|
//Returns terrain uv in world space
|
|
float2 worldPosTerrainUV(float2 worldPos, float2 terrainPosition, float2 terrainSize)
|
|
{
|
|
float2 pos = worldPos - terrainPosition;
|
|
return pos / terrainSize;
|
|
}
|
|
|
|
//Samples the terrain world normal texture
|
|
float3 worldSpaceTerrainNormal(float2 uv)
|
|
{
|
|
return SAMPLE_TEXTURE2D(_WorldNormalMap, SamplerState_Linear_Clamp, uv).xyz * 2 - 1;
|
|
}
|
|
|
|
//Samples the terrain world normal texture
|
|
float3 worldSpaceTerrainNormalLOD(float2 uv, float lod)
|
|
{
|
|
return SAMPLE_TEXTURE2D_LOD(_WorldNormalMap, SamplerState_Linear_Clamp, uv, lod).xyz * 2 - 1;
|
|
}
|
|
|
|
//Blends the tangent space normals into the terrain world normal
|
|
float3 worldSpaceNormalBlend(float3 terrainNormal, float3 surfaceNormal)
|
|
{
|
|
float3 terrainTangent = cross(terrainNormal, float3(0, 0, 1));
|
|
float3 terrainBitangent = cross(terrainTangent, terrainNormal);
|
|
return surfaceNormal.x * terrainTangent + surfaceNormal.y * terrainBitangent + surfaceNormal.z * terrainNormal;
|
|
}
|
|
|
|
|
|
#ifdef _ALPHATEST_ON
|
|
TEXTURE2D(_TerrainHolesTexture);
|
|
SAMPLER(sampler_TerrainHolesTexture);
|
|
#endif
|
|
|
|
#ifdef _ALPHATEST_ON
|
|
void clipHoles(float2 uv, out float alpha)
|
|
{
|
|
float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, sampler_TerrainHolesTexture, uv).r;
|
|
alpha = (hole == 0.0f ? -1 : 1);
|
|
}
|
|
#endif
|
|
|
|
void GTSTerrainHoles_float(float3 worldPos, out float alpha)
|
|
{
|
|
#ifdef _ALPHATEST_ON
|
|
float2 terrainUV = worldPosTerrainUV(worldPos.xz, _TerrainPosSize.xy, _TerrainPosSize.zw);
|
|
clipHoles(terrainUV, alpha);
|
|
#else
|
|
alpha = 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
//Samples the layer tiling
|
|
float2 layerUVs(float2 uv, float4 layerST)
|
|
{
|
|
return uv * layerST.xy + layerST.zw;
|
|
}
|
|
|
|
//Remaps a float value
|
|
float RemapFloat(float value, float from1, float to1, float from2, float to2)
|
|
{
|
|
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
|
|
}
|
|
|
|
//Remaps a float2 value
|
|
float2 RemapFloat2(float2 value, float2 from1, float2 to1, float2 from2, float2 to2)
|
|
{
|
|
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
|
|
}
|
|
|
|
//Remaps a float4 value
|
|
float4 RemapFloat4(float4 value, float4 from1, float4 to1, float4 from2, float4 to2)
|
|
{
|
|
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
|
|
}
|
|
|
|
// Approximate version from: https://hal.inria.fr/hal-01824773/document
|
|
void TriangleGrid(float2 uv,
|
|
out float w1, out float w2, out float w3,
|
|
out float2 vertex1, out float2 vertex2, out float2 vertex3)
|
|
{
|
|
uv = uv * 3.464;
|
|
|
|
const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054);
|
|
float2 skewedCoord = mul(gridToSkewedGrid, uv);
|
|
|
|
int2 baseId = int2(floor(skewedCoord));
|
|
float3 temp = float3(frac(skewedCoord), 0);
|
|
temp.z = 1.0 - temp.x - temp.y;
|
|
if (temp.z > 0.0)
|
|
{
|
|
w1 = temp.z;
|
|
w2 = temp.y;
|
|
w3 = temp.x;
|
|
vertex1 = baseId;
|
|
vertex2 = baseId + int2(0, 1);
|
|
vertex3 = baseId + int2(1, 0);
|
|
}
|
|
else
|
|
{
|
|
w1 = -temp.z;
|
|
w2 = 1.0 - temp.y;
|
|
w3 = 1.0 - temp.x;
|
|
vertex1 = baseId + int2(1, 1);
|
|
vertex2 = baseId + int2(1, 0);
|
|
vertex3 = baseId + int2(0, 1);
|
|
}
|
|
}
|
|
|
|
//Samples the packed albedo and normal maps, based on whether it's triplanar or not
|
|
void SampleLayerTexture(float3 worldPos, float3 worldNormal, int index,
|
|
float2 uv, float4 derivData, float4 triPlanarData,
|
|
float4 remapData, float4 heightData, float4 colorData, float4 layerDataA,
|
|
out float4 sampledAlbedoPacked, out float4 sampledNormalPacked)
|
|
{
|
|
|
|
UNITY_BRANCH
|
|
if (triPlanarData.z)
|
|
{
|
|
//Approximate version from: https://github.com/bgolus/Normal-Mapping-for-a-Triplanar-Shader/blob/master/TriplanarSwizzle.shader
|
|
float3 triplanarBlend = pow(abs(worldNormal), float3(4,15,4));
|
|
triplanarBlend /= max(dot(triplanarBlend, float3(1, 1, 1)), 0.0001);
|
|
|
|
float2 triPlanarSize = triPlanarData.xy;
|
|
|
|
//Sample world normal axis planes
|
|
float2 uvX = worldPos.zy / triPlanarSize;
|
|
float2 uvY = worldPos.xz / triPlanarSize;
|
|
float2 uvZ = worldPos.xy / triPlanarSize;
|
|
|
|
//Slight offset to prevent mirroring
|
|
uvY += 0.33;
|
|
uvZ += 0.67;
|
|
|
|
//Flip normals if the world normal is negative
|
|
float3 axisSign = worldNormal < 0 ? -1 : 1;
|
|
|
|
//flip tangents based on the axis
|
|
uvX.x *= axisSign.x;
|
|
uvY.x *= axisSign.y;
|
|
uvZ.x *= -axisSign.z;
|
|
|
|
//Sample the albedo color
|
|
float4 albedoX = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvX, index, derivData.xy, derivData.zw);
|
|
float4 albedoY = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvY, index, derivData.xy, derivData.zw);
|
|
float4 albedoZ = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvZ, index, derivData.xy, derivData.zw);
|
|
sampledAlbedoPacked = albedoX * triplanarBlend.x + albedoY * triplanarBlend.y + albedoZ * triplanarBlend.z;
|
|
sampledAlbedoPacked.rgb *= colorData.rgb;
|
|
sampledAlbedoPacked.a = pow(abs(sampledAlbedoPacked.a), heightData.x) * heightData.y + heightData.z;
|
|
|
|
//Sample the normal packed
|
|
float4 normalX = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvX, index, derivData.xy, derivData.zw);
|
|
float4 normalY = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvY, index, derivData.xy, derivData.zw);
|
|
float4 normalZ = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvZ, index, derivData.xy, derivData.zw);
|
|
|
|
sampledNormalPacked = normalX * triplanarBlend.x + normalY * triplanarBlend.y + normalZ * triplanarBlend.z;
|
|
sampledNormalPacked.xy = sampledNormalPacked.xy * 2 - 1;
|
|
sampledNormalPacked.xy *= layerDataA.z;
|
|
sampledNormalPacked.zw = RemapFloat2(sampledNormalPacked.zw, float2(0, 0), float2(1, 1), remapData.xy, remapData.zw);
|
|
|
|
}
|
|
else
|
|
{
|
|
UNITY_BRANCH
|
|
if ((int)layerDataA.y)
|
|
{
|
|
float w1, w2, w3;
|
|
float2 vertex1, vertex2, vertex3;
|
|
TriangleGrid(uv, w1, w2, w3, vertex1, vertex2, vertex3);
|
|
|
|
float2 uv1 = uv + hash(vertex1);
|
|
float2 uv2 = uv + hash(vertex2);
|
|
float2 uv3 = uv + hash(vertex3);
|
|
|
|
float4 albedo1 = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uv1, index, derivData.xy, derivData.zw);
|
|
float4 albedo2 = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uv2, index, derivData.xy, derivData.zw);
|
|
float4 albedo3 = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uv3, index, derivData.xy, derivData.zw);
|
|
|
|
sampledAlbedoPacked = (w1 * albedo1) + (w2 * albedo2) + (w3 * albedo3);
|
|
sampledAlbedoPacked.rgb *= colorData.rgb;
|
|
sampledAlbedoPacked.a = pow(abs(sampledAlbedoPacked.a), heightData.x) * heightData.y + heightData.z;
|
|
|
|
float4 normal1 = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uv1, index, derivData.xy, derivData.zw);
|
|
float4 normal2 = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uv2, index, derivData.xy, derivData.zw);
|
|
float4 normal3 = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uv3, index, derivData.xy, derivData.zw);
|
|
|
|
sampledNormalPacked = (w1 * normal1) + (w2 * normal2) + (w3 * normal3);
|
|
sampledNormalPacked.xy = sampledNormalPacked.xy * 2 - 1;
|
|
sampledNormalPacked.xy *= layerDataA.z;
|
|
sampledNormalPacked.zw = RemapFloat2(sampledNormalPacked.zw, float2(0, 0), float2(1, 1), remapData.xy, remapData.zw);
|
|
}
|
|
else
|
|
{
|
|
//Get Albedo
|
|
sampledAlbedoPacked = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uv, index, derivData.xy, derivData.zw);
|
|
sampledAlbedoPacked.rgb *= colorData.rgb;
|
|
sampledAlbedoPacked.a = pow(abs(sampledAlbedoPacked.a), heightData.x) * heightData.y + heightData.z;
|
|
|
|
//Get Normal
|
|
sampledNormalPacked = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uv, index, derivData.xy, derivData.zw);
|
|
sampledNormalPacked.xy = sampledNormalPacked.xy * 2 - 1;
|
|
sampledNormalPacked.xy *= layerDataA.z;
|
|
sampledNormalPacked.zw = RemapFloat2(sampledNormalPacked.zw, float2(0, 0), float2(1, 1), remapData.xy, remapData.zw);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void SampleLayerDisplacement(float3 worldPos, float3 worldNormal, int indexLOD,
|
|
float2 uvLOD, float4 triPlanarData,
|
|
float4 displacementData, float4 heightData, float4 layerDataA,
|
|
out float2 displacementTessellation, out float heightLOD)
|
|
{
|
|
displacementTessellation = float2(0, 0);
|
|
heightLOD = 0;
|
|
|
|
|
|
UNITY_BRANCH
|
|
if (triPlanarData.z)
|
|
{
|
|
//Approximate version from: https://github.com/bgolus/Normal-Mapping-for-a-Triplanar-Shader/blob/master/TriplanarSwizzle.shader
|
|
float3 triplanarBlend = pow(abs(worldNormal), float3(4,15,4));
|
|
triplanarBlend /= max(dot(triplanarBlend, float3(1, 1, 1)), 0.0001);
|
|
|
|
float2 triPlanarSize = triPlanarData.xy;
|
|
|
|
//Sample world normal axis planes
|
|
float2 uvX = worldPos.zy / triPlanarSize;
|
|
float2 uvY = worldPos.xz / triPlanarSize;
|
|
float2 uvZ = worldPos.xy / triPlanarSize;
|
|
|
|
//Slight offset to prevent mirroring
|
|
uvY += 0.33;
|
|
uvZ += 0.67;
|
|
|
|
//Flip normals if the world normal is negative
|
|
float3 axisSign = worldNormal < 0 ? -1 : 1;
|
|
|
|
//flip tangents based on the axis
|
|
uvX.x *= axisSign.x;
|
|
uvY.x *= axisSign.y;
|
|
uvZ.x *= -axisSign.z;
|
|
|
|
//Get Displacement
|
|
float displacementX = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uvX, indexLOD, 0).a;
|
|
float displacementY = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uvY, indexLOD, 0).a;
|
|
float displacementZ = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uvZ, indexLOD, 0).a;
|
|
float displacement = displacementX * triplanarBlend.x + displacementY * triplanarBlend.y + displacementZ * triplanarBlend.z;
|
|
displacement = pow(abs(displacement), displacementData.x) * displacementData.y + displacementData.z;
|
|
heightLOD = pow(abs(displacement), heightData.x) * heightData.y + heightData.z;
|
|
displacementTessellation = float2(displacement, displacementData.a);
|
|
}
|
|
else
|
|
{
|
|
UNITY_BRANCH
|
|
if ((int)layerDataA.y)
|
|
{
|
|
float w1, w2, w3;
|
|
float2 vertex1, vertex2, vertex3;
|
|
TriangleGrid(uvLOD, w1, w2, w3, vertex1, vertex2, vertex3);
|
|
|
|
float2 uv1 = uvLOD + hash(vertex1);
|
|
float2 uv2 = uvLOD + hash(vertex2);
|
|
float2 uv3 = uvLOD + hash(vertex3);
|
|
|
|
float displacement1 = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uv1, indexLOD, 0).a;
|
|
float displacement2 = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uv2, indexLOD, 0).a;
|
|
float displacement3 = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uv3, indexLOD, 0).a;
|
|
|
|
float displacement = (w1 * displacement1) + (w2 * displacement2) + (w3 * displacement3);
|
|
displacement = pow(abs(displacement), displacementData.x) * displacementData.y + displacementData.z;
|
|
heightLOD = pow(abs(displacement), heightData.x) * heightData.y + heightData.z;
|
|
displacementTessellation = float2(displacement, displacementData.a);
|
|
}
|
|
else
|
|
{
|
|
|
|
//Get Displacement
|
|
float displacement = SAMPLE_TEXTURE2D_ARRAY_LOD(_AlbedoArray, sampler_AlbedoArray, uvLOD, indexLOD, 0).a;
|
|
heightLOD = pow(abs(displacement), heightData.x) * heightData.y + heightData.z;
|
|
displacement = pow(abs(displacement), displacementData.x) * displacementData.y + displacementData.z;
|
|
displacementTessellation = float2(displacement, displacementData.a);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
float3 UnpackNormalmapRGAG(float4 packednormal)
|
|
{
|
|
packednormal.x *= packednormal.w;
|
|
float3 normal;
|
|
normal.xy = packednormal.xy * 2 - 1;
|
|
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
|
|
return normal;
|
|
}
|
|
|
|
float3 UnpackNormalTangent(float4 packednormal)
|
|
{
|
|
#if defined(UNITY_NO_DXT5nm)
|
|
return packednormal.xyz * 2 - 1;
|
|
#else
|
|
return UnpackNormalmapRGAG(packednormal);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//--Global Weather Data
|
|
//Snow
|
|
#ifdef _SNOW_ON
|
|
TEXTURE2D(_PW_SnowAlbedoMap);
|
|
TEXTURE2D(_PW_SnowNormalMap);
|
|
TEXTURE2D(_PW_SnowMaskMap);
|
|
float4 _PW_SnowDataA;
|
|
float4 _PW_SnowDataB;
|
|
float4 _PW_SnowColor;
|
|
float4 _PW_SnowMaskRemapMin;
|
|
float4 _PW_SnowMaskRemapMax;
|
|
float4 _PW_SnowHeightData;
|
|
float4 _PW_SnowDisplacementData;
|
|
#endif
|
|
|
|
//Rain
|
|
#ifdef _RAIN_ON
|
|
float4 _PW_RainDataA;
|
|
float4 _PW_RainDataB;
|
|
TEXTURE2D(_PW_RainMap);
|
|
#endif
|
|
|
|
#ifdef _RAIN_ON
|
|
float generateRain(float pointSample, float speed, float offset)
|
|
{
|
|
float time = frac((_Time.y * speed) + offset);
|
|
float rainGen = sin(lerp(0, -20, time) * pointSample);
|
|
float timeMask = sin(time * 3.1415);
|
|
|
|
return saturate(rainGen * timeMask);
|
|
}
|
|
|
|
float sampleRain(float2 uv, float4 pointData)
|
|
{
|
|
float rainA = generateRain(pointData.r, _PW_RainDataB.r, 0);
|
|
float rainB = generateRain(pointData.g, _PW_RainDataB.r, 0.5);
|
|
float rainC = generateRain(pointData.b, _PW_RainDataB.r, 0.75);
|
|
float rainD = generateRain(pointData.a, _PW_RainDataB.r, 0.25);
|
|
|
|
return saturate(rainA + rainB + rainC + rainD);
|
|
}
|
|
|
|
float3 getRainNormals(in float3 worldPosition)
|
|
{
|
|
float2 rainCenterUVs = worldPosition.xz / _PW_RainDataB.a;
|
|
float4 centerRainMap = SAMPLE_TEXTURE2D(_PW_RainMap, SamplerState_Linear_Repeat, rainCenterUVs);
|
|
|
|
float2 rainXDerivUVs = (worldPosition.xz / _PW_RainDataB.a) + float2(0.001, 0);
|
|
float4 xDerivRainMap = SAMPLE_TEXTURE2D(_PW_RainMap, SamplerState_Linear_Repeat, rainXDerivUVs);
|
|
|
|
float2 rainYDerivUVs = (worldPosition.xz / _PW_RainDataB.a) + float2(0, 0.001);
|
|
float4 yDerivRainMap = SAMPLE_TEXTURE2D(_PW_RainMap, SamplerState_Linear_Repeat, rainYDerivUVs);
|
|
|
|
//Sample center rain
|
|
float centerRain = sampleRain(rainCenterUVs, centerRainMap);
|
|
float xDerivRain = sampleRain(rainXDerivUVs, xDerivRainMap);
|
|
float yDerivRain = sampleRain(rainYDerivUVs, yDerivRainMap);
|
|
|
|
float xDeriv = centerRain - xDerivRain;
|
|
float yDeriv = centerRain - yDerivRain;
|
|
|
|
float3 xVector = float3(xDeriv, 1, 0);
|
|
float3 yVector = float3(0, 1, yDeriv);
|
|
|
|
return cross(xVector, yVector);
|
|
}
|
|
#endif
|
|
|
|
//Sand
|
|
|
|
#define MAXARRAYELEMENTS 8
|
|
#if !defined(_CREATE_ARRAYS_ON)
|
|
uniform float4 _HeightData[MAXARRAYELEMENTS];
|
|
uniform float4 _UVData[MAXARRAYELEMENTS];
|
|
uniform float4 _MaskMapRemapData[MAXARRAYELEMENTS];
|
|
uniform float4 _ColorData[MAXARRAYELEMENTS];
|
|
uniform float4 _TriPlanarData[MAXARRAYELEMENTS];
|
|
uniform float4 _DisplacementData[MAXARRAYELEMENTS];
|
|
uniform float4 _LayerDataA[MAXARRAYELEMENTS];
|
|
#endif
|
|
|
|
float snowHeightBlend(float heightA, float heightB, float mask, float blendFactor)
|
|
{
|
|
float t = saturate(mask);
|
|
t -= 0.2;
|
|
heightA = heightA * (1 - t);
|
|
heightB = heightB * t;
|
|
|
|
float heightStart = max(heightA, heightB) - blendFactor;
|
|
float level1 = max(heightA - heightStart, 0);
|
|
float level2 = max(heightB - heightStart, 0);
|
|
|
|
return saturate((0 * level1) + (1 * level2)) / (level1 + level2);
|
|
}
|
|
|
|
void GTSVertexData_float(float3 worldPos, out float globalBlendDistance, out float nearCameraDistance)
|
|
{
|
|
//Calculate camera distance
|
|
float3 CameraToWorldDirection = _WorldSpaceCameraPos.xyz - worldPos;
|
|
float squareMag = dot(CameraToWorldDirection, CameraToWorldDirection);
|
|
nearCameraDistance = 1 - saturate(squareMag * 0.000001);
|
|
|
|
globalBlendDistance = saturate(pow(saturate(squareMag * _GlobalBlendData.x), _GlobalBlendData.y));
|
|
}
|
|
|
|
void BlendOverlay(float3 Base, float3 Blend, float Opacity, out float3 Out)
|
|
{
|
|
float3 result1 = 1.0 - 2.0 * (1.0 - Base) * (1.0 - Blend);
|
|
float3 result2 = 2.0 * Base * Blend;
|
|
float3 zeroOrOne = step(Base, 0.5);
|
|
Out = result2 * zeroOrOne + (1 - zeroOrOne) * result1;
|
|
Out = lerp(Base, Out, Opacity);
|
|
}
|
|
|
|
void SampleBlendedLayers(float2 terrainUV, float2 controlUV, float2 rootUV, float2 bilinearWeights, float3 worldPos, float weightSplats[8],
|
|
float2 dxuv, float2 dyuv, float3 worldNormal, float blendDist,
|
|
out float4 albedoPackedBlend, out float4 normalPackedBlend, out float blendedGeoLayerStrength, out float blendedDetailLayerStrength)
|
|
{
|
|
blendedGeoLayerStrength = 0;
|
|
blendedDetailLayerStrength = 0;
|
|
|
|
float4 albedoA = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendA = float4(0, 0, 1, 0);
|
|
float4 albedoB = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendB = float4(0, 0, 1, 0);
|
|
float4 albedoC = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendC = float4(0, 0, 1, 0);
|
|
float4 albedoD = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendD = float4(0, 0, 1, 0);
|
|
float4 splatControl = float4(0, 0, 0, 0);
|
|
|
|
int currentIndex = 0;
|
|
float4 uvData;
|
|
float2 layerUV;
|
|
float4 deriv;
|
|
|
|
//Sample top 4 splats
|
|
//SAMPLE WITH SPLAT TEXEL REMAP INSTEAD?
|
|
float4 splatmapIndexed = SAMPLE_TEXTURE2D_LOD(_SplatmapIndex, SamplerState_Point_Clamp, terrainUV, 0) * 256;
|
|
|
|
//Layer A
|
|
currentIndex = round(splatmapIndexed.x);
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
float4 detailStrengthControl = float4(0, 0, 0, 0);
|
|
#endif
|
|
#ifdef _GEOLOGICAL_ON
|
|
float4 geoStrengthControl = float4(0, 0, 0, 0);
|
|
#endif
|
|
UNITY_BRANCH
|
|
|
|
if (weightSplats[currentIndex] > 0)
|
|
{
|
|
float4 layerDataA_A = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(rootUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_A,
|
|
albedoA, normalPackedBlendA);
|
|
//Set new splat weights Geo
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.x = layerDataA_A.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.x = layerDataA_A.w;
|
|
#endif
|
|
}
|
|
|
|
//Layer A
|
|
//Set new splat weights
|
|
splatControl.x = (weightSplats[currentIndex]);
|
|
|
|
|
|
//Layer B
|
|
currentIndex = round(splatmapIndexed.y);
|
|
UNITY_BRANCH
|
|
|
|
if (weightSplats[currentIndex] > 0)
|
|
{
|
|
float4 layerDataA_B = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(rootUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
float heightLODB;
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_B,
|
|
albedoB, normalPackedBlendB);
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.y = layerDataA_B.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.y = layerDataA_B.w;
|
|
#endif
|
|
}
|
|
|
|
//Set new splat weights
|
|
splatControl.y = (weightSplats[currentIndex]);
|
|
|
|
//Set new splat weights Geo
|
|
|
|
//Layer C
|
|
currentIndex = round(splatmapIndexed.z);
|
|
UNITY_BRANCH
|
|
|
|
if (weightSplats[currentIndex] > 0)
|
|
{
|
|
float4 layerDataA_C = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(rootUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_C,
|
|
albedoC, normalPackedBlendC);
|
|
//Set new splat weights Geo
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.z = layerDataA_C.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.z = layerDataA_C.w;
|
|
#endif
|
|
}
|
|
|
|
//Set new splat weights
|
|
splatControl.z = (weightSplats[currentIndex]);
|
|
|
|
|
|
|
|
//Layer D
|
|
currentIndex = round(splatmapIndexed.w);
|
|
UNITY_BRANCH
|
|
|
|
if (weightSplats[currentIndex] > 0)
|
|
{
|
|
float4 layerDataA_D = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(rootUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_D,
|
|
albedoD, normalPackedBlendD);
|
|
|
|
//Set new splat weights Geo
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.w = layerDataA_D.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.w = layerDataA_D.w;
|
|
#endif
|
|
}
|
|
|
|
|
|
//Set new splat weights
|
|
splatControl.w = (weightSplats[currentIndex]);
|
|
|
|
|
|
|
|
#ifdef _HEIGHT_BLEND_ON
|
|
//Height blending splat
|
|
float masks[] = { albedoA.a, albedoB.a, albedoC.a, albedoD.a };
|
|
float4 splatHeight = float4(masks[0], masks[1], masks[2], masks[3]) * splatControl.rgba;
|
|
float maxHeight = max(splatHeight.r, max(splatHeight.g, max(splatHeight.b, splatHeight.a)));
|
|
float heightTransition = _BlendFactor;
|
|
float transition = max(heightTransition, 1e-5);
|
|
half4 weightedHeights = splatHeight + transition - maxHeight.xxxx;
|
|
weightedHeights = max(0, weightedHeights);
|
|
weightedHeights = (weightedHeights + 1e-6) * splatControl;
|
|
float sumHeight = max(dot(weightedHeights, float4(1, 1, 1, 1)), 1e-6);
|
|
splatControl = weightedHeights / sumHeight.xxxx;
|
|
#endif
|
|
albedoPackedBlend = albedoA * splatControl.x + albedoB * splatControl.y + albedoC * splatControl.z + albedoD * splatControl.w;
|
|
normalPackedBlend = normalPackedBlendA * splatControl.x + normalPackedBlendB * splatControl.y + normalPackedBlendC * splatControl.z + normalPackedBlendD * splatControl.a;
|
|
|
|
//Blend Geo Layer Strengths
|
|
#ifdef _GEOLOGICAL_ON
|
|
blendedGeoLayerStrength = geoStrengthControl.x * splatControl.x + geoStrengthControl.y * splatControl.y + geoStrengthControl.z * splatControl.z + geoStrengthControl.w * splatControl.w;
|
|
blendedGeoLayerStrength = lerp(0, blendedGeoLayerStrength, blendDist);
|
|
#endif
|
|
//Blend Detail Layer Strengths
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
blendedDetailLayerStrength = detailStrengthControl.x * splatControl.x + detailStrengthControl.y * splatControl.y + detailStrengthControl.z * splatControl.z + detailStrengthControl.w * splatControl.w;
|
|
blendedDetailLayerStrength = lerp(1, blendedDetailLayerStrength, blendDist);
|
|
#endif
|
|
}
|
|
|
|
void GTSLayers_float(float2 terrainUV, float3 worldPos, float3 worldNormal, float2 splatTexelSize,
|
|
float globalBlendDistance, float nearCameraDistance,
|
|
out float3 blendedAlbedo, out float3 blendedNormal, out float4 blendedMask)
|
|
{
|
|
#ifdef UNITY_INSTANCING_ENABLED
|
|
//Calculate camera distance
|
|
float3 CameraToWorldDirection = _WorldSpaceCameraPos.xyz - worldPos;
|
|
float squareMag = dot(CameraToWorldDirection, CameraToWorldDirection);
|
|
nearCameraDistance = 1 - saturate(squareMag * 0.000001);
|
|
globalBlendDistance = saturate(pow(saturate(squareMag * _GlobalBlendData.x), _GlobalBlendData.y));
|
|
#endif
|
|
|
|
float3 worldToObjPos = mul(UNITY_MATRIX_I_M, float4(worldPos, 1.0)).xyz;
|
|
|
|
//Create Terrain World UVs
|
|
#ifdef UNITY_INSTANCING_ENABLED
|
|
terrainUV = worldPosTerrainUV(worldPos.xz, _TerrainPosSize.xy, _TerrainPosSize.zw);
|
|
#else
|
|
terrainUV = _WorldAlignedUVs == 1 ? worldPosTerrainUV(worldPos.xz, _TerrainPosSize.xy, _TerrainPosSize.zw) : terrainUV;
|
|
#endif
|
|
|
|
float2 sampleCoords = (terrainUV / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy;
|
|
|
|
//Sample Terrain splatmaps, used for weighting
|
|
float2 controlUV;
|
|
controlUV = (terrainUV * (splatTexelSize - 1) + 0.5) * (1 / splatTexelSize);
|
|
float4 splat = SAMPLE_TEXTURE2D(_Control0, SamplerState_Linear_Clamp, controlUV);
|
|
#ifndef _MOBILE_VR_ON
|
|
float4 splat1 = SAMPLE_TEXTURE2D(_Control1, SamplerState_Linear_Clamp, controlUV);
|
|
#endif
|
|
|
|
//Sample terrain normal map
|
|
float3 worldTerrainNormal = SAMPLE_TEXTURE2D(_WorldNormalMap, SamplerState_Linear_Clamp, sampleCoords).rgb * 2 - 1;
|
|
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
|
|
float3 detailWorldPos = _ObjectSpaceDataA.x == 0 ? worldPos : worldToObjPos;
|
|
|
|
//Get Detail UVs
|
|
float2 detailNearUV = detailWorldPos.xz / _DetailNearFarData.x;
|
|
float2 detailFarUV = detailWorldPos.xz / _DetailNearFarData.z;
|
|
|
|
//Sample Textures
|
|
float4 detailNearNormalData = SAMPLE_TEXTURE2D(_DetailNormal, SamplerState_Linear_Repeat, detailNearUV);
|
|
detailNearNormalData.xy = (detailNearNormalData.xy * 2 - 1) * _DetailNearFarData.y;
|
|
float4 detailFarNormalData = SAMPLE_TEXTURE2D(_DetailNormal, SamplerState_Linear_Repeat, detailFarUV);
|
|
detailFarNormalData.xy = (detailFarNormalData.xy * 2 - 1) * _DetailNearFarData.w;
|
|
|
|
//Blend Near and Far Normals
|
|
float4 blendedDetailNormals = lerp(detailNearNormalData, detailFarNormalData, globalBlendDistance);
|
|
float detailNormalGreyscale = saturate(dot(blendedDetailNormals.xy, blendedDetailNormals.xy));
|
|
float blendedDetailLayerStrength = 1;
|
|
|
|
#endif
|
|
|
|
#ifndef _MOBILE_VR_ON
|
|
//Declare weight splat array
|
|
|
|
|
|
float2 weightData[] =
|
|
{
|
|
float2(splat.r, 0),
|
|
float2(splat.g, 1),
|
|
float2(splat.b, 2),
|
|
float2(splat.a, 3),
|
|
float2(splat1.r, 4),
|
|
float2(splat1.g, 5),
|
|
float2(splat1.b, 6),
|
|
float2(splat1.a, 7)
|
|
};
|
|
|
|
// Unrolled bubble sort for 8 items
|
|
#define SWAP_IF_GREATER(i, j) \
|
|
if (weightData[i].x < weightData[j].x) { \
|
|
float2 temp = weightData[i]; \
|
|
weightData[i] = weightData[j]; \
|
|
weightData[j] = temp; \
|
|
}
|
|
|
|
// Pass 1
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
SWAP_IF_GREATER(4, 5);SWAP_IF_GREATER(5, 6);SWAP_IF_GREATER(6, 7);
|
|
// Pass 2
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
SWAP_IF_GREATER(4, 5);SWAP_IF_GREATER(5, 6);
|
|
// Pass 3
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
SWAP_IF_GREATER(4, 5);
|
|
// Pass 4
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
// Pass 5
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);
|
|
// Pass 6
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);
|
|
// Pass 7
|
|
SWAP_IF_GREATER(0, 1);
|
|
|
|
// Extract top 4 weights and their indexes
|
|
float4 weights = float4(weightData[0].x, weightData[1].x, weightData[2].x, weightData[3].x);
|
|
float4 splatmapIndexed = float4(weightData[0].y, weightData[1].y, weightData[2].y, weightData[3].y);
|
|
|
|
#endif
|
|
|
|
float2 dxuv = ddx(terrainUV);
|
|
float2 dyuv = ddy(terrainUV);
|
|
float4 uvData;
|
|
float4 deriv;
|
|
float4 finalNormalPackedBlend = float4(0, 0, 0, 0);
|
|
|
|
#ifndef _MOBILE_VR_ON
|
|
//Declare re-useable variables
|
|
int currentIndex;
|
|
float2 layerUV;
|
|
float4 currentRemapData;
|
|
float4 combinedAlbedoPacked;
|
|
#endif
|
|
|
|
//Geo Layers
|
|
#ifdef _GEOLOGICAL_ON
|
|
float blendedGeoLayerStrength = 1;
|
|
#endif
|
|
|
|
#ifdef _MOBILE_VR_ON
|
|
int index = 0;
|
|
float2 uvA = layerUVs(terrainUV, _LayerST0);
|
|
deriv = float4(dxuv * _LayerST0.x, dyuv * _LayerST0.y);
|
|
float4 albedoA = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvA, index, deriv.xy, deriv.zw);
|
|
float4 normalA = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvA, index, deriv.xy, deriv.zw);
|
|
albedoA.rgb *= _ColorData[index];
|
|
normalA.xy = normalA.xy * 2 - 1;
|
|
normalA.xy *= _LayerDataA[index].z;
|
|
normalA.zw = RemapFloat2(normalA.zw, float2(0, 0), float2(1, 1), _MaskMapRemapData[index].xy, _MaskMapRemapData[index].zw);
|
|
float weightA = splat.r;
|
|
|
|
index = 1;
|
|
float2 uvB = layerUVs(terrainUV, _LayerST1);
|
|
deriv = float4(dxuv * _LayerST1.x, dyuv * _LayerST1.y);
|
|
float4 albedoB = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvB, index, deriv.xy, deriv.zw);
|
|
float4 normalB = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvB, index, deriv.xy, deriv.zw);
|
|
albedoB.rgb *= _ColorData[index];
|
|
normalB.xy = normalB.xy * 2 - 1;
|
|
normalB.xy *= _LayerDataA[index].z;
|
|
normalB.zw = RemapFloat2(normalB.zw, float2(0, 0), float2(1, 1), _MaskMapRemapData[index].xy, _MaskMapRemapData[index].zw);
|
|
float weightB = splat.g;
|
|
|
|
index = 2;
|
|
float2 uvC = layerUVs(terrainUV, _LayerST2);
|
|
deriv = float4(dxuv * _LayerST2.x, dyuv * _LayerST2.y);
|
|
float4 albedoC = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvC, index, deriv.xy, deriv.zw);
|
|
float4 normalC = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvC, index, deriv.xy, deriv.zw);
|
|
albedoC.rgb *= _ColorData[index];
|
|
normalC.xy = normalC.xy * 2 - 1;
|
|
normalC.xy *= _LayerDataA[index].z;
|
|
normalC.zw = RemapFloat2(normalC.zw, float2(0, 0), float2(1, 1), _MaskMapRemapData[index].xy, _MaskMapRemapData[index].zw);
|
|
float weightC = splat.b;
|
|
|
|
index = 3;
|
|
float2 uvD = layerUVs(terrainUV, _LayerST3);
|
|
deriv = float4(dxuv * _LayerST3.x, dyuv * _LayerST3.y);
|
|
float4 albedoD = SAMPLE_TEXTURE2D_ARRAY_GRAD(_AlbedoArray, sampler_AlbedoArray, uvD, index, deriv.xy, deriv.zw);
|
|
float4 normalD = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalArray, sampler_NormalArray, uvD, index, deriv.xy, deriv.zw);
|
|
albedoD.rgb *= _ColorData[index];
|
|
normalD.xy = normalD.xy * 2 - 1;
|
|
normalD.xy *= _LayerDataA[index].z;
|
|
normalD.zw = RemapFloat2(normalD.zw, float2(0, 0), float2(1, 1), _MaskMapRemapData[index].xy, _MaskMapRemapData[index].zw);
|
|
float weightD = splat.a;
|
|
|
|
blendedAlbedo = (albedoA * weightA + albedoB * weightB + albedoC * weightC + albedoD * weightD).rgb;
|
|
finalNormalPackedBlend = normalA * weightA + normalB * weightB + normalC * weightC + normalD * weightD;
|
|
blendedMask = float4(0, finalNormalPackedBlend.b, 0, finalNormalPackedBlend.a);
|
|
|
|
|
|
#else
|
|
//Single Layer Sample
|
|
float4 lowResAlbedo = SAMPLE_TEXTURE2D(_SplatmapIndexLowRes, SamplerState_Linear_Clamp, terrainUV);
|
|
|
|
//Only sample top 4 if within certain distance.
|
|
UNITY_BRANCH
|
|
|
|
if (nearCameraDistance > 0)
|
|
{
|
|
float4 albedoA = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendA = float4(0, 0, 1, 0);
|
|
float4 albedoB = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendB = float4(0, 0, 1, 0);
|
|
float4 albedoC = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendC = float4(0, 0, 1, 0);
|
|
float4 albedoD = float4(0, 0, 0, 0);
|
|
float4 normalPackedBlendD = float4(0, 0, 1, 0);
|
|
float4 splatControl = float4(0, 0, 0, 0);
|
|
|
|
//Layer A
|
|
currentIndex = splatmapIndexed.x;
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
float4 detailStrengthControl = float4(0, 0, 0, 0);
|
|
#endif
|
|
#ifdef _GEOLOGICAL_ON
|
|
float4 geoStrengthControl = float4(0, 0, 0, 0);
|
|
#endif
|
|
|
|
|
|
|
|
float4 layerDataA_A = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(terrainUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_A,
|
|
albedoA, normalPackedBlendA);
|
|
//Set new splat weights Geo
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.x = layerDataA_A.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.x = layerDataA_A.w;
|
|
#endif
|
|
|
|
|
|
//Layer A
|
|
//Set new splat weights
|
|
splatControl.x = weights.x;
|
|
|
|
|
|
//Layer B
|
|
currentIndex = splatmapIndexed.y;
|
|
UNITY_BRANCH
|
|
|
|
if (weights.y > 0)
|
|
{
|
|
float4 layerDataA_B = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(terrainUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
float heightLODB;
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_B,
|
|
albedoB, normalPackedBlendB);
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.y = layerDataA_B.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.y = layerDataA_B.w;
|
|
#endif
|
|
}
|
|
|
|
//Set new splat weights
|
|
splatControl.y = weights.y;
|
|
|
|
//Set new splat weights Geo
|
|
|
|
//Layer C
|
|
currentIndex = splatmapIndexed.z;
|
|
UNITY_BRANCH
|
|
|
|
if (weights.z > 0)
|
|
{
|
|
float4 layerDataA_C = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(terrainUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_C,
|
|
albedoC, normalPackedBlendC);
|
|
//Set new splat weights Geo
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.z = layerDataA_C.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.z = layerDataA_C.w;
|
|
#endif
|
|
}
|
|
|
|
//Set new splat weights
|
|
splatControl.z = weights.z;
|
|
|
|
|
|
|
|
//Layer D
|
|
currentIndex = splatmapIndexed.w;
|
|
UNITY_BRANCH
|
|
|
|
if (weights.w > 0)
|
|
{
|
|
float4 layerDataA_D = _LayerDataA[currentIndex];
|
|
uvData = _UVData[currentIndex];
|
|
layerUV = layerUVs(terrainUV, uvData);
|
|
deriv = float4(dxuv * uvData.x, dyuv * uvData.y);
|
|
//Sample Current Layer
|
|
SampleLayerTexture(worldPos, worldNormal, currentIndex,
|
|
layerUV, deriv, _TriPlanarData[currentIndex],
|
|
_MaskMapRemapData[currentIndex], _HeightData[currentIndex], _ColorData[currentIndex], layerDataA_D,
|
|
albedoD, normalPackedBlendD);
|
|
|
|
//Set new splat weights Geo
|
|
#ifdef _GEOLOGICAL_ON
|
|
geoStrengthControl.w = layerDataA_D.x;
|
|
#endif
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
detailStrengthControl.w = layerDataA_D.w;
|
|
#endif
|
|
}
|
|
|
|
|
|
//Set new splat weights
|
|
splatControl.w = weights.w;
|
|
|
|
|
|
|
|
#ifdef _HEIGHT_BLEND_ON
|
|
//Height blending splat
|
|
float masks[] = { albedoA.a, albedoB.a, albedoC.a, albedoD.a };
|
|
float4 splatHeight = float4(masks[0], masks[1], masks[2], masks[3]) * splatControl.rgba;
|
|
float maxHeight = max(splatHeight.r, max(splatHeight.g, max(splatHeight.b, splatHeight.a)));
|
|
float heightTransition = _BlendFactor;
|
|
float transition = max(heightTransition, 1e-5);
|
|
half4 weightedHeights = splatHeight + transition - maxHeight.xxxx;
|
|
weightedHeights = max(0, weightedHeights);
|
|
weightedHeights = (weightedHeights + 1e-6) * splatControl;
|
|
float sumHeight = max(dot(weightedHeights, float4(1, 1, 1, 1)), 1e-6);
|
|
splatControl = weightedHeights / sumHeight.xxxx;
|
|
#endif
|
|
//Blend Albedo and Normals based on splats
|
|
combinedAlbedoPacked = albedoA * splatControl.x + albedoB * splatControl.y + albedoC * splatControl.z + albedoD * splatControl.w;
|
|
blendedAlbedo = combinedAlbedoPacked.rgb;
|
|
finalNormalPackedBlend = normalPackedBlendA * splatControl.x + normalPackedBlendB * splatControl.y + normalPackedBlendC * splatControl.z + normalPackedBlendD * splatControl.a;
|
|
//Set final blended mask
|
|
blendedMask = float4(0, finalNormalPackedBlend.z, 0, finalNormalPackedBlend.a);
|
|
|
|
float blendDist = saturate(nearCameraDistance * 2);
|
|
|
|
blendedAlbedo = lerp(lowResAlbedo.rgb, blendedAlbedo, blendDist);
|
|
finalNormalPackedBlend = lerp(float4(0, 0, 0, 0), finalNormalPackedBlend, blendDist);
|
|
blendedMask = lerp(float4(0, 1, 0, lowResAlbedo.a), blendedMask, blendDist);
|
|
|
|
//Blend Geo Layer Strengths
|
|
#ifdef _GEOLOGICAL_ON
|
|
blendedGeoLayerStrength = geoStrengthControl.x * splatControl.x + geoStrengthControl.y * splatControl.y + geoStrengthControl.z * splatControl.z + geoStrengthControl.w * splatControl.w;
|
|
blendedGeoLayerStrength = lerp(0, blendedGeoLayerStrength, blendDist);
|
|
#endif
|
|
//Blend Detail Layer Strengths
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
blendedDetailLayerStrength = detailStrengthControl.x * splatControl.x + detailStrengthControl.y * splatControl.y + detailStrengthControl.z * splatControl.z + detailStrengthControl.w * splatControl.w;
|
|
blendedDetailLayerStrength = lerp(1, blendedDetailLayerStrength, blendDist);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
blendedAlbedo = lowResAlbedo.rgb;
|
|
finalNormalPackedBlend = float4(0, 0, 0, 0);
|
|
blendedMask = float4(0, 1, 0, lowResAlbedo.a);
|
|
#ifdef _GEOLOGICAL_ON
|
|
blendedGeoLayerStrength = 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef _COLORMAP_ON
|
|
//Sample the textures
|
|
float4 colormap = SAMPLE_TEXTURE2D(_ColormapTex, SamplerState_Linear_Repeat, terrainUV);
|
|
|
|
//Create alpha
|
|
float colormapAlpha = saturate(colormap.a * _ColormapDataA.x);
|
|
|
|
//Create near far intensities
|
|
float colorIntensity = lerp(_ColormapNearFarData.x, _ColormapNearFarData.y, globalBlendDistance);
|
|
colormap.rgb = saturate(colormap.rgb * _ColormapDataA.y);
|
|
|
|
//Output final blend
|
|
blendedAlbedo = lerp(blendedAlbedo, colormap.rgb, colormapAlpha * colorIntensity);
|
|
#endif
|
|
|
|
#ifdef _GEOLOGICAL_ON
|
|
float3 geoWorldPos = _ObjectSpaceDataA.y == 0 ? worldPos : worldToObjPos;
|
|
float2 geoNormals = float2(0, 0);
|
|
//Sample near geo data
|
|
float2 nearGeoUVs = float2(0, (geoWorldPos.y / _GeoNearData.y) + _GeoNearData.z);
|
|
float3 nearGeoMap = SAMPLE_TEXTURE2D(_GeoMap, SamplerState_Linear_Repeat, nearGeoUVs).rgb * 2;
|
|
float2 nearGeoNormal = (SAMPLE_TEXTURE2D(_GeoNormal, SamplerState_Linear_Repeat, nearGeoUVs).ag * 2 - 1) * _GeoNearData.w;
|
|
|
|
//Sample far geo data
|
|
float2 farGeoUVs = float2(0, (geoWorldPos.y / _GeoFarData.y) + _GeoFarData.z);
|
|
float3 farGeoMap = SAMPLE_TEXTURE2D(_GeoMap, SamplerState_Linear_Repeat, farGeoUVs).rgb * 2;
|
|
float2 farGeoNormal = (SAMPLE_TEXTURE2D(_GeoNormal, SamplerState_Linear_Repeat, farGeoUVs).ag * 2 - 1) * _GeoFarData.w;
|
|
|
|
//Blend geo data
|
|
float3 nearGeoBlend = ((nearGeoMap + float3(-0.3, -0.3, -0.3)) * _GeoNearData.x);
|
|
float3 farGeoBlend = ((farGeoMap + float3(-0.3, -0.3, -0.3)) * _GeoFarData.x);
|
|
float3 geoBlend = lerp(nearGeoBlend, farGeoBlend, globalBlendDistance) * blendedGeoLayerStrength;
|
|
blendedAlbedo = saturate(blendedAlbedo + geoBlend);
|
|
|
|
geoNormals = lerp(nearGeoNormal, farGeoNormal, globalBlendDistance) * blendedGeoLayerStrength;
|
|
|
|
#endif
|
|
|
|
//Final Normal Calculation
|
|
blendedNormal.xy = finalNormalPackedBlend.xy;
|
|
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
blendedNormal.xy += blendedDetailNormals.xy * blendedDetailLayerStrength;
|
|
#endif
|
|
#ifdef _GEOLOGICAL_ON
|
|
blendedNormal.xy += geoNormals;
|
|
#endif
|
|
|
|
|
|
//Reconstruct Tangent Z Component from Combined XY Normals
|
|
blendedNormal.z = sqrt(1.0 - saturate(dot(blendedNormal.rg, blendedNormal.rg)));
|
|
blendedNormal = normalize(blendedNormal);
|
|
|
|
//Blend Tangent Space Normals on the World Terrain Normal
|
|
blendedNormal = worldSpaceNormalBlend(worldTerrainNormal, blendedNormal);
|
|
|
|
//Add detail into the snow
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
blendedAlbedo *= lerp(0.5, 1, 1 - (detailNormalGreyscale * blendedDetailLayerStrength));
|
|
#endif
|
|
|
|
#ifdef _SNOW_ON
|
|
float3 snowWorldPos = _ObjectSpaceDataA.z == 0 ? worldPos : worldToObjPos;
|
|
|
|
//Sample snow textures
|
|
float2 snowUV = worldPos.xz / _PW_SnowDataB.r;
|
|
float3 snowAlbedo = SAMPLE_TEXTURE2D(_PW_SnowAlbedoMap, SamplerState_Linear_Repeat, snowUV).rgb;
|
|
float3 snowNormal = UnpackNormalTangent(SAMPLE_TEXTURE2D(_PW_SnowNormalMap, SamplerState_Linear_Repeat, snowUV));
|
|
snowNormal.xy *= _PW_SnowDataB.w;
|
|
float4 snowMask = SAMPLE_TEXTURE2D(_PW_SnowMaskMap, SamplerState_Linear_Repeat, snowUV);
|
|
snowMask = RemapFloat4(snowMask, float4(0, 0, 0, 0), float4(1, 1, 1, 1), _PW_SnowMaskRemapMin, _PW_SnowMaskRemapMax);
|
|
|
|
snowMask.b = pow(abs(snowMask.b), _PW_SnowHeightData.x) * _PW_SnowHeightData.y + _PW_SnowHeightData.z;
|
|
|
|
//Create snow starting level
|
|
float snowStart = ((snowWorldPos.g - _PW_SnowDataA.z));
|
|
snowStart = smoothstep(-_PW_SnowDataB.y, _PW_SnowDataB.y, snowStart);
|
|
|
|
//Create snow slope mask
|
|
float snowSlopeAge = lerp(1, 10, _PW_SnowDataA.a);
|
|
float slopeMask = saturate(pow(abs(worldTerrainNormal.g), _PW_SnowDataB.z * snowSlopeAge));
|
|
|
|
#ifdef _DETAIL_NORMALS_ON
|
|
snowAlbedo *= lerp(0.2, 1, 1 - detailNormalGreyscale);
|
|
#endif
|
|
|
|
//Create Final Snow Mask
|
|
float finalSnowMask = snowStart * saturate(slopeMask * 4) * (1-_PW_SnowDataA.a);
|
|
finalSnowMask *= _PW_SnowDataA.g;
|
|
|
|
//Lerp Snow
|
|
blendedAlbedo = lerp(blendedAlbedo, snowAlbedo * _PW_SnowColor.rgb, finalSnowMask);
|
|
blendedMask = lerp(blendedMask, snowMask, finalSnowMask);
|
|
|
|
//Snow Normal
|
|
snowNormal = worldSpaceNormalBlend(worldTerrainNormal, snowNormal);
|
|
blendedNormal = lerp(blendedNormal, snowNormal, finalSnowMask);
|
|
|
|
#endif
|
|
|
|
#ifdef _RAIN_ON
|
|
//Rain
|
|
float topMask = saturate(pow(worldTerrainNormal.g, 4));
|
|
float blendRange = 30;
|
|
float rainStart = ((worldPos.g - _PW_RainDataA.z));
|
|
rainStart = smoothstep(-blendRange, blendRange, rainStart);
|
|
float rainEnd = ((worldPos.g - _PW_RainDataA.w));
|
|
rainEnd = smoothstep(-blendRange, blendRange, rainEnd);
|
|
rainStart = saturate(rainStart);
|
|
rainEnd = saturate(rainEnd);
|
|
float rainRange = rainStart * (1 - rainEnd);
|
|
float rainMask = _PW_RainDataA.y * rainRange;
|
|
|
|
blendedAlbedo = lerp(blendedAlbedo, blendedAlbedo * _PW_RainDataB.y, rainMask);
|
|
|
|
float3 rainNormals = getRainNormals(worldPos);
|
|
float rainNormalStrength = lerp(0, 0.6, rainMask);
|
|
rainNormals = float3(rainNormals.rg * rainNormalStrength, lerp(1, rainNormals.b, saturate(rainNormalStrength)));
|
|
float3 rainNormalBlend = worldSpaceNormalBlend(blendedNormal, rainNormals);
|
|
blendedNormal = lerp(blendedNormal, rainNormalBlend, rainMask * topMask);
|
|
float rainSmoothness = lerp(.4, _PW_RainDataB.z, topMask);
|
|
blendedMask.a = lerp(blendedMask.a, rainSmoothness, rainMask);
|
|
#endif
|
|
|
|
#ifdef _MACRO_VARIATION_ON
|
|
float3 variationWorldPos = _ObjectSpaceDataA.w == 0 ? worldPos : worldToObjPos;
|
|
//Sample variation offsets
|
|
float2 variationUVA = variationWorldPos.xz * _MacroVariationData.r;
|
|
float2 variationUVB = variationWorldPos.xz * _MacroVariationData.g;
|
|
float2 variationUVC = variationWorldPos.xz * _MacroVariationData.b;
|
|
|
|
//Sample variation texture at different uv scales
|
|
float4 variationA = SAMPLE_TEXTURE2D(_MacroVariationMap, SamplerState_Linear_Repeat, variationUVA);
|
|
float4 variationB = SAMPLE_TEXTURE2D(_MacroVariationMap, SamplerState_Linear_Repeat, variationUVB);
|
|
float4 variationC = SAMPLE_TEXTURE2D(_MacroVariationMap, SamplerState_Linear_Repeat, variationUVC);
|
|
|
|
//Blend variation
|
|
float variation = saturate((variationA.r + 0.5) * (variationB.r + 0.5) * (variationC.r + 0.5));
|
|
|
|
//Add variation
|
|
blendedAlbedo = blendedAlbedo * lerp(_MacroVariationData.w, 1, variation);
|
|
#endif
|
|
|
|
}
|
|
|
|
void GTSLayersDisplacement_float(float2 terrainUV, float3 worldPos, float3 worldNormal, float2 splatTexelSize,
|
|
out float blendedDisplacement, out float blendedTessellation)
|
|
{
|
|
|
|
blendedDisplacement = 0;
|
|
blendedTessellation = 1;
|
|
|
|
#ifdef _TESSELLATION_ON
|
|
//Create Terrain World UVs
|
|
terrainUV = worldPosTerrainUV(worldPos.xz, _TerrainPosSize.xy, _TerrainPosSize.zw);
|
|
|
|
//Calculate camera distance
|
|
float3 CameraToWorldDirection = _WorldSpaceCameraPos.xyz - worldPos;
|
|
float squareMag = dot(CameraToWorldDirection, CameraToWorldDirection);
|
|
float nearCameraDistance = 1 - saturate(squareMag * 0.00001);
|
|
|
|
float2 sampleCoords = (terrainUV / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy;
|
|
float3 worldTerrainNormal = SAMPLE_TEXTURE2D_LOD(_WorldNormalMap, SamplerState_Linear_Clamp, terrainUV, 0).rgb * 2 - 1;
|
|
|
|
//Sample Terrain splatmaps, used for weighting
|
|
float2 controlUV;
|
|
controlUV = (terrainUV * (splatTexelSize - 1) + 0.5) * (1 / splatTexelSize);
|
|
float4 splatLOD = SAMPLE_TEXTURE2D_LOD(_Control0, SamplerState_Linear_Clamp, controlUV, 0);
|
|
float4 splatLOD1 = SAMPLE_TEXTURE2D_LOD(_Control1, SamplerState_Linear_Clamp, controlUV, 0);
|
|
|
|
//Per Layer Data Arrays, so our index map can index into them
|
|
//float weightSplatsLOD[] = { splatLOD.r, splatLOD.g, splatLOD.b, splatLOD.a, splatLOD1.r, splatLOD1.g, splatLOD1.b, splatLOD1.a };
|
|
|
|
float2 weightData[] =
|
|
{
|
|
float2(splatLOD.r, 0),
|
|
float2(splatLOD.g, 1),
|
|
float2(splatLOD.b, 2),
|
|
float2(splatLOD.a, 3),
|
|
float2(splatLOD1.r, 4),
|
|
float2(splatLOD1.g, 5),
|
|
float2(splatLOD1.b, 6),
|
|
float2(splatLOD1.a, 7)
|
|
};
|
|
|
|
// Unrolled bubble sort for 8 items
|
|
#define SWAP_IF_GREATER(i, j) \
|
|
if (weightData[i].x < weightData[j].x) { \
|
|
float2 temp = weightData[i]; \
|
|
weightData[i] = weightData[j]; \
|
|
weightData[j] = temp; \
|
|
}
|
|
|
|
// Pass 1
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
SWAP_IF_GREATER(4, 5);SWAP_IF_GREATER(5, 6);SWAP_IF_GREATER(6, 7);
|
|
// Pass 2
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
SWAP_IF_GREATER(4, 5);SWAP_IF_GREATER(5, 6);
|
|
// Pass 3
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
SWAP_IF_GREATER(4, 5);
|
|
// Pass 4
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);SWAP_IF_GREATER(3, 4);
|
|
// Pass 5
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);SWAP_IF_GREATER(2, 3);
|
|
// Pass 6
|
|
SWAP_IF_GREATER(0, 1);SWAP_IF_GREATER(1, 2);
|
|
// Pass 7
|
|
SWAP_IF_GREATER(0, 1);
|
|
|
|
// Extract top 4 weights and their indexes
|
|
float4 weights = float4(weightData[0].x, weightData[1].x, weightData[2].x, weightData[3].x);
|
|
float4 splatmapIndexed = float4(weightData[0].y, weightData[1].y, weightData[2].y, weightData[3].y);
|
|
|
|
//Sample top 4 splats
|
|
float4 splatmapIndexedLOD = SAMPLE_TEXTURE2D_LOD(_SplatmapIndex, SamplerState_Point_Repeat, controlUV, 0) * 256;
|
|
|
|
//Declare re-useable variables
|
|
int currentIndexLOD;
|
|
float2 layerUVLOD;
|
|
|
|
UNITY_BRANCH
|
|
if (nearCameraDistance > 0)
|
|
{
|
|
float4 splatControlLOD = float4(0, 0, 0, 0);
|
|
float2 displacementTessellationA = float2(0,0);
|
|
float heightLODA;
|
|
float2 displacementTessellationB = float2(0,0);
|
|
float heightLODB;
|
|
float2 displacementTessellationC = float2(0,0);
|
|
float heightLODC;
|
|
float2 displacementTessellationD = float2(0,0);
|
|
float heightLODD;
|
|
|
|
//Layer A
|
|
currentIndexLOD = splatmapIndexed.x;
|
|
|
|
layerUVLOD = layerUVs(terrainUV, _UVData[currentIndexLOD]);
|
|
|
|
|
|
SampleLayerDisplacement(worldPos, worldNormal, currentIndexLOD,
|
|
layerUVLOD, _TriPlanarData[currentIndexLOD],
|
|
_DisplacementData[currentIndexLOD], _HeightData[currentIndexLOD], _LayerDataA[currentIndexLOD],
|
|
displacementTessellationA, heightLODA);
|
|
splatControlLOD.x = weights.x;
|
|
|
|
|
|
|
|
//Layer B
|
|
currentIndexLOD = splatmapIndexed.y;
|
|
UNITY_BRANCH
|
|
if (weights.y > 0)
|
|
{
|
|
layerUVLOD = layerUVs(terrainUV, _UVData[currentIndexLOD]);
|
|
|
|
|
|
|
|
SampleLayerDisplacement(worldPos, worldNormal, currentIndexLOD,
|
|
layerUVLOD, _TriPlanarData[currentIndexLOD],
|
|
_DisplacementData[currentIndexLOD], _HeightData[currentIndexLOD], _LayerDataA[currentIndexLOD],
|
|
displacementTessellationB, heightLODB);
|
|
splatControlLOD.y = weights.y;
|
|
}
|
|
|
|
|
|
//Layer C
|
|
currentIndexLOD = splatmapIndexed.z;
|
|
UNITY_BRANCH
|
|
if (weights.z > 0)
|
|
{
|
|
layerUVLOD = layerUVs(terrainUV, _UVData[currentIndexLOD]);
|
|
|
|
|
|
SampleLayerDisplacement(worldPos, worldNormal, currentIndexLOD,
|
|
layerUVLOD, _TriPlanarData[currentIndexLOD],
|
|
_DisplacementData[currentIndexLOD], _HeightData[currentIndexLOD], _LayerDataA[currentIndexLOD],
|
|
displacementTessellationC, heightLODC);
|
|
|
|
splatControlLOD.z = weights.z;
|
|
}
|
|
|
|
|
|
//Layer D
|
|
currentIndexLOD = splatmapIndexed.w;
|
|
if (weights.w > 0)
|
|
{
|
|
layerUVLOD = layerUVs(terrainUV, _UVData[currentIndexLOD]);
|
|
|
|
|
|
SampleLayerDisplacement(worldPos, worldNormal, currentIndexLOD,
|
|
layerUVLOD, _TriPlanarData[currentIndexLOD],
|
|
_DisplacementData[currentIndexLOD], _HeightData[currentIndexLOD], _LayerDataA[currentIndexLOD],
|
|
displacementTessellationD, heightLODD);
|
|
|
|
splatControlLOD.w = weights.w;
|
|
}
|
|
|
|
|
|
|
|
float2 displacementTessellation = float2(0, 0);
|
|
displacementTessellation = displacementTessellationA * splatControlLOD.x + displacementTessellationB * splatControlLOD.y + displacementTessellationC * splatControlLOD.z + displacementTessellationD * splatControlLOD.w;
|
|
blendedDisplacement = displacementTessellation.x;
|
|
blendedTessellation = displacementTessellation.y;
|
|
}
|
|
else
|
|
{
|
|
blendedDisplacement = 0;
|
|
blendedTessellation = 0;
|
|
}
|
|
|
|
|
|
#ifdef _SNOW_ON
|
|
//Sample snow textures
|
|
float2 snowUV = worldPos.xz / _PW_SnowDataB.r;
|
|
|
|
float snowHeight = SAMPLE_TEXTURE2D_LOD(_PW_SnowMaskMap, SamplerState_Linear_Repeat, snowUV, 0).b;
|
|
snowHeight = pow(abs(snowHeight), _PW_SnowHeightData.x) * _PW_SnowHeightData.y + _PW_SnowHeightData.z;
|
|
float snowDisplacement = pow(abs(snowHeight), _PW_SnowDisplacementData.x) * _PW_SnowDisplacementData.y + _PW_SnowDisplacementData.z;
|
|
float snowTessellation = _PW_SnowDisplacementData.w;
|
|
|
|
//Create snow starting level
|
|
float snowStartLOD = ((worldPos.g - _PW_SnowDataA.z));
|
|
snowStartLOD = smoothstep(-_PW_SnowDataB.y, _PW_SnowDataB.y, snowStartLOD);
|
|
|
|
//Create snow slope mask
|
|
float slopeMaskLOD = saturate(pow(abs(worldTerrainNormal.g), _PW_SnowDataB.z));
|
|
|
|
//Create Final Snow Mask
|
|
float finalSnowMaskLOD = snowStartLOD * saturate(slopeMaskLOD * 4) * (1-_PW_SnowDataA.a);
|
|
finalSnowMaskLOD *= _PW_SnowDataA.g;
|
|
|
|
blendedDisplacement = lerp(blendedDisplacement, snowDisplacement * _PW_SnowDataA.g, finalSnowMaskLOD);
|
|
blendedTessellation = lerp(blendedTessellation, snowTessellation * _PW_SnowDataA.g, finalSnowMaskLOD);
|
|
|
|
#endif
|
|
blendedTessellation *= _TessellationMultiplier;
|
|
#endif
|
|
}
|
|
|
|
#endif |