summaryrefslogtreecommitdiff
path: root/drivers/gles3/shaders/particles.glsl
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2022-11-15 10:28:44 +0100
committerRémi Verschelde <rverschelde@gmail.com>2022-11-15 10:28:44 +0100
commit5f78f24b081b123f480e00fd0cb1dad590ecd964 (patch)
tree47ab1c946a13557c98f162be0a759efef23b7b9d /drivers/gles3/shaders/particles.glsl
parent1e9a61cd404c9fbaf55d465268ca665c7acb4828 (diff)
parent9ce57050a5d12776deedd44fcb82dd5841a56686 (diff)
Merge pull request #68426 from clayjohn/GLES3-particles
Add GPUParticles to the OpenGL3 renderer.
Diffstat (limited to 'drivers/gles3/shaders/particles.glsl')
-rw-r--r--drivers/gles3/shaders/particles.glsl501
1 files changed, 501 insertions, 0 deletions
diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl
new file mode 100644
index 0000000000..f8741a22ab
--- /dev/null
+++ b/drivers/gles3/shaders/particles.glsl
@@ -0,0 +1,501 @@
+/* clang-format off */
+#[modes]
+
+mode_default =
+
+#[specializations]
+
+MODE_3D = false
+USERDATA1_USED = false
+USERDATA2_USED = false
+USERDATA3_USED = false
+USERDATA4_USED = false
+USERDATA5_USED = false
+USERDATA6_USED = false
+
+#[vertex]
+
+#define SDF_MAX_LENGTH 16384.0
+
+layout(std140) uniform GlobalShaderUniformData { //ubo:1
+ vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
+};
+
+// This needs to be outside clang-format so the ubo comment is in the right place
+#ifdef MATERIAL_UNIFORMS_USED
+layout(std140) uniform MaterialUniforms{ //ubo:2
+
+#MATERIAL_UNIFORMS
+
+};
+#endif
+
+/* clang-format on */
+
+#define MAX_ATTRACTORS 32
+
+#define ATTRACTOR_TYPE_SPHERE uint(0)
+#define ATTRACTOR_TYPE_BOX uint(1)
+#define ATTRACTOR_TYPE_VECTOR_FIELD uint(2)
+
+struct Attractor {
+ mat4 transform;
+ vec4 extents; // Extents or radius. w-channel is padding.
+
+ uint type;
+ float strength;
+ float attenuation;
+ float directionality;
+};
+
+#define MAX_COLLIDERS 32
+
+#define COLLIDER_TYPE_SPHERE uint(0)
+#define COLLIDER_TYPE_BOX uint(1)
+#define COLLIDER_TYPE_SDF uint(2)
+#define COLLIDER_TYPE_HEIGHT_FIELD uint(3)
+#define COLLIDER_TYPE_2D_SDF uint(4)
+
+struct Collider {
+ mat4 transform;
+ vec4 extents; // Extents or radius. w-channel is padding.
+
+ uint type;
+ float scale;
+ float pad0;
+ float pad1;
+};
+
+layout(std140) uniform FrameData { //ubo:0
+ bool emitting;
+ uint cycle;
+ float system_phase;
+ float prev_system_phase;
+
+ float explosiveness;
+ float randomness;
+ float time;
+ float delta;
+
+ float particle_size;
+ float pad0;
+ float pad1;
+ float pad2;
+
+ uint random_seed;
+ uint attractor_count;
+ uint collider_count;
+ uint frame;
+
+ mat4 emission_transform;
+
+ Attractor attractors[MAX_ATTRACTORS];
+ Collider colliders[MAX_COLLIDERS];
+};
+
+#define PARTICLE_FLAG_ACTIVE uint(1)
+#define PARTICLE_FLAG_STARTED uint(2)
+#define PARTICLE_FLAG_TRAILED uint(4)
+#define PARTICLE_FRAME_MASK uint(0xFFFF)
+#define PARTICLE_FRAME_SHIFT uint(16)
+
+// ParticleData
+layout(location = 0) in highp vec4 color;
+layout(location = 1) in highp vec4 velocity_flags;
+layout(location = 2) in highp vec4 custom;
+layout(location = 3) in highp vec4 xform_1;
+layout(location = 4) in highp vec4 xform_2;
+#ifdef MODE_3D
+layout(location = 5) in highp vec4 xform_3;
+#endif
+#ifdef USERDATA1_USED
+layout(location = 6) in highp vec4 userdata1;
+#endif
+#ifdef USERDATA2_USED
+layout(location = 7) in highp vec4 userdata2;
+#endif
+#ifdef USERDATA3_USED
+layout(location = 8) in highp vec4 userdata3;
+#endif
+#ifdef USERDATA4_USED
+layout(location = 9) in highp vec4 userdata4;
+#endif
+#ifdef USERDATA5_USED
+layout(location = 10) in highp vec4 userdata5;
+#endif
+#ifdef USERDATA6_USED
+layout(location = 11) in highp vec4 userdata6;
+#endif
+
+out highp vec4 out_color; //tfb:
+out highp vec4 out_velocity_flags; //tfb:
+out highp vec4 out_custom; //tfb:
+out highp vec4 out_xform_1; //tfb:
+out highp vec4 out_xform_2; //tfb:
+#ifdef MODE_3D
+out highp vec4 out_xform_3; //tfb:MODE_3D
+#endif
+#ifdef USERDATA1_USED
+out highp vec4 out_userdata1; //tfb:USERDATA1_USED
+#endif
+#ifdef USERDATA2_USED
+out highp vec4 out_userdata2; //tfb:USERDATA2_USED
+#endif
+#ifdef USERDATA3_USED
+out highp vec4 out_userdata3; //tfb:USERDATA3_USED
+#endif
+#ifdef USERDATA4_USED
+out highp vec4 out_userdata4; //tfb:USERDATA4_USED
+#endif
+#ifdef USERDATA5_USED
+out highp vec4 out_userdata5; //tfb:USERDATA5_USED
+#endif
+#ifdef USERDATA6_USED
+out highp vec4 out_userdata6; //tfb:USERDATA6_USED
+#endif
+
+uniform sampler2D height_field_texture; //texunit:0
+
+uniform float lifetime;
+uniform bool clear;
+uniform uint total_particles;
+uniform bool use_fractional_delta;
+
+uint hash(uint x) {
+ x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
+ x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
+ x = (x >> uint(16)) ^ x;
+ return x;
+}
+
+vec3 safe_normalize(vec3 direction) {
+ const float EPSILON = 0.001;
+ if (length(direction) < EPSILON) {
+ return vec3(0.0);
+ }
+ return normalize(direction);
+}
+
+// Needed whenever 2D sdf texture is read from as it is packed in RGBA8.
+float vec4_to_float(vec4 p_vec) {
+ return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0;
+}
+
+#GLOBALS
+
+void main() {
+ bool apply_forces = true;
+ bool apply_velocity = true;
+ float local_delta = delta;
+
+ float mass = 1.0;
+
+ bool restart = false;
+
+ bool restart_position = false;
+ bool restart_rotation_scale = false;
+ bool restart_velocity = false;
+ bool restart_color = false;
+ bool restart_custom = false;
+
+ mat4 xform = mat4(1.0);
+ uint flags = 0u;
+
+ if (clear) {
+ out_color = vec4(1.0);
+ out_custom = vec4(0.0);
+ out_velocity_flags = vec4(0.0);
+ } else {
+ out_color = color;
+ out_velocity_flags = velocity_flags;
+ out_custom = custom;
+ xform[0] = xform_1;
+ xform[1] = xform_2;
+#ifdef MODE_3D
+ xform[2] = xform_3;
+#endif
+ xform = transpose(xform);
+ flags = floatBitsToUint(velocity_flags.w);
+ }
+
+ //clear started flag if set
+ flags &= ~PARTICLE_FLAG_STARTED;
+
+ bool collided = false;
+ vec3 collision_normal = vec3(0.0);
+ float collision_depth = 0.0;
+
+ vec3 attractor_force = vec3(0.0);
+
+#if !defined(DISABLE_VELOCITY)
+
+ if (bool(flags & PARTICLE_FLAG_ACTIVE)) {
+ xform[3].xyz += out_velocity_flags.xyz * local_delta;
+ }
+#endif
+ uint index = uint(gl_VertexID);
+ if (emitting) {
+ float restart_phase = float(index) / float(total_particles);
+
+ if (randomness > 0.0) {
+ uint seed = cycle;
+ if (restart_phase >= system_phase) {
+ seed -= uint(1);
+ }
+ seed *= uint(total_particles);
+ seed += index;
+ float random = float(hash(seed) % uint(65536)) / 65536.0;
+ restart_phase += randomness * random * 1.0 / float(total_particles);
+ }
+
+ restart_phase *= (1.0 - explosiveness);
+
+ if (system_phase > prev_system_phase) {
+ // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
+
+ if (restart_phase >= prev_system_phase && restart_phase < system_phase) {
+ restart = true;
+ if (use_fractional_delta) {
+ local_delta = (system_phase - restart_phase) * lifetime;
+ }
+ }
+
+ } else if (delta > 0.0) {
+ if (restart_phase >= prev_system_phase) {
+ restart = true;
+ if (use_fractional_delta) {
+ local_delta = (1.0 - restart_phase + system_phase) * lifetime;
+ }
+
+ } else if (restart_phase < system_phase) {
+ restart = true;
+ if (use_fractional_delta) {
+ local_delta = (system_phase - restart_phase) * lifetime;
+ }
+ }
+ }
+
+ if (restart) {
+ flags = emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (cycle << PARTICLE_FRAME_SHIFT)) : 0u;
+ restart_position = true;
+ restart_rotation_scale = true;
+ restart_velocity = true;
+ restart_color = true;
+ restart_custom = true;
+ }
+ }
+
+ bool particle_active = bool(flags & PARTICLE_FLAG_ACTIVE);
+
+ uint particle_number = (flags >> PARTICLE_FRAME_SHIFT) * uint(total_particles) + index;
+
+ if (restart && particle_active) {
+#CODE : START
+ }
+
+ if (particle_active) {
+ for (uint i = 0u; i < attractor_count; i++) {
+ vec3 dir;
+ float amount;
+ vec3 rel_vec = xform[3].xyz - attractors[i].transform[3].xyz;
+ vec3 local_pos = rel_vec * mat3(attractors[i].transform);
+
+ switch (attractors[i].type) {
+ case ATTRACTOR_TYPE_SPHERE: {
+ dir = safe_normalize(rel_vec);
+ float d = length(local_pos) / attractors[i].extents.x;
+ if (d > 1.0) {
+ continue;
+ }
+ amount = max(0.0, 1.0 - d);
+ } break;
+ case ATTRACTOR_TYPE_BOX: {
+ dir = safe_normalize(rel_vec);
+
+ vec3 abs_pos = abs(local_pos / attractors[i].extents.xyz);
+ float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
+ if (d > 1.0) {
+ continue;
+ }
+ amount = max(0.0, 1.0 - d);
+
+ } break;
+ case ATTRACTOR_TYPE_VECTOR_FIELD: {
+ } break;
+ }
+ amount = pow(amount, attractors[i].attenuation);
+ dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality));
+ attractor_force -= amount * dir * attractors[i].strength;
+ }
+
+ float particle_size = particle_size;
+
+#ifdef USE_COLLISION_SCALE
+
+ particle_size *= dot(vec3(length(xform[0].xyz), length(xform[1].xyz), length(xform[2].xyz)), vec3(0.33333333333));
+
+#endif
+
+ if (collider_count == 1u && colliders[0].type == COLLIDER_TYPE_2D_SDF) {
+ //2D collision
+
+ vec2 pos = xform[3].xy;
+ vec4 to_sdf_x = colliders[0].transform[0];
+ vec4 to_sdf_y = colliders[0].transform[1];
+ vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y));
+
+ vec4 sdf_to_screen = vec4(colliders[0].extents.xyz, colliders[0].scale);
+
+ vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw;
+
+ if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) {
+ vec2 pos2 = pos + vec2(0, particle_size);
+ vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y));
+ float sdf_particle_size = distance(sdf_pos, sdf_pos2);
+
+ float d = vec4_to_float(texture(height_field_texture, uv_pos)) * SDF_MAX_LENGTH;
+
+ d -= sdf_particle_size;
+
+ if (d < 0.0) {
+ const float EPSILON = 0.001;
+ vec2 n = normalize(vec2(
+ vec4_to_float(texture(height_field_texture, uv_pos + vec2(EPSILON, 0.0))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(EPSILON, 0.0))),
+ vec4_to_float(texture(height_field_texture, uv_pos + vec2(0.0, EPSILON))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(0.0, EPSILON)))));
+
+ collided = true;
+ sdf_pos2 = sdf_pos + n * d;
+ pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[3]));
+
+ n = pos - pos2;
+
+ collision_normal = normalize(vec3(n, 0.0));
+ collision_depth = length(n);
+ }
+ }
+
+ } else {
+ for (uint i = 0u; i < collider_count; i++) {
+ vec3 normal;
+ float depth;
+ bool col = false;
+
+ vec3 rel_vec = xform[3].xyz - colliders[i].transform[3].xyz;
+ vec3 local_pos = rel_vec * mat3(colliders[i].transform);
+
+ switch (colliders[i].type) {
+ case COLLIDER_TYPE_SPHERE: {
+ float d = length(rel_vec) - (particle_size + colliders[i].extents.x);
+
+ if (d < 0.0) {
+ col = true;
+ depth = -d;
+ normal = normalize(rel_vec);
+ }
+
+ } break;
+ case COLLIDER_TYPE_BOX: {
+ vec3 abs_pos = abs(local_pos);
+ vec3 sgn_pos = sign(local_pos);
+
+ if (any(greaterThan(abs_pos, colliders[i].extents.xyz))) {
+ //point outside box
+
+ vec3 closest = min(abs_pos, colliders[i].extents.xyz);
+ vec3 rel = abs_pos - closest;
+ depth = length(rel) - particle_size;
+ if (depth < 0.0) {
+ col = true;
+ normal = mat3(colliders[i].transform) * (normalize(rel) * sgn_pos);
+ depth = -depth;
+ }
+ } else {
+ //point inside box
+ vec3 axis_len = colliders[i].extents.xyz - abs_pos;
+ // there has to be a faster way to do this?
+ if (all(lessThan(axis_len.xx, axis_len.yz))) {
+ normal = vec3(1, 0, 0);
+ } else if (all(lessThan(axis_len.yy, axis_len.xz))) {
+ normal = vec3(0, 1, 0);
+ } else {
+ normal = vec3(0, 0, 1);
+ }
+
+ col = true;
+ depth = dot(normal * axis_len, vec3(1)) + particle_size;
+ normal = mat3(colliders[i].transform) * (normal * sgn_pos);
+ }
+
+ } break;
+ case COLLIDER_TYPE_SDF: {
+ } break;
+ case COLLIDER_TYPE_HEIGHT_FIELD: {
+ vec3 local_pos_bottom = local_pos;
+ local_pos_bottom.y -= particle_size;
+
+ if (any(greaterThan(abs(local_pos_bottom), colliders[i].extents.xyz))) {
+ continue;
+ }
+ const float DELTA = 1.0 / 8192.0;
+
+ vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5;
+
+ float y = 1.0 - texture(height_field_texture, uvw_pos.xz).r;
+
+ if (y > uvw_pos.y) {
+ //inside heightfield
+
+ vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
+ vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
+ vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz;
+
+ normal = normalize(cross(pos1 - pos2, pos1 - pos3));
+ float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y;
+
+ col = true;
+ depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
+ }
+
+ } break;
+ }
+
+ if (col) {
+ if (!collided) {
+ collided = true;
+ collision_normal = normal;
+ collision_depth = depth;
+ } else {
+ vec3 c = collision_normal * collision_depth;
+ c += normal * max(0.0, depth - dot(normal, c));
+ collision_normal = normalize(c);
+ collision_depth = length(c);
+ }
+ }
+ }
+ }
+ }
+
+ if (particle_active) {
+#CODE : PROCESS
+ }
+
+ flags &= ~PARTICLE_FLAG_ACTIVE;
+ if (particle_active) {
+ flags |= PARTICLE_FLAG_ACTIVE;
+ }
+
+ xform = transpose(xform);
+ out_xform_1 = xform[0];
+ out_xform_2 = xform[1];
+#ifdef MODE_3D
+ out_xform_3 = xform[2];
+#endif
+ out_velocity_flags.w = uintBitsToFloat(flags);
+}
+
+/* clang-format off */
+#[fragment]
+
+void main() {
+}
+/* clang-format on */