Skip to content

Commit

Permalink
reduce particle size to 24bytes, small changes
Browse files Browse the repository at this point in the history
  • Loading branch information
BoyBaykiller committed Mar 25, 2024
1 parent 21eb9bb commit 867e174
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ layout(location = 0) out vec4 FragColor;

in InOutVars
{
vec4 Color;
vec3 Color;
} inData;

void main()
{
FragColor = vec4(inData.Color);
FragColor = vec4(inData.Color, 0.25);
}
51 changes: 36 additions & 15 deletions Newtonian-Particle-Simulator/res/shaders/particles/vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
#define EPSILON 0.001
const float DRAG_COEF = log(0.998) * 176.0; // log(0.70303228048)

struct PackedVector3
{
float X, Y, Z;
};

struct Particle
{
vec3 Position;
float _pad0;
vec3 Velocity;
float _pad1;
PackedVector3 Position;
PackedVector3 Velocity;
};

layout(std430, binding = 0) restrict buffer ParticlesSSBO
Expand All @@ -21,16 +24,22 @@ layout(location = 2) uniform float isActive;
layout(location = 3) uniform float isRunning;
layout(location = 4) uniform mat4 projViewMatrix;

vec3 PackedVec3ToVec3(PackedVector3 vec);
PackedVector3 Vec3ToPackedVec3(vec3 vec);

out InOutVars
{
vec4 Color;
vec3 Color;
} outData;

void main()
{
Particle particle = particleSSBO.Particles[gl_VertexID];
PackedVector3 packedPosition = particleSSBO.Particles[gl_VertexID].Position;
PackedVector3 packedVelocity = particleSSBO.Particles[gl_VertexID].Velocity;
vec3 position = PackedVec3ToVec3(packedPosition);
vec3 velocity = PackedVec3ToVec3(packedVelocity);

const vec3 toMass = pointOfMass - particle.Position;
const vec3 toMass = pointOfMass - position;

/// Implementation of Newton's law of gravity
const float m1 = 1.0; // constant particle mass
Expand All @@ -44,15 +53,27 @@ void main()
// acceleration = force / m.
const vec3 acceleration = (force * isRunning * isActive) / m1;

particle.Velocity *= mix(1.0, exp(DRAG_COEF * dT), isRunning); // https://stackoverflow.com/questions/61812575/which-formula-to-use-for-drag-simulation-each-frame
particle.Position += (dT * particle.Velocity + 0.5 * acceleration * dT * dT) * isRunning; // Euler integration
particle.Velocity += acceleration * dT;
particleSSBO.Particles[gl_VertexID] = particle;
velocity *= mix(1.0, exp(DRAG_COEF * dT), isRunning); // https://stackoverflow.com/questions/61812575/which-formula-to-use-for-drag-simulation-each-frame
position += (dT * velocity + 0.5 * acceleration * dT * dT) * isRunning; // Euler integration
velocity += acceleration * dT;

particleSSBO.Particles[gl_VertexID].Position = Vec3ToPackedVec3(position);
particleSSBO.Particles[gl_VertexID].Velocity = Vec3ToPackedVec3(velocity);

const float red = 0.0045 * dot(particle.Velocity, particle.Velocity);
const float green = clamp(0.08 * max(particle.Velocity.x, max(particle.Velocity.y, particle.Velocity.z)), 0.2, 0.5);
const float red = 0.0045 * dot(velocity, velocity);
const float green = clamp(0.08 * max(velocity.x, max(velocity.y, velocity.z)), 0.2, 0.5);
const float blue = 0.7 - red;

outData.Color = vec4(red, green, blue, 0.25);
gl_Position = projViewMatrix * vec4(particle.Position, 1.0);
outData.Color = vec3(red, green, blue);
gl_Position = projViewMatrix * vec4(position, 1.0);
}

vec3 PackedVec3ToVec3(PackedVector3 vec)
{
return vec3(vec.X, vec.Y, vec.Z);
}

