aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Kummerlaender2019-04-28 12:53:27 +0200
committerAdrian Kummerlaender2019-04-28 14:22:15 +0200
commitecaf665a05bdfcd10937152c378cfaec7cdf1836 (patch)
treeb161f5fa94a054b672b81ff1826fbf3ca388975e
parentfa708e6ed52115026f5dd09d270445cfb1b9f26a (diff)
downloadcompustream-ecaf665a05bdfcd10937152c378cfaec7cdf1836.tar
compustream-ecaf665a05bdfcd10937152c378cfaec7cdf1836.tar.gz
compustream-ecaf665a05bdfcd10937152c378cfaec7cdf1836.tar.bz2
compustream-ecaf665a05bdfcd10937152c378cfaec7cdf1836.tar.xz
compustream-ecaf665a05bdfcd10937152c378cfaec7cdf1836.zip
Experimental visualization of the velocity curl
Calculating the curl of our simulated velocity field requires an additional compute shader step. Handling of buffer and shader switching depending on the display mode is implemented rudimentarily for now. Most of this commit is scaffolding, the actual computation is more or less trivial: ``` const float dxvy = (getFluidVelocity(x+1,y).y - getFluidVelocity(x-1,y).y) / (2*convLength); const float dyvx = (getFluidVelocity(x,y+1).x - getFluidVelocity(x,y-1).x) / (2*convLength); setFluidExtra(x, y, dxvy - dyvx); ``` This implements the following discretization of the 2d curl operator: Let $V : \mathbb{N}^2 \to \mathbb{R}^2$ be the simulated velocity field at discrete lattice points spaced by $\Delta x \in \mathbb{R}_{\gt 0}$. We want to approximate the $z$-component of the curl for visualization: $$\omega := \partial_x F_y - \partial_y F_x$$ As we do not possess the actual function $F$ but only its values at a set of discrete points we approximate the two partial derivatives using a second order central difference scheme: $$\overline{\omega}(i,j) := \frac{F_y(i+1,j) - F_y(i-1,j)}{2 \Delta x} - \frac{F_x(i,j+1) - F_x(i,j-1)}{2 \Delta x}$$ Note that the scene shader does some further rescaling of the curl to better fit the color palette. One issue that irks me is the emergence of some artefacts near boundaries as well as isolated "single-cell-vortices". This might be caused by running the simulation too close to divergence but as I am currently mostly interested in building an interactive fluid playground it could be worth it to try running an additional smoothening shader pass to straighten things out.
-rw-r--r--src/buffer/vertex/fluid_cell_buffer.cc12
-rw-r--r--src/buffer/vertex/fluid_cell_buffer.h1
-rw-r--r--src/main.cc64
-rw-r--r--src/shader/code/extra.glsl53
-rw-r--r--src/shader/code/interact.glsl2
-rw-r--r--src/shader/code/vertex.glsl52
6 files changed, 164 insertions, 20 deletions
diff --git a/src/buffer/vertex/fluid_cell_buffer.cc b/src/buffer/vertex/fluid_cell_buffer.cc
index 8fdc579..d9af989 100644
--- a/src/buffer/vertex/fluid_cell_buffer.cc
+++ b/src/buffer/vertex/fluid_cell_buffer.cc
@@ -5,11 +5,8 @@
FluidCellBuffer::FluidCellBuffer(GLuint nX, GLuint nY, std::function<int(int,int)>&& geometry):
_nX(nX), _nY(nY) {
glGenBuffers(1, &_buffer);
- glBindVertexArray(_array);
+ enable();
init(std::forward<decltype(geometry)>(geometry));
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
-
}
FluidCellBuffer::~FluidCellBuffer() {
@@ -17,9 +14,14 @@ FluidCellBuffer::~FluidCellBuffer() {
glDeleteVertexArrays(1, &_array);
}
-void FluidCellBuffer::init(std::function<int(int,int)>&& geometry) {
+void FluidCellBuffer::enable() {
+ glBindVertexArray(_array);
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
+}
+void FluidCellBuffer::init(std::function<int(int,int)>&& geometry) {
std::vector<GLfloat> data(3*_nX*_nY, GLfloat{});
for ( int x = 0; x < _nX; ++x ) {
diff --git a/src/buffer/vertex/fluid_cell_buffer.h b/src/buffer/vertex/fluid_cell_buffer.h
index 791ebea..5148606 100644
--- a/src/buffer/vertex/fluid_cell_buffer.h
+++ b/src/buffer/vertex/fluid_cell_buffer.h
@@ -16,6 +16,7 @@ public:
FluidCellBuffer(GLuint nX, GLuint nY, std::function<int(int,int)>&& geometry);
~FluidCellBuffer();
+ void enable();
void init(std::function<int(int,int)>&& geometry);
GLuint getBuffer() const;
diff --git a/src/main.cc b/src/main.cc
index 394248c..a98ca44 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -20,6 +20,7 @@
#include "shader/code/interact.glsl"
#include "shader/code/collide.glsl"
+#include "shader/code/extra.glsl"
#include "timer.h"
@@ -27,6 +28,12 @@ GLuint maxLUPS = 100;
GLuint nX = 512;
GLuint nY = 256;
+enum DisplayMode {
+ VELOCITY,
+ QUALITY,
+ CURL
+};
+
float getWorldHeight(int window_width, int window_height, float world_width) {
return world_width / window_width * window_height;
}
@@ -84,9 +91,11 @@ int render() {
std::unique_ptr<LatticeCellBuffer> lattice_a;
std::unique_ptr<LatticeCellBuffer> lattice_b;
std::unique_ptr<FluidCellBuffer> fluid;
+ std::unique_ptr<FluidCellBuffer> extra;
std::unique_ptr<ComputeShader> interact_shader;
std::unique_ptr<ComputeShader> collide_shader;
+ std::unique_ptr<ComputeShader> extra_shader;
window.init([&]() {
scene_shader = std::make_unique<GraphicShader>(
@@ -94,9 +103,11 @@ int render() {
lattice_a = std::make_unique<LatticeCellBuffer>(nX, nY);
lattice_b = std::make_unique<LatticeCellBuffer>(nX, nY);
+ extra = std::make_unique< FluidCellBuffer>(nX, nY, setupOpenGeometry);
fluid = std::make_unique< FluidCellBuffer>(nX, nY, setupOpenGeometry);
interact_shader = std::make_unique<ComputeShader>(INTERACT_SHADER_CODE);
+ extra_shader = std::make_unique<ComputeShader>(EXTRA_SHADER_CODE);
collide_shader = std::make_unique<ComputeShader>(COLLIDE_SHADER_CODE);
});
@@ -110,8 +121,10 @@ int render() {
auto pause_key = window.getKeyWatcher(GLFW_KEY_SPACE);
bool update_lattice = true;
- auto toggle_key = window.getKeyWatcher(GLFW_KEY_T);
- bool show_fluid_quality = false;
+ auto velocity_mode_key = window.getKeyWatcher(GLFW_KEY_V);
+ auto quality_mode_key = window.getKeyWatcher(GLFW_KEY_Q);
+ auto curl_mode_key = window.getKeyWatcher(GLFW_KEY_C);
+ DisplayMode display_mode = DisplayMode::VELOCITY;
auto palette_factor_incr = window.getKeyWatcher(GLFW_KEY_UP);
auto palette_factor_decr = window.getKeyWatcher(GLFW_KEY_DOWN);
@@ -125,8 +138,8 @@ int render() {
float currLatticeMouseX;
float currLatticeMouseY;
- auto tick_buffers = { lattice_a->getBuffer(), lattice_b->getBuffer(), fluid->getBuffer() };
- auto tock_buffers = { lattice_b->getBuffer(), lattice_a->getBuffer(), fluid->getBuffer() };
+ auto tick_buffers = { lattice_a->getBuffer(), lattice_b->getBuffer(), fluid->getBuffer(), extra->getBuffer() };
+ auto tock_buffers = { lattice_b->getBuffer(), lattice_a->getBuffer(), fluid->getBuffer(), extra->getBuffer() };
GLuint iT = 0;
int statLUPS = 0;
@@ -146,14 +159,22 @@ int render() {
update_lattice = !update_lattice;
}
- if ( toggle_key.wasClicked() ) {
- show_fluid_quality = !show_fluid_quality;
+ if ( velocity_mode_key.wasClicked() ) {
+ display_mode = DisplayMode::VELOCITY;
+ fluid->enable();
+ }
+ if ( quality_mode_key.wasClicked() ) {
+ display_mode = DisplayMode::QUALITY;
+ fluid->enable();
+ }
+ if ( curl_mode_key.wasClicked() ) {
+ display_mode = DisplayMode::CURL;
+ extra->enable();
}
if ( palette_factor_incr.wasClicked() ) {
palette_factor += 1;
}
-
if ( palette_factor_decr.wasClicked() ) {
palette_factor -= 1;
}
@@ -175,10 +196,12 @@ int render() {
if ( tick ) {
interact_shader->workOn(tick_buffers);
collide_shader->workOn(tick_buffers);
+ extra_shader->workOn(tick_buffers);
tick = false;
} else {
interact_shader->workOn(tock_buffers);
collide_shader->workOn(tock_buffers);
+ extra_shader->workOn(tock_buffers);
tick = true;
}
@@ -186,13 +209,18 @@ int render() {
{
auto guard = collide_shader->use();
- collide_shader->setUniform("show_fluid_quality", show_fluid_quality);
+ collide_shader->setUniform("show_fluid_quality", display_mode == DisplayMode::QUALITY);
collide_shader->setUniform("iT", iT);
iT += 1;
collide_shader->dispatch(nX, nY);
}
+ if ( display_mode == DisplayMode::CURL ) {
+ auto guard = extra_shader->use();
+ extra_shader->dispatch(nX, nY);
+ }
+
last_lattice_update = timer::now();
}
@@ -228,11 +256,27 @@ int render() {
scene_shader->setUniform("MVP", MVP);
scene_shader->setUniform("nX", nX);
scene_shader->setUniform("nY", nY);
- scene_shader->setUniform("show_fluid_quality", show_fluid_quality);
scene_shader->setUniform("palette_factor", palette_factor);
glClear(GL_COLOR_BUFFER_BIT);
- fluid->draw();
+
+ switch ( display_mode ) {
+ case DisplayMode::VELOCITY:
+ scene_shader->setUniform("show_fluid_quality", false);
+ scene_shader->setUniform("show_curl", false);
+ fluid->draw();
+ break;
+ case DisplayMode::QUALITY:
+ scene_shader->setUniform("show_fluid_quality", true);
+ scene_shader->setUniform("show_curl", false);
+ fluid->draw();
+ break;
+ case DisplayMode::CURL:
+ scene_shader->setUniform("show_fluid_quality", false);
+ scene_shader->setUniform("show_curl", true);
+ extra->draw();
+ break;
+ }
}
});
diff --git a/src/shader/code/extra.glsl b/src/shader/code/extra.glsl
new file mode 100644
index 0000000..5ba6207
--- /dev/null
+++ b/src/shader/code/extra.glsl
@@ -0,0 +1,53 @@
+static const std::string EXTRA_SHADER_CODE = R"(
+#version 430
+
+layout (local_size_x = 1, local_size_y = 1) in;
+
+layout (std430, binding=3) buffer bufferFluid { float fluidCells[]; };
+layout (std430, binding=4) buffer bufferExtra { float extraCells[]; };
+
+uniform uint nX;
+uniform uint nY;
+
+const float convLength = 1.0 / float(max(nX,nY));
+
+/// Array indexing
+
+uint indexOfDirection(int i, int j) {
+ return 3*(i+1) + (j+1);
+}
+
+uint indexOfFluidVertex(uint x, uint y) {
+ return 3*nX*y + 3*x;
+}
+
+/// Data access
+
+vec2 getFluidVelocity(uint x, uint y) {
+ const uint idx = indexOfFluidVertex(x, y);
+ return vec2(
+ fluidCells[idx + 0],
+ fluidCells[idx + 1]
+ );
+}
+
+void setFluidExtra(uint x, uint y, float curl) {
+ const uint idx = indexOfFluidVertex(x, y);
+ extraCells[idx + 0] = curl;
+}
+
+void main() {
+ const uint x = gl_GlobalInvocationID.x;
+ const uint y = gl_GlobalInvocationID.y;
+
+ if ( !(x < nX && y < nY) ) {
+ return;
+ }
+
+ // simple central difference discretization of the 2d curl operator
+ const float dxvy = (getFluidVelocity(x+1,y).y - getFluidVelocity(x-1,y).y) / (2*convLength);
+ const float dyvx = (getFluidVelocity(x,y+1).x - getFluidVelocity(x,y-1).x) / (2*convLength);
+
+ setFluidExtra(x, y, dxvy - dyvx);
+}
+)";
diff --git a/src/shader/code/interact.glsl b/src/shader/code/interact.glsl
index 613c4cd..141673a 100644
--- a/src/shader/code/interact.glsl
+++ b/src/shader/code/interact.glsl
@@ -4,6 +4,7 @@ static const std::string INTERACT_SHADER_CODE = R"(
layout (local_size_x = 1, local_size_y = 1) in;
layout (std430, binding=3) buffer bufferFluid { float fluidCells[]; };
+layout (std430, binding=4) buffer bufferExtra { float extraCells[]; };
uniform uint nX;
uniform uint nY;
@@ -62,6 +63,7 @@ int getMaterial(uint x, uint y) {
void setMaterial(uint x, uint y, int m) {
const uint idx = indexOfFluidVertex(x, y);
fluidCells[idx + 2] = m;
+ extraCells[idx + 2] = m;
}
/// Geometry cleanup
diff --git a/src/shader/code/vertex.glsl b/src/shader/code/vertex.glsl
index 0f0e07c..5136023 100644
--- a/src/shader/code/vertex.glsl
+++ b/src/shader/code/vertex.glsl
@@ -11,6 +11,7 @@ uniform uint nX;
uniform uint nY;
uniform bool show_fluid_quality;
+uniform bool show_curl;
uniform int palette_factor;
float unit(float x) {
@@ -65,6 +66,38 @@ vec3 trafficLightPalette(float x) {
}
}
+vec3 blueBlackRedPalette(float x) {
+ if ( x < 0.5 ) {
+ return mix(
+ vec3(0.0, 0.0, 1.0),
+ vec3(0.0, 0.0, 0.0),
+ 2*x
+ );
+ } else {
+ return mix(
+ vec3(0.0, 0.0, 0.0),
+ vec3(1.0, 0.0, 0.0),
+ 2*(x - 0.5)
+ );
+ }
+}
+
+vec3 blackGoldPalette(float x) {
+ return mix(
+ vec3(0.0, 0.0, 0.0),
+ vec3(0.5, 0.35, 0.05),
+ x
+ );
+}
+
+vec3 blueRedPalette(float x) {
+ return mix(
+ vec3(0.0, 0.0, 1.0),
+ vec3(1.0, 0.0, 0.0),
+ x
+ );
+}
+
void main() {
const vec2 idx = fluidVertexAtIndex(gl_VertexID);
@@ -83,12 +116,21 @@ void main() {
vs_out.color = vec3(0.0, 0.0, 0.0);
} else {
if ( show_fluid_quality ) {
- vs_out.color = trafficLightPalette(restrictedQuality(VertexPosition.y));
+ vs_out.color = trafficLightPalette(
+ restrictedQuality(VertexPosition.y)
+ );
+ } else if ( show_curl ) {
+ const float factor = 1.0 / float(100*palette_factor);
+ if ( abs(VertexPosition.x) > 1.0 ) {
+ vs_out.color = blueBlackRedPalette(
+ 0.5 + (VertexPosition.x * factor)
+ );
+ } else {
+ vs_out.color = vec3(0.0,0.0,0.0);
+ }
} else {
- vs_out.color = mix(
- vec3(-0.5, 0.0, 1.0),
- vec3( 1.0, 0.0, 0.0),
- norm(VertexPosition.xy) / float(palette_factor)
+ vs_out.color = blueRedPalette(
+ norm(VertexPosition.xy) / palette_factor
);
}
}