GpuParticlesSimulation.ankiprog 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. // This shader does a particle simulation
  6. #pragma anki start comp
  7. #include <AnKi/Shaders/Include/ParticleTypes.h>
  8. #include <AnKi/Shaders/Common.glsl>
  9. layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
  10. layout(set = 0, binding = 0) uniform texture2D u_depthRt;
  11. layout(set = 1, binding = 0) buffer ssbo_
  12. {
  13. GpuParticle u_particles[];
  14. };
  15. layout(set = 1, binding = 1, std140) uniform ubo_
  16. {
  17. GpuParticleEmitterProperties u_props;
  18. };
  19. layout(set = 1, binding = 2) readonly buffer ubo1_
  20. {
  21. U32 u_randomFactorCount;
  22. F32 u_randomFactors[];
  23. };
  24. layout(set = 1, binding = 3) uniform sampler u_nearestAnyClampSampler;
  25. layout(set = 1, binding = 4, std140, row_major) uniform ubo1_
  26. {
  27. GpuParticleSimulationState u_state;
  28. };
  29. F32 smallerDelta(F32 left, F32 mid, F32 right)
  30. {
  31. const F32 a = mid - left;
  32. const F32 b = right - mid;
  33. return (abs(a) < abs(b)) ? a : b;
  34. }
  35. Vec3 unproject(Vec2 ndc, F32 depth)
  36. {
  37. const F32 z = u_state.m_unprojectionParams.z / (u_state.m_unprojectionParams.w + depth);
  38. const Vec2 xy = ndc * u_state.m_unprojectionParams.xy * z;
  39. return Vec3(xy, z);
  40. }
  41. // Compute the normal using the depth buffer
  42. Vec3 computeNormal(const Vec2 uv, const F32 depth)
  43. {
  44. const F32 depthLeft = textureLodOffset(sampler2D(u_depthRt, u_nearestAnyClampSampler), uv, 0.0, ivec2(-2, 0)).r;
  45. const F32 depthRight = textureLodOffset(sampler2D(u_depthRt, u_nearestAnyClampSampler), uv, 0.0, ivec2(2, 0)).r;
  46. const F32 depthTop = textureLodOffset(sampler2D(u_depthRt, u_nearestAnyClampSampler), uv, 0.0, ivec2(0, 2)).r;
  47. const F32 depthBottom = textureLodOffset(sampler2D(u_depthRt, u_nearestAnyClampSampler), uv, 0.0, ivec2(0, -2)).r;
  48. const F32 ddx = smallerDelta(depthLeft, depth, depthRight);
  49. const F32 ddy = smallerDelta(depthBottom, depth, depthTop);
  50. const Vec2 ndc = UV_TO_NDC(uv);
  51. const Vec2 TEXEL_SIZE = 1.0 / Vec2(textureSize(u_depthRt, 0));
  52. const Vec2 NDC_TEXEL_SIZE = 2.0 * TEXEL_SIZE;
  53. const Vec3 right = unproject(ndc + Vec2(NDC_TEXEL_SIZE.x, 0.0), depth + ddx);
  54. const Vec3 top = unproject(ndc + Vec2(0.0, NDC_TEXEL_SIZE.y), depth + ddy);
  55. const Vec3 origin = unproject(ndc, depth);
  56. Vec3 normalVSpace = cross(origin - top, right - origin);
  57. normalVSpace = normalize(normalVSpace);
  58. return u_state.m_invViewRotation * Vec4(normalVSpace, 0.0);
  59. }
  60. void initParticle(out GpuParticle p)
  61. {
  62. const F32 randFactor = u_randomFactors[(gl_GlobalInvocationID.x + u_state.m_randomIndex) % u_randomFactorCount];
  63. p.m_newWorldPosition =
  64. mix(u_props.m_minStartingPosition, u_props.m_maxStartingPosition, randFactor) + u_state.m_emitterPosition;
  65. p.m_oldWorldPosition = p.m_newWorldPosition;
  66. p.m_mass = mix(u_props.m_minMass, u_props.m_maxMass, randFactor);
  67. p.m_startingLife = mix(u_props.m_minLife, u_props.m_maxLife, randFactor);
  68. p.m_life = p.m_startingLife;
  69. p.m_acceleration = mix(u_props.m_minGravity, u_props.m_maxGravity, randFactor);
  70. // Calculate the initial velocity
  71. const Vec3 initialForce =
  72. u_state.m_emitterRotation * Vec4(mix(u_props.m_minForce, u_props.m_maxForce, randFactor), 0.0);
  73. const Vec3 totalForce = (p.m_acceleration * p.m_mass) + initialForce;
  74. const Vec3 acceleration = totalForce / p.m_mass;
  75. p.m_velocity = acceleration * u_state.m_dt;
  76. }
  77. void main()
  78. {
  79. const U32 particleIdx = gl_GlobalInvocationID.x;
  80. if(particleIdx >= u_props.m_particleCount)
  81. {
  82. return;
  83. }
  84. GpuParticle particle = u_particles[particleIdx];
  85. const F32 dt = u_state.m_dt;
  86. // Check if it's dead
  87. if(particle.m_life - dt <= 0.0)
  88. {
  89. // Dead, revive
  90. initParticle(particle);
  91. }
  92. else
  93. {
  94. // Simulate
  95. particle.m_life -= dt;
  96. const Vec3 xp = particle.m_oldWorldPosition;
  97. const Vec3 xc = particle.m_acceleration * (dt * dt) + u_particles[particleIdx].m_velocity * dt + xp;
  98. // Project the point
  99. const Vec4 proj4 = u_state.m_viewProjMat * Vec4(xc, 1.0);
  100. const Vec3 proj3 = proj4.xyz / proj4.w;
  101. if(all(greaterThan(proj3.xy, Vec2(-1.0))) && all(lessThan(proj3.xy, Vec2(1.0))))
  102. {
  103. // It's visible, test against the depth buffer
  104. const F32 refDepth = textureLod(u_depthRt, u_nearestAnyClampSampler, NDC_TO_UV(proj3.xy), 0.0).r;
  105. const F32 testDepth = proj3.z;
  106. if(testDepth >= refDepth)
  107. {
  108. // Collides, change its direction
  109. const Vec3 normal = computeNormal(NDC_TO_UV(proj3.xy), refDepth);
  110. particle.m_velocity = reflect(particle.m_velocity, normal);
  111. particle.m_oldWorldPosition = particle.m_newWorldPosition;
  112. }
  113. else
  114. {
  115. particle.m_oldWorldPosition = particle.m_newWorldPosition;
  116. particle.m_newWorldPosition = xc;
  117. }
  118. }
  119. else
  120. {
  121. particle.m_oldWorldPosition = particle.m_newWorldPosition;
  122. particle.m_newWorldPosition = xc;
  123. }
  124. }
  125. // Write back the particle
  126. u_particles[particleIdx] = particle;
  127. }
  128. #pragma anki end