PackedVector3 Vec3ToPackedVec3(vec3 vec)
{
return PackedVector3(vec.x, vec.y, vec.z);
}
13 changes: 3 additions & 10 deletions Newtonian-Particle-Simulator/src/Helper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
Expand All @@ -10,13 +9,6 @@ static class Helper
{
// API version as a decimal. For example major 4 and minor 6 => 4.6
public static readonly double APIVersion = Convert.ToDouble($"{GL.GetInteger(GetPName.MajorVersion)}{GL.GetInteger(GetPName.MinorVersion)}") / 10.0;
public static string GetPathContent(this string path)
{
if (!File.Exists(path))
throw new FileNotFoundException($"{path} does not exist");

return File.ReadAllText(path);
}

public static Vector3 RandomUnitVector(Random rng)
{
Expand All @@ -29,17 +21,18 @@ public static Vector3 RandomUnitVector(Random rng)
return new Vector3(x, y, z);
}

private static readonly HashSet<string> glExtensions = new HashSet<string>(GetExtensions());
private static HashSet<string> GetExtensions()
{
HashSet<string> extensions = new HashSet<string>(GL.GetInteger(GetPName.NumExtensions));
for (int i = 0; i < GL.GetInteger(GetPName.NumExtensions); i++)
{
extensions.Add(GL.GetString(StringNameIndexed.Extensions, i));
}

return extensions;
}

private static readonly HashSet<string> glExtensions = new HashSet<string>(GetExtensions());

public static bool IsExtensionsAvailable(string extension)
{
return glExtensions.Contains(extension);
Expand Down
3 changes: 1 addition & 2 deletions Newtonian-Particle-Simulator/src/KeyboardManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using OpenTK;
using OpenTK.Input;
using OpenTK.Input;

namespace Newtonian_Particle_Simulator
{
Expand Down
2 changes: 2 additions & 0 deletions Newtonian-Particle-Simulator/src/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ protected override void OnLoad(EventArgs e)
}
particleSimulator = new ParticleSimulator(particles);

GC.Collect();

base.OnLoad(e);
}

Expand Down
2 changes: 0 additions & 2 deletions Newtonian-Particle-Simulator/src/Particle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ namespace Newtonian_Particle_Simulator
struct Particle
{
public Vector3 Position;
private readonly int _pad0;
public Vector3 Velocity;
private readonly int _pad1;
}
}
62 changes: 5 additions & 57 deletions Newtonian-Particle-Simulator/src/Render/Objects/BufferObject.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Runtime.InteropServices;
using OpenTK.Graphics.OpenGL4;

namespace Newtonian_Particle_Simulator.Render.Objects
Expand All @@ -25,71 +24,20 @@ public void BindBase(BufferRangeTarget bufferRangeTarget, int index)
GL.BindBufferBase(bufferRangeTarget, index, ID);
}

public void Bind(BufferTarget bufferTarget)
public unsafe void ImmutableAllocate<T>(nint size, in T data, BufferStorageFlags bufferStorageFlags) where T : unmanaged
{
GL.BindBuffer(bufferTarget, ID);
fixed (void* ptr = &data)
{
ImmutableAllocate(size, (IntPtr)ptr, bufferStorageFlags);
}
}

public void SubData<T>(nint offset, nint size, T data) where T : struct
{
GL.NamedBufferSubData(ID, offset, size, ref data);
}
public void SubData<T>(nint offset, nint size, T[] data) where T : struct
{
GL.NamedBufferSubData(ID, offset, size, data);
}
public void SubData(nint offset, nint size, IntPtr data)
{
GL.NamedBufferSubData(ID, offset, size, data);
}

