Jan. 12, 2021
Inspiriert von einem Reddit Beitrag, wollte ich sehen wie schwer es ist so eine Simulation mit Shadern zu implementieren. Mit Compute Shadern hatte ich bisher keine Erfahrung, also habe ich mir erstmal ein Tutorial angesehen. Das ganze war deutlich simpler als ich erwartet hätte. Also ein einfaches Beispiel geschrieben und ich hatte eine direkte Kommunikation zwischen CPU und GPU. Der Algorithmus für die Wasseroberfläche ist ein Height-Field basierte Simulation, welche auf der GDC2008 von Matthias Müller-Fischer vorgestellt wurde. Der Algorithmus berechnet aus einem Höhenfeld ein Geschwindigkeitsfeld welches wiederum das Höhenfeld beeinflusst. Aus dem Höhenfeld wird dann eine Normalmap errechnet. Der Algorithmus ist super einfach zu verstehen und der Effekt wirklich sehr realistisch und performant.
#pragma kernel WaterHeight
RWTexture2D<float> HeightMap;
RWTexture2D<float> HeightMapNew;
RWTexture2D<float> VelocityMap;
RWBuffer<float> CollisionBuffer;
float deltaTime;
static const uint2 one_zero = { 1,0 };
static const uint2 zero_one = { 0,1 };
float WaveSpeed;
float Resolution;
int TextureSize;
[numthreads(8,8,1)]
void WaterHeight(uint3 id : SV_DispatchThreadID)
{
HeightMap[id.xy] += CollisionBuffer[id.x * TextureSize + id.y];
CollisionBuffer[id.x * TextureSize + id.y] = 0;
float f = pow(WaveSpeed,2) * (HeightMap[id.xy - one_zero] + HeightMap[id.xy + one_zero] +
HeightMap[id.xy - zero_one] + HeightMap[id.xy + zero_one] - 4.0 * HeightMap[id.xy]) / pow(Resolution,2);
VelocityMap[id.xy] += f * deltaTime;
HeightMapNew[id.xy] = HeightMap[id.xy] + VelocityMap[id.xy];
VelocityMap[id.xy] *= 0.99;
}