Browse Source

gpu particle system

Kajetan Johannes Hammerle 3 years ago
parent
commit
2485fde842
6 changed files with 203 additions and 18 deletions
  1. 102 15
      Game.cpp
  2. 11 2
      Game.h
  3. 8 1
      Main.cpp
  4. 11 0
      resources/particles.fs
  5. 31 0
      resources/particles.gs
  6. 40 0
      resources/particles.vs

+ 102 - 15
Game.cpp

@@ -9,12 +9,13 @@
 #include "gaming-core/utils/Utils.h"
 #include "gaming-core/wrapper/GL.h"
 
-Game::Game(Shader& shader, Shader& noiceShader, LayeredFramebuffer& buffer,
-           Buttons& buttons, const Size& size)
-    : shader(shader), noiceShader(noiceShader), noiceBuffer(buffer),
-      bricks("resources/bricks.png"), bricksBump("resources/bricks_bump.png"),
+Game::Game(Shader& shader, Shader& noiceShader, Shader& particleShader,
+           LayeredFramebuffer& buffer, Buttons& buttons, const Size& size)
+    : shader(shader), noiceShader(noiceShader), particleShader(particleShader),
+      noiceBuffer(buffer), bricks("resources/bricks.png"),
+      bricksBump("resources/bricks_bump.png"),
       bricksNormal("resources/bricks_normal.png"), buttons(buttons), size(size),
-      frustum(60, 0.1f, 1000.0f, size), up(GLFW_KEY_SPACE, "Up"),
+      frustum(60.0f, 0.1f, 1000.0f, size), up(GLFW_KEY_SPACE, "Up"),
       down(GLFW_KEY_LEFT_SHIFT, "Down"), left(GLFW_KEY_A, "left"),
       right(GLFW_KEY_D, "right"), front(GLFW_KEY_W, "front"),
       back(GLFW_KEY_S, "back"), toggle(GLFW_KEY_T, "toggle"),