public void MutableAllocate<T>(nint size, T data, BufferUsageHint bufferUsageHint) where T : struct
{
GL.NamedBufferData(ID, size, ref data, bufferUsageHint);
Size = size;
}
public void MutableAllocate<T>(nint size, T[] data, BufferUsageHint bufferUsageHint) where T : struct
{
GL.NamedBufferData(ID, size, data, bufferUsageHint);
Size = size;
}
public void MutableAllocate(nint size, IntPtr data, BufferUsageHint bufferUsageHint)
{
GL.NamedBufferData(ID, size, data, bufferUsageHint);
Size = size;
}

public void ImmutableAllocate<T>(nint size, T data, BufferStorageFlags bufferStorageFlags) where T : struct
{
GL.NamedBufferStorage(ID, size, ref data, bufferStorageFlags);
Size = size;
}
public void ImmutableAllocate<T>(nint size, T[] data, BufferStorageFlags bufferStorageFlags) where T : struct
{
GL.NamedBufferStorage(ID, size, data, bufferStorageFlags);
Size = size;
}
public void ImmutableAllocate(nint size, IntPtr data, BufferStorageFlags bufferStorageFlags)
{
GL.NamedBufferStorage(ID, size, data, bufferStorageFlags);
Size = size;
}

public void GetSubData<T>(nint offset, nint size, out T data) where T : struct
{
data = new T();
GL.GetNamedBufferSubData(ID, offset, size, ref data);
}
public void GetSubData<T>(nint offset, nint size, T[] data) where T : struct
{
GL.GetNamedBufferSubData(ID, offset, size, data);
}
public void GetSubData(nint offset, nint size, out IntPtr data)
{
data = Marshal.AllocHGlobal(size);
GL.GetNamedBufferSubData(ID, offset, size, data);
}

public void Dispose()
{
GL.DeleteBuffer(ID);
Expand Down
12 changes: 8 additions & 4 deletions Newtonian-Particle-Simulator/src/Render/ParticleSimulator.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Newtonian_Particle_Simulator.Render.Objects;
using System;
using System.IO;
using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics.OpenGL4;
using Newtonian_Particle_Simulator.Render.Objects;

namespace Newtonian_Particle_Simulator.Render
{
Expand All @@ -10,14 +12,16 @@ class ParticleSimulator
public readonly int NumParticles;
private readonly BufferObject particleBuffer;
private readonly ShaderProgram shaderProgram;
public unsafe ParticleSimulator(Particle[] particles)
public unsafe ParticleSimulator(ReadOnlySpan<Particle> particles)
{
NumParticles = particles.Length;

shaderProgram = new ShaderProgram(new Shader(ShaderType.VertexShader, "res/shaders/particles/vertex.glsl".GetPathContent()), new Shader(ShaderType.FragmentShader, "res/shaders/particles/fragment.glsl".GetPathContent()));
shaderProgram = new ShaderProgram(
new Shader(ShaderType.VertexShader, File.ReadAllText("res/shaders/particles/vertex.glsl")),
new Shader(ShaderType.FragmentShader, File.ReadAllText("res/shaders/particles/fragment.glsl")));

particleBuffer = new BufferObject(BufferRangeTarget.ShaderStorageBuffer, 0);
particleBuffer.ImmutableAllocate(sizeof(Particle) * (nint)NumParticles, particles, BufferStorageFlags.DynamicStorageBit);
particleBuffer.ImmutableAllocate(sizeof(Particle) * (nint)NumParticles, particles[0], BufferStorageFlags.None);

IsRunning = true;
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
![gif](Video.gif?raw=true)
![img1](sample.png?raw=true)

This is a very simplistic particle engine.
This is a very simple interactive particle simulator.
The whole pipeline consists out of a [single shader program](Newtonian-Particle-Simulator/res/shaders/particles).
The vertex shader computes and renders particles at the same time. The fragment shader just outputs the computed colors with blending enabled.
The actual particles are stored in a Shader Storage Buffer which is a really useful arbitrary read/write interface of global GPU memory.
Expand Down

0 comments on commit 867e174

Please sign in to comment.