GpuParticlesSimulation.ankiprog 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // Copyright (C) 2009-2020, 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 <shaders/glsl_cpp_common/GpuParticles.h>
  8. #include <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 * normalVSpace;
  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 = u_state.m_emitterRotation * mix(u_props.m_minForce, u_props.m_maxForce, randFactor);
  72. const Vec3 totalForce = (p.m_acceleration * p.m_mass) + initialForce;
  73. const Vec3 acceleration = totalForce / p.m_mass;
  74. p.m_velocity = acceleration * u_state.m_dt;
  75. }
  76. void main()
  77. {
  78. const U32 particleIdx = gl_GlobalInvocationID.x;
  79. if(particleIdx >= u_props.m_particleCount)
  80. {
  81. return;
  82. }
  83. GpuParticle particle = u_particles[particleIdx];
  84. const F32 dt = u_state.m_dt;
  85. // Check if it's dead
  86. if(particle.m_life - dt <= 0.0)
  87. {
  88. // Dead, revive
  89. initParticle(particle);
  90. }
  91. else
  92. {
  93. // Simulate
  94. particle.m_life -= dt;
  95. const Vec3 xp = particle.m_oldWorldPosition;
  96. const Vec3 xc = particle.m_acceleration * (dt * dt) + u_particles[particleIdx].m_velocity * dt + xp;
  97. // Project the point
  98. const Vec4 proj4 = u_state.m_viewProjMat * Vec4(xc, 1.0);
  99. const Vec3 proj3 = proj4.xyz / proj4.w;
  100. if(all(greaterThan(proj3.xy, Vec2(-1.0))) && all(lessThan(proj3.xy, Vec2(1.0))))
  101. {
  102. // It's visible, test against the depth buffer
  103. const F32 refDepth = textureLod(u_depthRt, u_nearestAnyClampSampler, NDC_TO_UV(proj3.xy), 0.0).r;
  104. const F32 testDepth = proj3.z;
  105. if(testDepth >= refDepth)
  106. {
  107. // Collides, change its direction
  108. const Vec3 normal = computeNormal(NDC_TO_UV(proj3.xy), refDepth);
  109. particle.m_velocity = reflect(particle.m_velocity, normal);
  110. particle.m_oldWorldPosition = particle.m_newWorldPosition;
  111. }
  112. else
  113. {
  114. particle.m_oldWorldPosition = particle.m_newWorldPosition;
  115. particle.m_newWorldPosition = xc;
  116. }
  117. }
  118. else
  119. {
  120. particle.m_oldWorldPosition = particle.m_newWorldPosition;
  121. particle.m_newWorldPosition = xc;
  122. }
  123. }
  124. // Write back the particle
  125. u_particles[particleIdx] = particle;
  126. }
  127. #pragma anki end