Skip to content

Commit

Permalink
Added the OpenGL ParticleRenderer. #60
Browse files Browse the repository at this point in the history
Owned by the OpenGLRenderer, ParticleRenderer iterates all the ParticleEmitters and updates their Particle vectors and renders them using the particle shader.
Particle data is uploaded to an SSBO and instanced rendered.
  • Loading branch information
MStachowicz committed Oct 11, 2023
1 parent e422ff5 commit 9f6f0d3
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 4 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ PUBLIC Component
PUBLIC Geometry
PUBLIC ECS #SceneSystem.hpp uses ECS storage
PUBLIC Utility
PRIVATE Utility
PRIVATE Platform
)
# System end ----------------------------------------------------------------------------------
Expand Down Expand Up @@ -199,6 +198,8 @@ source/OpenGL/LightPositionRenderer.hpp
source/OpenGL/LightPositionRenderer.cpp
source/OpenGL/DebugRenderer.hpp
source/OpenGL/DebugRenderer.cpp
source/OpenGL/ParticleRenderer.cpp
source/OpenGL/ParticleRenderer.hpp
source/OpenGL/ShadowMapper.hpp
source/OpenGL/ShadowMapper.cpp
source/OpenGL/GLState.hpp
Expand Down
2 changes: 1 addition & 1 deletion source/Application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class Application
mOpenGLRenderer.start_frame();

m_grid_renderer.draw();
mOpenGLRenderer.draw();
mOpenGLRenderer.draw(durationSinceLastRenderTick);
mEditor.draw(durationSinceLastRenderTick);
OpenGL::DebugRenderer::render(mSceneSystem);

Expand Down
18 changes: 18 additions & 0 deletions source/OpenGL/GLSL/particle.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#version 430 core

uniform sampler2D diffuse;

in VS_OUT
{
vec2 tex_coord;
} fs_in;

out vec4 Colour;

void main()
{
Colour = texture(diffuse, fs_in.tex_coord);

if (Colour.a < 0.1)
discard;
}
32 changes: 32 additions & 0 deletions source/OpenGL/GLSL/particle.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#version 430 core

layout (location = 0) in vec3 VertexPosition;
layout (location = 3) in vec2 VertexTexCoord;

struct Particle
{
vec4 position;
vec4 velocity;
};
layout(std430, binding = 3) buffer ParticlesBuffer
{
uint number_of_particles;
Particle particles[];
};

layout(shared) uniform ViewProperties
{
mat4 view;
mat4 projection;
} viewProperties;

out VS_OUT
{
vec2 tex_coord;
} vs_out;

void main()
{
vs_out.tex_coord = VertexTexCoord;
gl_Position = viewProperties.projection * viewProperties.view * vec4(VertexPosition + particles[gl_InstanceID].position.xyz, 1.0);
}
5 changes: 4 additions & 1 deletion source/OpenGL/OpenGLRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ namespace OpenGL
, mScreenTextureShader{"screenTexture"}
, mSkyBoxShader{"skybox"}
, m_phong_renderer{}
, m_particle_renderer{}
, m_light_position_renderer{}
, m_shadow_mapper{p_window}
, m_missing_texture{pTextureSystem.mTextureManager.create(Config::Texture_Directory / "missing.png")}
Expand Down Expand Up @@ -158,7 +159,7 @@ namespace OpenGL
}
}

void OpenGLRenderer::draw()
void OpenGLRenderer::draw(const DeltaTime& delta_time)
{
m_phong_renderer.update_light_data(mSceneSystem.m_scene, m_shadow_mapper.get_depth_map());

Expand Down Expand Up @@ -217,6 +218,8 @@ namespace OpenGL
draw(*p_mesh.mModel);
});

m_particle_renderer.update(delta_time, mSceneSystem.m_scene, glm::vec3());

renderDebug();
}

Expand Down
4 changes: 3 additions & 1 deletion source/OpenGL/OpenGLRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "PhongRenderer.hpp"
#include "LightPositionRenderer.hpp"
#include "ShadowMapper.hpp"
#include "OpenGL/ParticleRenderer.hpp"