@@ -22,8 +23,11 @@ Game::Game(Shader& shader, Shader& noiceShader, LayeredFramebuffer& buffer,
       stepsUp(GLFW_KEY_Y, "steps up"), stepsDown(GLFW_KEY_U, "steps down"),
       fineStepsUp(GLFW_KEY_I, "fine steps up"),
       fineStepsDown(GLFW_KEY_O, "fine steps down"),
-      modeToggle(GLFW_KEY_C, "mode toggle"), oldHeight(0.0f), height(0.0f),
-      heightScale(0.01f), steps(1), fineSteps(1), mode(false) {
+      modeToggle(GLFW_KEY_C, "mode toggle"),
+      primaryMouse(GLFW_MOUSE_BUTTON_1, "primary click"),
+      timeUp(GLFW_KEY_N, "time up"), timeDown(GLFW_KEY_M, "time down"),
+      oldHeight(0.0f), height(0.0f), heightScale(0.01f), steps(1), fineSteps(1),
+      mode(false), time(0.0f), emitterAge(999999.0f), timeFactor(1.0f) {
     buttons.add(up);
     buttons.add(down);
     buttons.add(left);
@@ -38,6 +42,10 @@ Game::Game(Shader& shader, Shader& noiceShader, LayeredFramebuffer& buffer,
     buttons.add(fineStepsUp);
     buttons.add(fineStepsDown);
     buttons.add(modeToggle);
+    buttons.add(timeUp);
+    buttons.add(timeDown);
+
+    buttons.addMouse(primaryMouse);
 
     bricks.setLinearFilter();
     bricksBump.setLinearFilter();
@@ -68,7 +76,8 @@ void Game::render(float lag) {
     shader.use();
     GL::bindMainFramebuffer();
     GL::clear();
-    shader.setMatrix("proj", frustum.updateProjection().getValues());
+    Matrix& proj = frustum.updateProjection();
+    shader.setMatrix("proj", proj.getValues());
 
     Matrix m;
     m.translate(Utils::interpolate(oldPosition, position, lag));
@@ -92,16 +101,94 @@ void Game::render(float lag) {
     bricksNormal.bindTo(3);
     emptyBuffer.drawPoints(64 * 64 * 64);
     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+    glPointSize(5.0f);
+    particleShader.use();
+    particleShader.setMatrix("proj", proj.getValues());
+    m.translateTo(Utils::interpolate(oldPosition, position, lag));
+    m.translateY(-32.0f - (oldHeight + (height - oldHeight) * lag));
+    particleShader.setMatrix("view", m.getValues());
+    particleShader.setFloat("time", time + lag);
+
+    GL::enableBlending();
+    glDepthMask(false);
+
+    if(primaryMouse.wasReleased()) {
+        noiceBuffer.bindTextureTo(0);
+        static float buffer[64][64][64];
+        glGetTexImage(GL_TEXTURE_3D, 0, GL_RED, GL_FLOAT, buffer);
+
+        float hWidth = size.width * 0.5f;
+        float hHeight = size.height * 0.5f;
+
+        float x = (buttons.getMouseX() - hWidth) / hWidth;
+        float y = -(buttons.getMouseY() - hHeight) / hHeight;
+
+        float aspect = hWidth / hHeight;
+        float tan = tanf((0.5f * frustum.fieldOfView) * M_PI / 180.0f);
+        float q = 1.0f / tan;
+
+        Vector3 direction(x / (q / aspect), y / q, 1.0f);
+        direction[2] = -direction[2];
+        direction.normalize();
+
+        Vector3 pos = -position;
+        pos[1] = 32.0f;
+        pos[1] = 32.0f;
+        for(int i = 0; i < 150; i++) {
+            int x = pos[0] + 0.5f;
+            int y = pos[1] + 0.5f;
+            int z = pos[2] + 0.5f;
+            if(x >= 0 && x < 64 && y >= 0 && y < 64 && z >= 0 && z < 64 &&
+               buffer[x][y][z] > 0.5f) {
+                emitterPos = pos + Vector3(0.0f, height, 0.0f);
+                emitterAge = time;
+                break;
+            }
+            pos += direction;
+        }
+    }
+    particleShader.setFloat("timeFactor", timeFactor);
+
+    particleShader.setVector("position", emitterPos);
+    particleShader.setFloat("age", emitterAge);
+    particleShader.setVector("color", Vector4(1.0f, 0.0f, 0.0f, 1.0f));
+    particleShader.setInt("seedBase", 0);
+    emptyBuffer.drawPoints(2000000);
+
+    particleShader.setVector("position", emitterPos);
+    particleShader.setFloat("age", emitterAge + 5);
+    particleShader.setVector("color", Vector4(0.0f, 1.0f, 0.0f, 1.0f));
+    particleShader.setInt("seedBase", 1);
+    emptyBuffer.drawPoints(10000);
+
+    particleShader.setVector("position", emitterPos);
+    particleShader.setFloat("age", emitterAge + 10);
+    particleShader.setVector("color", Vector4(0.0f, 0.0f, 1.0f, 1.0f));
+    particleShader.setInt("seedBase", 2);
+    emptyBuffer.drawPoints(5000);
+
+    particleShader.setVector("position", emitterPos);
+    particleShader.setFloat("age", emitterAge + 20);
+    particleShader.setVector("color", Vector4(0.0f, 1.0f, 1.0f, 1.0f));
+    particleShader.setInt("seedBase", 2);
+    emptyBuffer.drawPoints(5000);
+
+    glDepthMask(true);
+    GL::disableBlending();
 }
 
 void Game::tick() {
-    StringBuffer<50>()
-        .append(steps)
-        .append(" ")
-        .append(fineSteps)
-        .append(" ")
-        .append(heightScale)
-        .printLine();
+    if(timeUp.isDown()) {
+        timeFactor *= 1.05f;
+        std::cout << timeFactor << "\n";
+    }
+    if(timeDown.isDown()) {
+        timeFactor /= 1.05f;
+        std::cout << timeFactor << "\n";
+    }
+
+    time++;
     oldHeight = height;
     oldPosition = position;
     if(up.isDown()) {

+ 11 - 2
Game.h

@@ -13,6 +13,7 @@
 class Game final {
     Shader& shader;
     Shader& noiceShader;
+    Shader& particleShader;
     LayeredFramebuffer& noiceBuffer;
     FileTexture bricks;
     FileTexture bricksBump;
@@ -37,6 +38,9 @@ class Game final {
     Button fineStepsUp;
     Button fineStepsDown;
     Button modeToggle;
+    Button primaryMouse;
+    Button timeUp;
+    Button timeDown;
 
     Vector3 oldPosition;
     Vector3 position;
@@ -47,10 +51,15 @@ class Game final {
     int steps;
     int fineSteps;
     bool mode;
+    float time;
+
+    Vector3 emitterPos;
+    float emitterAge;
+    float timeFactor;
 
 public:
-    Game(Shader& shader, Shader& noiceShader, LayeredFramebuffer& buffer,
-         Buttons& buttons, const Size& size);
+    Game(Shader& shader, Shader& noiceShader, Shader& particleShader,
+         LayeredFramebuffer& buffer, Buttons& buttons, const Size& size);
 
     void render(float lag);
     void tick();

+ 8 - 1
Main.cpp

@@ -26,12 +26,19 @@ int main() {
         return 0;
     }
 
+    Shader particleShader("resources/particles.vs", "resources/particles.fs",
+                          "resources/particles.gs");
+    if(particleShader.hasError()) {
+        return 0;
+    }
+
     LayeredFramebuffer buffer(64, 64, 64);
     if(buffer.hasError()) {
         return 0;
     }
 
-    Game game(shader, noiceShader, buffer, w.getButtons(), w.getSize());
+    Game game(shader, noiceShader, particleShader, buffer, w.getButtons(),
+              w.getSize());
     GL::printError("setup error");
     GL::enableDepthTesting();
     w.run(game, 50'000'000);

+ 11 - 0
resources/particles.fs

@@ -0,0 +1,11 @@
+#version 430
+
+uniform vec4 color;
+
+in float varFadeG;
+
+out vec4 outColor;
+
+void main(void) {
+    outColor = color * varFadeG;
+}

+ 31 - 0
resources/particles.gs

@@ -0,0 +1,31 @@
+#version 430
+
+layout (points) in;
+layout (triangle_strip, max_vertices = 3) out;
+
+uniform mat4 proj;
+uniform mat4 view;
+
+in float varFade[];
+in vec3 varOffsetA[];
+in vec3 varOffsetB[];
+
+out float varFadeG;
+
+void main(void) {	
+    vec4 base = gl_in[0].gl_Position;
+
+    gl_Position = proj * view * vec4(base.xyz, 1.0);
+    varFadeG = varFade[0];
+    EmitVertex();
+
+    gl_Position = proj * view * vec4(base.xyz + varOffsetA[0], 1.0);
+    varFadeG = varFade[0];
+    EmitVertex();
+
+    gl_Position = proj * view * vec4(base.xyz + varOffsetB[0], 1.0);
+    varFadeG = varFade[0];
+    EmitVertex();
+
+    EndPrimitive();
+}

+ 40 - 0
resources/particles.vs

@@ -0,0 +1,40 @@
+#version 430
+
+uniform mat4 proj;
+uniform mat4 view;
+uniform float time;
+uniform float timeFactor;
+uniform float age;
+uniform vec3 position;
+uniform int seedBase;
+
+int seed = 0;
+
+out vec3 varOffsetA;
+out vec3 varOffsetB;
+out float varFade;
+
+float random() {
+    seed = seed * 534534569 + 42334571;
+    return float(seed & 0xFFFFFF) / float(0xFFFFFF);
+}
+
+void main(void) { 
+    seed = gl_VertexID * 3895924481 + seedBase * 2434483;
+    float lengthAngle = random() * 6.283185307;
+    float widthAngle = random() * 3.141592654 - 1.570796327;
+
+    float sinWidth = sin(widthAngle);
+    float cosWidth = cos(widthAngle);
+    float sinLength = sin(lengthAngle);
+    float cosLength = cos(lengthAngle);
+    vec3 v = vec3(cosWidth * cosLength, sinWidth, -sinLength * cosWidth) * random();
+
+    float realTime = max(time - age, 0.0) * timeFactor;
+    float life = random() * 20.0 * float(realTime > 0.0);
+    varFade = max(life - realTime, 0.0f) / life;
+    varOffsetA = vec3(random(), random(), random()) * 2.0 - 1.0;
+    varOffsetB = vec3(random(), random(), random()) * 2.0 - 1.0;
+
+    gl_Position = vec4(position + v * realTime, 1.0);
+}