103 lines
4.5 KiB
HLSL
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);
|
|
}
|
|
}
|
|
} |