summaryrefslogtreecommitdiff
path: root/servers/rendering/rasterizer_rd/shaders/particles.glsl
diff options
context:
space:
mode:
Diffstat (limited to 'servers/rendering/rasterizer_rd/shaders/particles.glsl')
-rw-r--r--servers/rendering/rasterizer_rd/shaders/particles.glsl333
1 files changed, 244 insertions, 89 deletions
diff --git a/servers/rendering/rasterizer_rd/shaders/particles.glsl b/servers/rendering/rasterizer_rd/shaders/particles.glsl
index a924509771..926c7ef9fc 100644
--- a/servers/rendering/rasterizer_rd/shaders/particles.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/particles.glsl
@@ -31,6 +31,40 @@ global_variables;
/* Set 1: FRAME AND PARTICLE DATA */
// a frame history is kept for trail deterministic behavior
+
+#define MAX_ATTRACTORS 32
+
+#define ATTRACTOR_TYPE_SPHERE 0
+#define ATTRACTOR_TYPE_BOX 1
+#define ATTRACTOR_TYPE_VECTOR_FIELD 2
+
+struct Attractor {
+ mat4 transform;
+ vec3 extents; //exents or radius
+ uint type;
+ uint texture_index; //texture index for vector field
+ float strength;
+ float attenuation;
+ float directionality;
+};
+
+#define MAX_COLLIDERS 32
+
+#define COLLIDER_TYPE_SPHERE 0
+#define COLLIDER_TYPE_BOX 1
+#define COLLIDER_TYPE_SDF 2
+#define COLLIDER_TYPE_HEIGHT_FIELD 3
+
+struct Collider {
+ mat4 transform;
+ vec3 extents; //exents or radius
+ uint type;
+
+ uint texture_index; //texture index for vector field
+ float scale;
+ uint pad[2];
+};
+
struct FrameParams {
bool emitting;
float system_phase;
@@ -43,9 +77,14 @@ struct FrameParams {
float delta;
uint random_seed;
- uint pad[3];
+ uint attractor_count;
+ uint collider_count;
+ float particle_size;
mat4 emission_transform;
+
+ Attractor attractors[MAX_ATTRACTORS];
+ Collider colliders[MAX_COLLIDERS];
};
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
@@ -80,7 +119,7 @@ struct ParticleEmission {
vec4 custom;
};
-layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmission {
+layout(set = 1, binding = 2, std430) restrict buffer SourceEmission {
int particle_count;
uint pad0;
uint pad1;
@@ -89,7 +128,7 @@ layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmi
}
src_particles;
-layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmission {
+layout(set = 1, binding = 3, std430) restrict buffer DestEmission {
int particle_count;
int particle_max;
uint pad1;
@@ -98,10 +137,17 @@ layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmiss
}
dst_particles;
-/* SET 2: MATERIAL */
+/* SET 2: COLLIDER/ATTRACTOR TEXTURES */
+
+#define MAX_3D_TEXTURES 7
+
+layout(set = 2, binding = 0) uniform texture3D sdf_vec_textures[MAX_3D_TEXTURES];
+layout(set = 2, binding = 1) uniform texture2D height_field_texture;
+
+/* SET 3: MATERIAL */
#ifdef USE_MATERIAL_UNIFORMS
-layout(set = 2, binding = 0, std140) uniform MaterialUniforms{
+layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
@@ -140,29 +186,7 @@ bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, u
atomicAdd(dst_particles.particle_count, -1);
return false;
}
- /*
- valid = true;
-
- int attempts = 256; // never trust compute
- while(attempts-- > 0) {
- dst_index = dst_particles.particle_count;
- if (dst_index == dst_particles.particle_max) {
- return false; //can't emit anymore
- }
-
- if (atomicCompSwap(dst_particles.particle_count, dst_index, dst_index +1 ) != dst_index) {
- continue;
- }
- valid=true;
- break;
- }
- barrier();
-
- if (!valid) {
- return false; //gave up (attempts exhausted)
- }
-*/
dst_particles.data[dst_index].xform = p_xform;
dst_particles.data[dst_index].velocity = p_velocity;
dst_particles.data[dst_index].color = p_color;
@@ -217,6 +241,199 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0));
}
+ 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 (PARTICLE.is_active) {
+ PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
+ }
+#endif
+
+ /* Process physics if active */
+
+ if (PARTICLE.is_active) {
+ for (uint i = 0; i < FRAME.attractor_count; i++) {
+ vec3 dir;
+ float amount;
+ vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.attractors[i].transform[3].xyz;
+ vec3 local_pos = rel_vec * mat3(FRAME.attractors[i].transform);
+
+ switch (FRAME.attractors[i].type) {
+ case ATTRACTOR_TYPE_SPHERE: {
+ dir = normalize(rel_vec);
+ float d = length(local_pos) / FRAME.attractors[i].extents.x;
+ if (d > 1.0) {
+ continue;
+ }
+ amount = max(0.0, 1.0 - d);
+ } break;
+ case ATTRACTOR_TYPE_BOX: {
+ dir = normalize(rel_vec);
+
+ vec3 abs_pos = abs(local_pos / FRAME.attractors[i].extents);
+ 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: {
+ vec3 uvw_pos = (local_pos / FRAME.attractors[i].extents) * 2.0 - 1.0;
+ if (any(lessThan(uvw_pos, vec3(0.0))) || any(greaterThan(uvw_pos, vec3(1.0)))) {
+ continue;
+ }
+ vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).xyz;
+ dir = mat3(FRAME.attractors[i].transform) * normalize(s); //revert direction
+ amount = length(s);
+
+ } break;
+ }
+ amount = pow(amount, FRAME.attractors[i].attenuation);
+ dir = normalize(mix(dir, FRAME.attractors[i].transform[2].xyz, FRAME.attractors[i].directionality));
+ attractor_force -= amount * dir * FRAME.attractors[i].strength;
+ }
+
+ float particle_size = FRAME.particle_size;
+
+#ifdef USE_COLLISON_SCALE
+
+ particle_size *= dot(vec3(length(PARTICLE.xform[0].xyz), length(PARTICLE.xform[1].xyz), length(PARTICLE.xform[2].xyz)), vec3(0.33333333333));
+
+#endif
+
+ for (uint i = 0; i < FRAME.collider_count; i++) {
+ vec3 normal;
+ float depth;
+ bool col = false;
+
+ vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz;
+ vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform);
+
+ switch (FRAME.colliders[i].type) {
+ case COLLIDER_TYPE_SPHERE: {
+ float d = length(rel_vec) - (particle_size + FRAME.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, FRAME.colliders[i].extents))) {
+ //point outside box
+
+ vec3 closest = min(abs_pos, FRAME.colliders[i].extents);
+ vec3 rel = abs_pos - closest;
+ depth = length(rel) - particle_size;
+ if (depth < 0.0) {
+ col = true;
+ normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos);
+ depth = -depth;
+ }
+ } else {
+ //point inside box
+ vec3 axis_len = FRAME.colliders[i].extents - 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(FRAME.colliders[i].transform) * (normal * sgn_pos);
+ }
+
+ } break;
+ case COLLIDER_TYPE_SDF: {
+ vec3 apos = abs(local_pos);
+ float extra_dist = 0.0;
+ if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside
+ vec3 mpos = min(apos, FRAME.colliders[i].extents);
+ extra_dist = distance(mpos, apos);
+ }
+
+ if (extra_dist > particle_size) {
+ continue;
+ }
+
+ vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5;
+ float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r;
+ s *= FRAME.colliders[i].scale;
+ s += extra_dist;
+ if (s < particle_size) {
+ col = true;
+ depth = particle_size - s;
+ const float EPSILON = 0.001;
+ normal = mat3(FRAME.colliders[i].transform) *
+ normalize(
+ vec3(
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r));
+ }
+
+ } 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), FRAME.colliders[i].extents))) {
+ continue;
+ }
+
+ const float DELTA = 1.0 / 8192.0;
+
+ vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
+
+ float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), 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) * FRAME.colliders[i].extents;
+ vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+
+ normal = normalize(cross(pos1 - pos2, pos1 - pos3));
+ float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 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 (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
@@ -329,66 +546,4 @@ COMPUTE_SHADER_CODE
/* clang-format on */
}
-
-#if !defined(DISABLE_VELOCITY)
-
- if (PARTICLE.is_active) {
- PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
- }
-#endif
-
-#if 0
- if (PARTICLE.is_active) {
- //execute shader
-
-
-
-
- //!defined(DISABLE_FORCE)
-
- if (false) {
- vec3 force = vec3(0.0);
- for (int i = 0; i < attractor_count; i++) {
- vec3 rel_vec = xform[3].xyz - attractors[i].pos;
- float dist = length(rel_vec);
- if (attractors[i].radius < dist)
- continue;
- if (attractors[i].eat_radius > 0.0 && attractors[i].eat_radius > dist) {
- out_velocity_active.a = 0.0;
- }
-
- rel_vec = normalize(rel_vec);
-
- float attenuation = pow(dist / attractors[i].radius, attractors[i].attenuation);
-
- if (attractors[i].dir == vec3(0.0)) {
- //towards center
- force += attractors[i].strength * rel_vec * attenuation * mass;
- } else {
- force += attractors[i].strength * attractors[i].dir * attenuation * mass;
- }
- }
-
- out_velocity_active.xyz += force * local_delta;
- }
-
-#if !defined(DISABLE_VELOCITY)
-
- if (true) {
- xform[3].xyz += out_velocity_active.xyz * local_delta;
- }
-#endif
- } else {
- xform = mat4(0.0);
- }
-
-
- xform = transpose(xform);
-
- out_velocity_active.a = mix(0.0, 1.0, shader_active);
-
- out_xform_1 = xform[0];
- out_xform_2 = xform[1];
- out_xform_3 = xform[2];
-#endif
}