particles.glsl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. #[compute]
  2. #version 450
  3. VERSION_DEFINES
  4. layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
  5. #define SAMPLER_NEAREST_CLAMP 0
  6. #define SAMPLER_LINEAR_CLAMP 1
  7. #define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
  8. #define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
  9. #define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
  10. #define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
  11. #define SAMPLER_NEAREST_REPEAT 6
  12. #define SAMPLER_LINEAR_REPEAT 7
  13. #define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
  14. #define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
  15. #define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
  16. #define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
  17. /* SET 0: GLOBAL DATA */
  18. layout(set = 0, binding = 1) uniform sampler material_samplers[12];
  19. layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalVariableData {
  20. vec4 data[];
  21. }
  22. global_variables;
  23. /* Set 1: FRAME AND PARTICLE DATA */
  24. // a frame history is kept for trail deterministic behavior
  25. #define MAX_ATTRACTORS 32
  26. #define ATTRACTOR_TYPE_SPHERE 0
  27. #define ATTRACTOR_TYPE_BOX 1
  28. #define ATTRACTOR_TYPE_VECTOR_FIELD 2
  29. struct Attractor {
  30. mat4 transform;
  31. vec3 extents; //exents or radius
  32. uint type;
  33. uint texture_index; //texture index for vector field
  34. float strength;
  35. float attenuation;
  36. float directionality;
  37. };
  38. #define MAX_COLLIDERS 32
  39. #define COLLIDER_TYPE_SPHERE 0
  40. #define COLLIDER_TYPE_BOX 1
  41. #define COLLIDER_TYPE_SDF 2
  42. #define COLLIDER_TYPE_HEIGHT_FIELD 3
  43. struct Collider {
  44. mat4 transform;
  45. vec3 extents; //exents or radius
  46. uint type;
  47. uint texture_index; //texture index for vector field
  48. float scale;
  49. uint pad[2];
  50. };
  51. struct FrameParams {
  52. bool emitting;
  53. float system_phase;
  54. float prev_system_phase;
  55. uint cycle;
  56. float explosiveness;
  57. float randomness;
  58. float time;
  59. float delta;
  60. uint random_seed;
  61. uint attractor_count;
  62. uint collider_count;
  63. float particle_size;
  64. mat4 emission_transform;
  65. Attractor attractors[MAX_ATTRACTORS];
  66. Collider colliders[MAX_COLLIDERS];
  67. };
  68. layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
  69. FrameParams data[];
  70. }
  71. frame_history;
  72. struct ParticleData {
  73. mat4 xform;
  74. vec3 velocity;
  75. bool is_active;
  76. vec4 color;
  77. vec4 custom;
  78. };
  79. layout(set = 1, binding = 1, std430) restrict buffer Particles {
  80. ParticleData data[];
  81. }
  82. particles;
  83. #define EMISSION_FLAG_HAS_POSITION 1
  84. #define EMISSION_FLAG_HAS_ROTATION_SCALE 2
  85. #define EMISSION_FLAG_HAS_VELOCITY 4
  86. #define EMISSION_FLAG_HAS_COLOR 8
  87. #define EMISSION_FLAG_HAS_CUSTOM 16
  88. struct ParticleEmission {
  89. mat4 xform;
  90. vec3 velocity;
  91. uint flags;
  92. vec4 color;
  93. vec4 custom;
  94. };
  95. layout(set = 1, binding = 2, std430) restrict buffer SourceEmission {
  96. int particle_count;
  97. uint pad0;
  98. uint pad1;
  99. uint pad2;
  100. ParticleEmission data[];
  101. }
  102. src_particles;
  103. layout(set = 1, binding = 3, std430) restrict buffer DestEmission {
  104. int particle_count;
  105. int particle_max;
  106. uint pad1;
  107. uint pad2;
  108. ParticleEmission data[];
  109. }
  110. dst_particles;
  111. /* SET 2: COLLIDER/ATTRACTOR TEXTURES */
  112. #define MAX_3D_TEXTURES 7
  113. layout(set = 2, binding = 0) uniform texture3D sdf_vec_textures[MAX_3D_TEXTURES];
  114. layout(set = 2, binding = 1) uniform texture2D height_field_texture;
  115. /* SET 3: MATERIAL */
  116. #ifdef USE_MATERIAL_UNIFORMS
  117. layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
  118. /* clang-format off */
  119. MATERIAL_UNIFORMS
  120. /* clang-format on */
  121. } material;
  122. #endif
  123. layout(push_constant, binding = 0, std430) uniform Params {
  124. float lifetime;
  125. bool clear;
  126. uint total_particles;
  127. uint trail_size;
  128. bool use_fractional_delta;
  129. bool sub_emitter_mode;
  130. bool can_emit;
  131. uint pad;
  132. }
  133. params;
  134. uint hash(uint x) {
  135. x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
  136. x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
  137. x = (x >> uint(16)) ^ x;
  138. return x;
  139. }
  140. bool emit_subparticle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
  141. if (!params.can_emit) {
  142. return false;
  143. }
  144. bool valid = false;
  145. int dst_index = atomicAdd(dst_particles.particle_count, 1);
  146. if (dst_index >= dst_particles.particle_max) {
  147. atomicAdd(dst_particles.particle_count, -1);
  148. return false;
  149. }
  150. dst_particles.data[dst_index].xform = p_xform;
  151. dst_particles.data[dst_index].velocity = p_velocity;
  152. dst_particles.data[dst_index].color = p_color;
  153. dst_particles.data[dst_index].custom = p_custom;
  154. dst_particles.data[dst_index].flags = p_flags;
  155. return true;
  156. }
  157. /* clang-format off */
  158. COMPUTE_SHADER_GLOBALS
  159. /* clang-format on */
  160. void main() {
  161. uint particle = gl_GlobalInvocationID.x;
  162. if (particle >= params.total_particles * params.trail_size) {
  163. return; //discard
  164. }
  165. uint index = particle / params.trail_size;
  166. uint frame = (particle % params.trail_size);
  167. #define FRAME frame_history.data[frame]
  168. #define PARTICLE particles.data[particle]
  169. bool apply_forces = true;
  170. bool apply_velocity = true;
  171. float local_delta = FRAME.delta;
  172. float mass = 1.0;
  173. bool restart = false;
  174. bool restart_position = false;
  175. bool restart_rotation_scale = false;
  176. bool restart_velocity = false;
  177. bool restart_color = false;
  178. bool restart_custom = false;
  179. if (params.clear) {
  180. PARTICLE.color = vec4(1.0);
  181. PARTICLE.custom = vec4(0.0);
  182. PARTICLE.velocity = vec3(0.0);
  183. PARTICLE.is_active = false;
  184. PARTICLE.xform = mat4(
  185. vec4(1.0, 0.0, 0.0, 0.0),
  186. vec4(0.0, 1.0, 0.0, 0.0),
  187. vec4(0.0, 0.0, 1.0, 0.0),
  188. vec4(0.0, 0.0, 0.0, 1.0));
  189. }
  190. bool collided = false;
  191. vec3 collision_normal = vec3(0.0);
  192. float collision_depth = 0.0;
  193. vec3 attractor_force = vec3(0.0);
  194. #if !defined(DISABLE_VELOCITY)
  195. if (PARTICLE.is_active) {
  196. PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
  197. }
  198. #endif
  199. /* Process physics if active */
  200. if (PARTICLE.is_active) {
  201. for (uint i = 0; i < FRAME.attractor_count; i++) {
  202. vec3 dir;
  203. float amount;
  204. vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.attractors[i].transform[3].xyz;
  205. vec3 local_pos = rel_vec * mat3(FRAME.attractors[i].transform);
  206. switch (FRAME.attractors[i].type) {
  207. case ATTRACTOR_TYPE_SPHERE: {
  208. dir = normalize(rel_vec);
  209. float d = length(local_pos) / FRAME.attractors[i].extents.x;
  210. if (d > 1.0) {
  211. continue;
  212. }
  213. amount = max(0.0, 1.0 - d);
  214. } break;
  215. case ATTRACTOR_TYPE_BOX: {
  216. dir = normalize(rel_vec);
  217. vec3 abs_pos = abs(local_pos / FRAME.attractors[i].extents);
  218. float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
  219. if (d > 1.0) {
  220. continue;
  221. }
  222. amount = max(0.0, 1.0 - d);
  223. } break;
  224. case ATTRACTOR_TYPE_VECTOR_FIELD: {
  225. vec3 uvw_pos = (local_pos / FRAME.attractors[i].extents) * 2.0 - 1.0;
  226. if (any(lessThan(uvw_pos, vec3(0.0))) || any(greaterThan(uvw_pos, vec3(1.0)))) {
  227. continue;
  228. }
  229. vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).xyz;
  230. dir = mat3(FRAME.attractors[i].transform) * normalize(s); //revert direction
  231. amount = length(s);
  232. } break;
  233. }
  234. amount = pow(amount, FRAME.attractors[i].attenuation);
  235. dir = normalize(mix(dir, FRAME.attractors[i].transform[2].xyz, FRAME.attractors[i].directionality));
  236. attractor_force -= amount * dir * FRAME.attractors[i].strength;
  237. }
  238. float particle_size = FRAME.particle_size;
  239. #ifdef USE_COLLISON_SCALE
  240. particle_size *= dot(vec3(length(PARTICLE.xform[0].xyz), length(PARTICLE.xform[1].xyz), length(PARTICLE.xform[2].xyz)), vec3(0.33333333333));
  241. #endif
  242. for (uint i = 0; i < FRAME.collider_count; i++) {
  243. vec3 normal;
  244. float depth;
  245. bool col = false;
  246. vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz;
  247. vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform);
  248. switch (FRAME.colliders[i].type) {
  249. case COLLIDER_TYPE_SPHERE: {
  250. float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x);
  251. if (d < 0.0) {
  252. col = true;
  253. depth = -d;
  254. normal = normalize(rel_vec);
  255. }
  256. } break;
  257. case COLLIDER_TYPE_BOX: {
  258. vec3 abs_pos = abs(local_pos);
  259. vec3 sgn_pos = sign(local_pos);
  260. if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) {
  261. //point outside box
  262. vec3 closest = min(abs_pos, FRAME.colliders[i].extents);
  263. vec3 rel = abs_pos - closest;
  264. depth = length(rel) - particle_size;
  265. if (depth < 0.0) {
  266. col = true;
  267. normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos);
  268. depth = -depth;
  269. }
  270. } else {
  271. //point inside box
  272. vec3 axis_len = FRAME.colliders[i].extents - abs_pos;
  273. // there has to be a faster way to do this?
  274. if (all(lessThan(axis_len.xx, axis_len.yz))) {
  275. normal = vec3(1, 0, 0);
  276. } else if (all(lessThan(axis_len.yy, axis_len.xz))) {
  277. normal = vec3(0, 1, 0);
  278. } else {
  279. normal = vec3(0, 0, 1);
  280. }
  281. col = true;
  282. depth = dot(normal * axis_len, vec3(1)) + particle_size;
  283. normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos);
  284. }
  285. } break;
  286. case COLLIDER_TYPE_SDF: {
  287. vec3 apos = abs(local_pos);
  288. float extra_dist = 0.0;
  289. if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside
  290. vec3 mpos = min(apos, FRAME.colliders[i].extents);
  291. extra_dist = distance(mpos, apos);
  292. }
  293. if (extra_dist > particle_size) {
  294. continue;
  295. }
  296. vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5;
  297. float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r;
  298. s *= FRAME.colliders[i].scale;
  299. s += extra_dist;
  300. if (s < particle_size) {
  301. col = true;
  302. depth = particle_size - s;
  303. const float EPSILON = 0.001;
  304. normal = mat3(FRAME.colliders[i].transform) *
  305. normalize(
  306. vec3(
  307. 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,
  308. 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,
  309. 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));
  310. }
  311. } break;
  312. case COLLIDER_TYPE_HEIGHT_FIELD: {
  313. vec3 local_pos_bottom = local_pos;
  314. local_pos_bottom.y -= particle_size;
  315. if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) {
  316. continue;
  317. }
  318. const float DELTA = 1.0 / 8192.0;
  319. vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
  320. float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r;
  321. if (y > uvw_pos.y) {
  322. //inside heightfield
  323. vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
  324. 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;
  325. 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;
  326. normal = normalize(cross(pos1 - pos2, pos1 - pos3));
  327. float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
  328. col = true;
  329. depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
  330. }
  331. } break;
  332. }
  333. if (col) {
  334. if (!collided) {
  335. collided = true;
  336. collision_normal = normal;
  337. collision_depth = depth;
  338. } else {
  339. vec3 c = collision_normal * collision_depth;
  340. c += normal * max(0.0, depth - dot(normal, c));
  341. collision_normal = normalize(c);
  342. collision_depth = length(c);
  343. }
  344. }
  345. }
  346. }
  347. if (params.sub_emitter_mode) {
  348. if (!PARTICLE.is_active) {
  349. int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
  350. if (src_index >= 0) {
  351. PARTICLE.is_active = true;
  352. restart = true;
  353. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
  354. PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
  355. } else {
  356. PARTICLE.xform[3] = vec4(0, 0, 0, 1);
  357. restart_position = true;
  358. }
  359. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
  360. PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
  361. PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
  362. PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
  363. } else {
  364. PARTICLE.xform[0] = vec4(1, 0, 0, 0);
  365. PARTICLE.xform[1] = vec4(0, 1, 0, 0);
  366. PARTICLE.xform[2] = vec4(0, 0, 1, 0);
  367. restart_rotation_scale = true;
  368. }
  369. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
  370. PARTICLE.velocity = src_particles.data[src_index].velocity;
  371. } else {
  372. PARTICLE.velocity = vec3(0);
  373. restart_velocity = true;
  374. }
  375. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
  376. PARTICLE.color = src_particles.data[src_index].color;
  377. } else {
  378. PARTICLE.color = vec4(1);
  379. restart_color = true;
  380. }
  381. if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
  382. PARTICLE.custom = src_particles.data[src_index].custom;
  383. } else {
  384. PARTICLE.custom = vec4(0);
  385. restart_custom = true;
  386. }
  387. }
  388. }
  389. } else if (FRAME.emitting) {
  390. float restart_phase = float(index) / float(params.total_particles);
  391. if (FRAME.randomness > 0.0) {
  392. uint seed = FRAME.cycle;
  393. if (restart_phase >= FRAME.system_phase) {
  394. seed -= uint(1);
  395. }
  396. seed *= uint(params.total_particles);
  397. seed += uint(index);
  398. float random = float(hash(seed) % uint(65536)) / 65536.0;
  399. restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
  400. }
  401. restart_phase *= (1.0 - FRAME.explosiveness);
  402. if (FRAME.system_phase > FRAME.prev_system_phase) {
  403. // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
  404. if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
  405. restart = true;
  406. if (params.use_fractional_delta) {
  407. local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
  408. }
  409. }
  410. } else if (FRAME.delta > 0.0) {
  411. if (restart_phase >= FRAME.prev_system_phase) {
  412. restart = true;
  413. if (params.use_fractional_delta) {
  414. local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
  415. }
  416. } else if (restart_phase < FRAME.system_phase) {
  417. restart = true;
  418. if (params.use_fractional_delta) {
  419. local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
  420. }
  421. }
  422. }
  423. uint current_cycle = FRAME.cycle;
  424. if (FRAME.system_phase < restart_phase) {
  425. current_cycle -= uint(1);
  426. }
  427. uint particle_number = current_cycle * uint(params.total_particles) + particle;
  428. if (restart) {
  429. PARTICLE.is_active = FRAME.emitting;
  430. restart_position = true;
  431. restart_rotation_scale = true;
  432. restart_velocity = true;
  433. restart_color = true;
  434. restart_custom = true;
  435. }
  436. }
  437. if (PARTICLE.is_active) {
  438. /* clang-format off */
  439. COMPUTE_SHADER_CODE
  440. /* clang-format on */
  441. }
  442. }