From 2d97364b3cdf1354e5b60807732d447217436523 Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Sun, 24 Feb 2019 13:02:14 +0100 Subject: Extract interaction logic into separate shader The collide shader became to crowded for my taste. As a nice side benefit we can now execute interaction processing only when actual interaction is taking place. --- src/main.cc | 33 +++++++---- src/shader/code/collide.glsl | 84 +++------------------------- src/shader/code/interact.glsl | 127 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 86 deletions(-) create mode 100644 src/shader/code/interact.glsl (limited to 'src') diff --git a/src/main.cc b/src/main.cc index 4621d30..2a3dd9b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -18,6 +18,7 @@ #include "shader/code/vertex.glsl" #include "shader/code/fragment.glsl" +#include "shader/code/interact.glsl" #include "shader/code/collide.glsl" #include "shader/code/stream.glsl" @@ -67,6 +68,7 @@ int renderWindow() { std::unique_ptr lattice_b; std::unique_ptr fluid; + std::unique_ptr interact_shader; std::unique_ptr collide_shader; std::unique_ptr stream_shader; @@ -87,11 +89,12 @@ int renderWindow() { return 1; // everything shall be fluid }); - collide_shader = std::make_unique(COLLIDE_SHADER_CODE); - stream_shader = std::make_unique(STREAM_SHADER_CODE); + interact_shader = std::make_unique(INTERACT_SHADER_CODE); + collide_shader = std::make_unique(COLLIDE_SHADER_CODE); + stream_shader = std::make_unique(STREAM_SHADER_CODE); }); - if ( !collide_shader->isGood() || !stream_shader->isGood() ) { + if ( !interact_shader->isGood() || !collide_shader->isGood() || !stream_shader->isGood() ) { std::cerr << "Compute shader error." << std::endl; return -1; } @@ -125,30 +128,40 @@ int renderWindow() { if ( tick ) { collide_shader->workOn(tick_buffers); stream_shader->workOn(tick_buffers); + interact_shader->workOn(tick_buffers); tick = false; } else { collide_shader->workOn(tock_buffers); stream_shader->workOn(tock_buffers); + interact_shader->workOn(tock_buffers); tick = true; } - { - auto guard = collide_shader->use(); + /// Handle mouse-based interaction + const auto m = window.getMouse(); - collide_shader->setUniform("prevMouseState", prevLatticeMouseState); - collide_shader->setUniform("prevMousePos", prevLatticeMouseX, prevLatticeMouseY); + if ( std::get<0>(m) != 0 || prevLatticeMouseState != 0 ) { + auto guard = interact_shader->use(); + + interact_shader->setUniform("prevMouseState", prevLatticeMouseState); + interact_shader->setUniform("prevMousePos", prevLatticeMouseX, prevLatticeMouseY); - const auto m = window.getMouse(); const float latticeMouseX = float(std::get<1>(m)) / window.getWidth() * world_width + nX/2; const float latticeMouseY = float(std::get<2>(m)) / window.getHeight() * world_height + nY/2; - collide_shader->setUniform("currMouseState", std::get<0>(m)); - collide_shader->setUniform("currMousePos", latticeMouseX, latticeMouseY); + interact_shader->setUniform("currMouseState", std::get<0>(m)); + interact_shader->setUniform("currMousePos", latticeMouseX, latticeMouseY); prevLatticeMouseState = std::get<0>(m); prevLatticeMouseX = latticeMouseX; prevLatticeMouseY = latticeMouseY; + interact_shader->dispatch(nX, nY); + } + + /// Perform collide & stream steps + { + auto guard = collide_shader->use(); collide_shader->dispatch(nX, nY); } { diff --git a/src/shader/code/collide.glsl b/src/shader/code/collide.glsl index a73024e..0ddae7b 100644 --- a/src/shader/code/collide.glsl +++ b/src/shader/code/collide.glsl @@ -7,13 +7,6 @@ layout (std430, binding=1) buffer bufferCollide { float collideCells[]; }; layout (std430, binding=2) buffer bufferStream { float streamCells[]; }; layout (std430, binding=3) buffer bufferFluid { float fluidCells[]; }; -/// external influence - -uniform int prevMouseState; -uniform vec2 prevMousePos; -uniform int currMouseState; -uniform vec2 currMousePos; - /// LBM constants uniform uint nX; @@ -43,22 +36,6 @@ float norm(vec2 v) { return sqrt(dot(v,v)); } -float distanceToLineSegment(vec2 a, vec2 b, vec2 p) { - const vec2 ab = b - a; - - const vec2 pa = a - p; - if ( dot(ab, pa) > 0.0 ) { - return norm(pa); - } - - const vec2 bp = p - b; - if ( dot(ab, bp) > 0.0 ) { - return norm(bp); - } - - return norm(pa - ab * (dot(ab, pa) / dot(ab, ab))); -} - /// Array indexing uint indexOfDirection(int i, int j) { @@ -98,11 +75,6 @@ int getMaterial(uint x, uint y) { return int(fluidCells[idx + 2]); } -void setMaterial(uint x, uint y, int m) { - const uint idx = indexOfFluidVertex(x, y); - fluidCells[idx + 2] = m; -} - /// Moments float density(uint x, uint y) { @@ -127,50 +99,19 @@ float equilibrium(float d, vec2 v, int i, int j) { return w(i,j) * d * (1 + 3*comp(i,j,v) + 4.5*sq(comp(i,j,v)) - 1.5*sq(norm(v))); } -/// Disable wall interior - -void disableWallInterior(uint x, uint y) { - int wallNeighbors = 0; - - for ( int i = -1; i <= 1; ++i ) { - for ( int j = -1; j <= 1; ++j ) { - const int material = getMaterial(x+i,y+j); - if ( material == 0 || material == 2 || material == 3 ) { - ++wallNeighbors; - } - } - } +/// Material number meaning (geometry is only changed by the interaction shader) - if ( wallNeighbors == 9 ) { - setMaterial(x,y,0); - } -} - -/// Determine external influence - -bool isNearMouse(uint x, uint y, float eps) { - if ( prevMouseState == currMouseState ) { - return distanceToLineSegment(prevMousePos, currMousePos, vec2(x,y)) < eps; - } else { - return norm(vec2(x,y) - currMousePos) < eps; - } +bool isBulkFluidCell(int material) { + return material == 1 || material == 4; } -float getExternalPressureInflux(uint x, uint y) { - if ( currMouseState == 1 && isNearMouse(x, y, 3) ) { +float getExternalMassInflux(int material) { + if ( material == 4 ) { return 1.5; } else { return 0.0; } -} - -bool isWallRequestedAt(uint x, uint y) { - if ( currMouseState == 2 && isNearMouse(x, y, 3) ) { - return true; - } else { - return false; - } -} +}; /// Actual collide kernel @@ -184,17 +125,8 @@ void main() { const int material = getMaterial(x,y); - if ( isWallRequestedAt(x,y) && material == 1 ) { - setMaterial(x,y,3); - return; - } - - if ( material == 3 ) { // manually added wall - disableWallInterior(x,y); - } - - if ( material == 1 ) { // fluid - const float d = max(getExternalPressureInflux(x,y), density(x,y)); + if ( isBulkFluidCell(material) ) { + const float d = max(density(x,y), getExternalMassInflux(material)); const vec2 v = velocity(x,y,d); setFluid(x,y,v); diff --git a/src/shader/code/interact.glsl b/src/shader/code/interact.glsl new file mode 100644 index 0000000..a65d96a --- /dev/null +++ b/src/shader/code/interact.glsl @@ -0,0 +1,127 @@ +static const std::string INTERACT_SHADER_CODE = R"( +#version 430 + +layout (local_size_x = 1, local_size_y = 1) in; + +layout (std430, binding=3) buffer bufferFluid { float fluidCells[]; }; + +uniform uint nX; +uniform uint nY; + +/// External influence + +uniform int prevMouseState; +uniform vec2 prevMousePos; +uniform int currMouseState; +uniform vec2 currMousePos; + +/// Vector utilities + +float norm(vec2 v) { + return sqrt(dot(v,v)); +} + +float distanceToLineSegment(vec2 a, vec2 b, vec2 p) { + const vec2 ab = b - a; + + const vec2 pa = a - p; + if ( dot(ab, pa) > 0.0 ) { + return norm(pa); + } + + const vec2 bp = p - b; + if ( dot(ab, bp) > 0.0 ) { + return norm(bp); + } + + return norm(pa - ab * (dot(ab, pa) / dot(ab, ab))); +} + +/// Array indexing + +uint indexOfFluidVertex(uint x, uint y) { + return 3*nX*y + 3*x; +} + +/// Data access + +int getMaterial(uint x, uint y) { + const uint idx = indexOfFluidVertex(x, y); + return int(fluidCells[idx + 2]); +} + +void setMaterial(uint x, uint y, int m) { + const uint idx = indexOfFluidVertex(x, y); + fluidCells[idx + 2] = m; +} + +/// Geometry cleanup + +void disableWallInterior(uint x, uint y) { + int wallNeighbors = 0; + + for ( int i = -1; i <= 1; ++i ) { + for ( int j = -1; j <= 1; ++j ) { + const int material = getMaterial(x+i,y+j); + if ( material == 0 || material == 2 || material == 3 ) { + ++wallNeighbors; + } + } + } + + if ( wallNeighbors == 9 ) { + setMaterial(x,y,0); + } +} + +/// Determine external influence + +bool isNearMouse(uint x, uint y, float eps) { + if ( prevMouseState == currMouseState ) { + return distanceToLineSegment(prevMousePos, currMousePos, vec2(x,y)) < eps; + } else { + return norm(vec2(x,y) - currMousePos) < eps; + } +} + +bool isInfluxRequestedAt(uint x, uint y) { + return currMouseState == 1 && isNearMouse(x, y, 3); +} + +bool isWallRequestedAt(uint x, uint y) { + return currMouseState == 2 && isNearMouse(x, y, 3); +} + +/// Actual interaction kernel + +void main() { + const uint x = gl_GlobalInvocationID.x; + const uint y = gl_GlobalInvocationID.y; + + if ( !(x < nX && y < nY) ) { + return; + } + + const int material = getMaterial(x,y); + + if ( material == 1 ) { + if ( isInfluxRequestedAt(x,y) ) { + setMaterial(x,y,4); + return; + } + if ( isWallRequestedAt(x,y) ) { + setMaterial(x,y,3); + return; + } + } + + if ( material == 3 ) { + disableWallInterior(x,y); + } + + if ( material == 4 ) { + // reset influx material after execution + setMaterial(x,y,1); + } +} +)"; -- cgit v1.2.3