// GEOMETRY
#include "Cylinder.hpp"
Expand Down Expand Up @@ -136,6 +137,7 @@ namespace OpenGL
Shader mSkyBoxShader;

PhongRenderer m_phong_renderer;
ParticleRenderer m_particle_renderer;
LightPositionRenderer m_light_position_renderer;
ShadowMapper m_shadow_mapper;
TextureRef m_missing_texture;
Expand All @@ -156,7 +158,7 @@ namespace OpenGL
void start_frame();
void end_frame();
// Draw the current state of the ECS.
void draw();
void draw(const DeltaTime& delta_time);

private:

Expand Down
128 changes: 128 additions & 0 deletions source/OpenGL/ParticleRenderer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include "ParticleRenderer.hpp"

#include "System/SceneSystem.hpp"

#include "OpenGL/GLState.hpp"
#include "OpenGL/Types.hpp"

#include <random>

namespace OpenGL
{
ParticleRenderer::ParticleRenderer()
: m_particle_shader{"particle"}
, m_quad_VAO{}
, m_quad_VBO{}
, m_quad_EBO{}
, m_particle_buffer{m_particle_shader.get_SSBO_backing("ParticlesBuffer").value()}
{// Prepare the quad buffer for rendering.
m_quad_VAO.bind();
m_quad_VBO.bind();
constexpr auto size = sizeof(float) * quad_vertices.size();
buffer_data(BufferType::ArrayBuffer, size, &quad_vertices.front(), BufferUsage::StaticDraw);
vertex_attrib_pointer(0, 3, ShaderDataType::Float, false, sizeof(float) * 5, (void*)0); // Position
enable_vertex_attrib_array(0);
vertex_attrib_pointer(3, 2, ShaderDataType::Float, false, sizeof(float) * 5, (void*)(3 * sizeof(float))); // Texture
enable_vertex_attrib_array(3);
m_quad_EBO.bind();
buffer_data(BufferType::ElementArrayBuffer, sizeof(quad_indices), &quad_indices.front(), BufferUsage::StaticDraw);
}

void ParticleRenderer::update(const DeltaTime& p_delta_time, System::Scene& p_scene, const glm::vec3& p_camera_position)
{
p_scene.m_entities.foreach([&](Component::ParticleEmitter& p_emitter)
{
constexpr auto zero_seconds = std::chrono::seconds(0);

{ // Spawning new particles every spawn_period
p_emitter.time_to_next_spawn -= p_delta_time;
if (p_emitter.time_to_next_spawn <= zero_seconds)
{
p_emitter.time_to_next_spawn = p_emitter.spawn_period; // Reset time.

ASSERT(p_emitter.particles.size() <= p_emitter.max_particle_count, "Particles container grew larger than max particle count limit!");
auto remaining_size = p_emitter.max_particle_count - p_emitter.particles.size();
auto new_particle_count = std::min(remaining_size, p_emitter.spawn_count);

if (new_particle_count > 0)
{// Create random number generators for each component
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<float> distX(p_emitter.emit_velocity_min.x, p_emitter.emit_velocity_max.x);
std::uniform_real_distribution<float> distY(p_emitter.emit_velocity_min.y, p_emitter.emit_velocity_max.y);
std::uniform_real_distribution<float> distZ(p_emitter.emit_velocity_min.z, p_emitter.emit_velocity_max.z);

for (auto i = 0; i < new_particle_count; i++)
p_emitter.particles.push_back({glm::vec4(p_emitter.emit_position, 1.f), glm::vec4(distX(gen), distY(gen), distZ(gen), 1.f), p_emitter.lifetime});
}
}
}
{// Update particle lifetimes and positions.
// When the lifetime of a particle is below zero, remove it from the vector.
// Otherwise, integrate its position by the velocity.
for (auto it = p_emitter.particles.begin(); it != p_emitter.particles.end();)
{
it->lifetime -= p_delta_time;

if (it->lifetime <= zero_seconds)
it = p_emitter.particles.erase(it);
else
{
it->position += (it->velocity * p_delta_time.count());
++it;
}
}
}

{// Upload the particle data to the SSBO in particle_shader
m_particle_buffer->bind();

// TODO: set these by querying the SSBO using GL introspection.
GLint particle_count_offset = 0;
GLint particle_count_size = 16; // Size of the uint number_of_particles variable.
GLint particle_array_start_offset = 16; // Starts after number_of_particles + padding.
GLint particle_position_offset = 0; // Offset from the start of an index to the position var.
GLint particle_velocity_offset = 16; // Offset from the start of an index to the velocity var.
GLintptr particle_stride = 32; // Size of one particle

auto particle_count = p_emitter.particles.size();
const auto required_size = particle_count_size + particle_count * particle_stride;
// Resize the buffer to accomodate at least the directional_light_count
if (required_size > m_particle_buffer->m_size)
{
LOG("[OPENGL][PARTICLE RENDERER] ParticleEmitter particle count changed ({}), resized the particles buffer to {}B", particle_count, required_size);
auto grow_size = required_size - m_particle_buffer->m_size;

buffer_data(BufferType::ShaderStorageBuffer, required_size, NULL, BufferUsage::StaticDraw);
m_particle_buffer->m_size = required_size;
// Binding point = 3?
bind_buffer_range(BufferType::ShaderStorageBuffer, m_particle_buffer->m_binding_point, m_particle_buffer->m_handle, 0, m_particle_buffer->m_size);

if (particle_count_offset > particle_array_start_offset)
{ // The var is after the variable sized array, update its offset by the growth of the variable-sized-array.
particle_count_offset += grow_size;
}
}

// Set the count
auto uint_particle_count = static_cast<unsigned int>(particle_count);
buffer_sub_data(BufferType::ShaderStorageBuffer, particle_count_offset, sizeof(unsigned int), &uint_particle_count);

GLuint i = 0;
for (auto i = 0; i < p_emitter.particles.size(); i++)
{
buffer_sub_data(BufferType::ShaderStorageBuffer, particle_array_start_offset + particle_position_offset + (particle_stride * i), sizeof(glm::vec4), &p_emitter.particles[i].position);
buffer_sub_data(BufferType::ShaderStorageBuffer, particle_array_start_offset + particle_velocity_offset + (particle_stride * i), sizeof(glm::vec4), &p_emitter.particles[i].velocity);
}
}
{ // Draw the particles
m_particle_shader.use();
m_particle_shader.set_uniform("diffuse", 0);
active_texture(0);
p_emitter.diffuse->m_GL_texture.bind();
m_quad_VAO.bind();
draw_elements_instanced(PrimitiveMode::Triangles, 6, p_emitter.particles.size());
}
});
}
}
45 changes: 45 additions & 0 deletions source/OpenGL/ParticleRenderer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include "OpenGL/Shader.hpp"
#include "OpenGL/Types.hpp"

#include "Component/ParticleEmitter.hpp"
#include "Component/Mesh.hpp"

#include "Utility/Config.hpp"

#include "glm/fwd.hpp"

#include <array>

namespace System
{
class Scene;
}
namespace OpenGL
{
class ParticleRenderer
{
static constexpr std::array<float, 20> quad_vertices = {
// Positions // Texture coords
1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // Top right
1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // Bottom right
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // Bottom left
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f // Top left
};
static constexpr std::array<unsigned int, 6> quad_indices = {
3, 2, 1, // Second triangle
3, 1, 0 // First triangle
};

Shader m_particle_shader;
VAO m_quad_VAO;
VBO m_quad_VBO;
EBO m_quad_EBO;
Utility::ResourceRef<SSBO> m_particle_buffer;
public:
ParticleRenderer();

void update(const DeltaTime& p_delta_time, System::Scene& p_scene, const glm::vec3& p_camera_position);
};
}

0 comments on commit 9f6f0d3

Please sign in to comment.