Files
client/Editor/Internal Data/Third Party/Shaders/Layers/HydraulicErosion.hlsl
T
2026-07-31 19:29:07 +03:00

103 lines
4.5 KiB
HLSL

// Licensed under the Non-Profit Open Software License version 3.0
// Variables
uint ErosionParticleCount;
uint ErosionMaxLifeTime;
float ErosionInertia;
float ErosionParticleStartSpeed;
float ErosionParticleStartWater;
float ErosionSedimentCapacityFactor;
float ErosionMinSedimentCapacity;
float ErosionSpeed;
float ErosionEvaporateSpeed;
float ErosionGravity;
float ErosionDepositSpeed;
// Kernel
[numthreads(32,1,1)]
void HydraulicErosion (uint3 id : SV_DispatchThreadID)
{
if(id.x < ErosionParticleCount){ // Keep in bounds
float randomNormalizedX = Random(id.x);
float randomNormalizedY = Random(id.x + HeightMapResolution * randomNormalizedX);
float2 pos = float2(
randomNormalizedX * HeightMapResolution,
randomNormalizedY * HeightMapResolution
);
// Particle values
float2 dir = float2(0,0);
float speed = ErosionParticleStartSpeed;
float water = ErosionParticleStartWater;
float sediment = 0;
for(uint lifeTime = 0; lifeTime < ErosionMaxLifeTime; lifeTime++){
// Calculate texture coords
uint2 coord = uint2(
(uint) pos.x,
(uint) pos.y
);
int index = PosToIndex(coord, HeightMapResolution);
// Calculate cell offset
float2 cellOffset = float2(
pos.x - float(coord.x),
pos.y - float(coord.y)
);
// Calculate interpolated gradient and height
float3 heightAndGradient = CalculateGradientAndHeight(pos, HeightMapResolution, HeightBuffer);
// Update the droplet's direction and position (move position 1 unit regardless of speed)
dir.x = (dir.x * ErosionInertia - heightAndGradient.x * (1 - ErosionInertia));
dir.y = (dir.y * ErosionInertia - heightAndGradient.y * (1 - ErosionInertia));
// Normalize dir
dir = Normalize2D(dir);
// Add to position
pos += dir;
// Header guard to check wheter particle is still inside texture and moving
if((dir.x == 0 && dir.y == 0) || pos.x <= 0 || pos.y <= 0 || pos.x >= float(HeightMapResolution - 2) || pos.y >= float(HeightMapResolution - 2)){ break; }
// Calculate new height and deltaHeight
float height = CalculateGradientAndHeight(pos, HeightMapResolution, HeightBuffer).z;
float deltaHeight = height - heightAndGradient.z;
// Calculate the droplet's sediment capacity (higher when moving fast down a slope and contains lots of water)
float sedimentCapacity = max(-deltaHeight, ErosionMinSedimentCapacity) * speed * water * ErosionSedimentCapacityFactor;
// If carrying more sediment than capacity, or if flowing uphill:
if (sediment > sedimentCapacity || deltaHeight > 0) {
// If moving uphill (deltaHeight > 0) try fill up to the current height, otherwise deposit a fraction of the excess sediment
float amountToDeposit = (deltaHeight > 0) ?
min(deltaHeight, sediment) :
(sediment - sedimentCapacity) * ErosionDepositSpeed;
// Remove deposited sediment
sediment -= amountToDeposit;
// Add the sediment to the four nodes of the current cell using bilinear interpolation
AddToBuffer(index, HeightMapResolution, cellOffset, amountToDeposit, HeightBuffer);
}
else {
// Erode a fraction of the droplet's current carry capacity.
// Clamp the erosion to the change in height so that it doesn't dig a hole in the terrain behind the droplet
float amountToErode = min((sedimentCapacity - sediment) * ErosionSpeed, -deltaHeight);
// Remove erosion amount from heightmap
AddToBuffer(index, HeightMapResolution, cellOffset, -amountToErode, HeightBuffer);
// Add eroded amount to sediment currently carried
sediment += amountToErode;
}
// Update droplet's speed and water content
speed = sqrt (max(0,speed * speed + deltaHeight * ErosionGravity));
water *= (1 - ErosionEvaporateSpeed);
}
}
}