Unreal 5.3 - Inter particle collision and neighbor grids explained (FULL TUTORIAL)

Поділитися
Вставка
  • Опубліковано 7 лют 2025
  • In this video I'll show a step by step guide on how to make particles collide with each other in Niagara, using Neighbor grids and simple rigid body dynamics. Enjoy! :)

КОМЕНТАРІ • 56

  • @Aquarica
    @Aquarica 10 місяців тому +1

    That's some smarticle particles you got in your brain
    I suppose that sounds a bit funny, really neat system thanks a bunch for walking us through it

  • @MRX360IBZ
    @MRX360IBZ 7 місяців тому +3

    ¡Muchas gracias Enrique por este pedazo de tutorial! Pego por aquí el script del minuto 42:
    OutPosition=Position;
    NeighbourCount=0;
    Collided=false;
    #if GPU_SIMULATION
    const int3 IndexOffsets[27]=
    {
    int3(-1,-1,-1),
    int3(-1,-1,0),
    int3(-1,-1,1),
    int3(-1,0,-1),
    int3(-1,0,0),
    int3(-1,0,1),
    int3(-1,1,-1),
    int3(-1,1,0),
    int3(-1,1,1),
    int3(0,-1,-1),
    int3(0,-1,0),
    int3(0,-1,1),
    int3(0,0,-1),
    int3(0,0,0),
    int3(0,0,1),
    int3(0,1,-1),
    int3(0,1,0),
    int3(0,1,1),
    int3(1,-1,-1),
    int3(1,-1,0),
    int3(1,-1,1),
    int3(1,0,-1),
    int3(1,0,0),
    int3(1,0,1),
    int3(1,1,-1),
    int3(1,1,0),
    int3(1,1,1),
    };
    float3 FinalOffsetVector={0,0,0};
    //Number of grod Cells
    int3 NumCells;
    Grid.GetNumCells(NumCells.x, NumCells.y, NumCells.z);
    //Max neighbours per Cell
    int MaxNeighborsPerCell;
    Grid.MaxNeighborsPerCell(MaxNeighborsPerCell);
    //Position in Unit Space
    float3 UnitPos;
    Grid.SimulationToUnit(Position,SimToUnit,UnitPos);
    //Position in XYZ grid Index
    int3 Index;
    Grid.UnitToIndex(UnitPos,Index.x,Index.y,Index.z);
    //Iterate through Cells
    for (int xxx=0;xxx= 0 && IndexToUse.x < NumCells.x && IndexToUse.y >= 0 && IndexToUse.y < NumCells.y && IndexToUse.z >= 0 && IndexToUse.z < NumCells.z)
    {
    int LinearIndex;
    Grid.IndexToLinear(IndexToUse.x, IndexToUse.y, IndexToUse.z, LinearIndex);
    int NeighboursInCell;
    Grid.GetParticleNeighborCount(LinearIndex, NeighboursInCell);
    for (int ind = 0; ind < NeighboursInCell; ind++)
    {
    int NeighborLinearIndex;
    Grid.NeighborGridIndexToLinear(IndexToUse.x, IndexToUse.y, IndexToUse.z, ind, NeighborLinearIndex);
    int CurrNeighborldx;
    Grid.GetParticleNeighbor(NeighborLinearIndex, CurrNeighborldx);
    if(CurrNeighborldx==ExecIndex || CurrNeighborldx==-1) continue;
    bool readResult;
    float3 OtherPosition;
    ParticleReader.GetPositionByIndex(CurrNeighborldx, readResult, OtherPosition);
    if (!readResult) continue;
    float OtherRadius;
    ParticleReader.GetFloatByIndex(CurrNeighborldx, readResult, OtherRadius);
    if (!readResult) continue;
    const float3 PushDirection = Position - OtherPosition;
    const float Dist = length(PushDirection);
    const float3 PushDirectionUnit = PushDirection/Dist;
    float Overlap = (CollisionRadius + OtherRadius) - Dist;
    if (Overlap > 0.5)
    {
    NeighbourCount +=1;
    Collided = true;
    float OtherMass;
    ParticleReader.GetFloatByIndex(CurrNeighborldx, readResult, OtherMass);
    if (!readResult) continue;
    float MassPercentage=Mass/(Mass+OtherMass);
    FinalOffsetVector += (1.0-MassPercentage)*Overlap*PushDirectionUnit;
    }
    }
    }
    }
    if(Collided)
    {
    OutPosition += 1.*FinalOffsetVector/NeighbourCount;
    }
    #endif

    • @crisrocha3819
      @crisrocha3819 7 місяців тому

      Hola, muchas gracias por postear el codigo pero sabras porque a lo mejor no me esta sirviendo?

    • @nguyencsc
      @nguyencsc 5 місяців тому

      Thank you so much. You save my hours :))

    • @nguyencsc
      @nguyencsc 5 місяців тому

      @@crisrocha3819 Maybe all input and output names in Custom Hlsl must be match like video.

  • @colepeterson5392
    @colepeterson5392 8 місяців тому

    Haven't watched yet, but I'm so happy you're covering the neighbor grid. Now I know it will finally make sense!

  • @parse_error
    @parse_error 7 місяців тому

    Thanks a lot Enrique! Another great tutorial. Thank you so much for explaining this in such an amazing and simple manner!

  • @rtea_academy
    @rtea_academy 10 місяців тому

    Te quiero Enrique.. por fin alguien de autoridad que se digna a explicar esto... Desde hace meses quiero hablar contigo.. dime como puedo hacerlo por favor.

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  10 місяців тому

      Entre el trabajo, algunos problemillas de salud, y otros proyectos, la verdad es que tengo muy poco tiempo. Que era de lo que querias hablar?

    • @rtea_academy
      @rtea_academy 9 місяців тому

      @@EnriqueVenturaGames Soy Technical FX tengo 52 años, en el pasado enviroment artist... una escuela desde hace 3 años y valoro muchísimo tu trabajo.. Quería conocerte y presentarte a la comunidad.. Eres uno de los iconos como techcnical artist con mas significado que conozco, desafortunadamente de habla inglesa (o no...). Me encantaría que mi comunidad te conociera por todo este año 2024 en un directo. La salud tampoco me acompaña últimamente y entiendo que un perfil como el tuyo tenga trabajo para aburrir. Lo tengo yo y no te llego ni a la suela de tus zapatos... Por cierto, muy bien explicado pero ha sido abrumador. Me he tenido que tomar la pastilla roja para CASI entenderlo y eso que llevo mas de un año 24/7 con hlsl (prueba que necesitare 10 + XD). Gracias por tu contenido Enrique.

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  9 місяців тому +1

      @@rtea_academy Venga a ver si este mes podemos coordinar algo! Si quieres puedes mandarme un correo a (enriqueventura en gmail) y ya vemos :)

  • @billlee9757
    @billlee9757 10 місяців тому +1

    Thanks for the tutorial, Enrique, always love your tutorial. This is what I'm looking for. If it is possible to upload a higher resolution video?This video only has the option of 360p resolution. Higher resolution will help me a lot. Again, thank you very much.

  • @iPEMiC.
    @iPEMiC. 7 місяців тому

    Infinitly thank you I know why I subscribed to your channel since a while now. Was looking for explanations since ages and finally you explain it and very well. Is this what they call PBD particle base dynamic?

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  7 місяців тому +1

      Thanks! Almost! :) "PBD" stands for position based dynamics. Most physics simulations are force based instead (sometimes called impulse based), or field based.
      PBD trades precision and detail for speed, since it doesn't simulate effects like pressure.

    • @iPEMiC.
      @iPEMiC. 7 місяців тому

      @@EnriqueVenturaGames Understand, thank you for the precision.

  • @NaikouVR
    @NaikouVR 8 місяців тому

    First of all, really good work, the level of explanation is really over the top. I'm currently trying this effect with the Chaldini pattern (another video of yours ^^), but unfortunately (after watching your video several times and a few hours later) I have no success with it. Maybe you have an idea why or a suggestion where you could help me. Thank you very much :) ... i tryd it from scratch, 80% nothing happens and sometimes when i speed up the world slighty movment are there but not quite what it should be

  • @Techne89
    @Techne89 10 місяців тому

    Greta ch just found it. It mind fing why I haven’t seen your ch before when I search for unreal tutorials. It just pop up in my feed randomly . Fix your alrogtrm UA-cam don’t hide good content 😅

  • @thebigflatnow8352
    @thebigflatnow8352 5 місяців тому +1

    // Insert the body of the function here and add any inputs
    // and outputs by name using the add pins above.
    // Currently, complicated branches, for loops, switches, etc are not advised.
    OutPosition = Position;
    NeighbourCount = 0;
    Collided = false;
    #if GPU_SIMULATION
    const int3 IndexOffsets [ 27 ] =
    {
    int3(-1,-1,-1),
    int3(-1,-1, 0),
    int3(-1,-1, 1),
    int3(-1, 0,-1),
    int3(-1, 0, 0),
    int3(-1, 0, 1),
    int3(-1, 1,-1),
    int3(-1, 1, 0),
    int3(-1, 1, 1),

    int3(0,-1,-1),
    int3(0,-1, 0),
    int3(0,-1, 1),
    int3(0, 0,-1),
    int3(0, 0, 0),
    int3(0, 0, 1),
    int3(0, 1,-1),
    int3(0, 1, 0),
    int3(0, 1, 1),

    int3(1,-1,-1),
    int3(1,-1, 0),
    int3(1,-1, 1),
    int3(1, 0,-1),
    int3(1, 0, 0),
    int3(1, 0, 1),
    int3(1, 1,-1),
    int3(1, 1, 0),
    int3(1, 1, 1),
    };
    float3 FinalOffsetVector = {0, 0, 0}; // Corrected initialization
    //Number of grid cells
    int3 NumCells;
    Grid.GetNumCells(NumCells.x, NumCells.y, NumCells.z);
    //Max neighbours per cell
    int MaxNeighborsPerCell;
    Grid.MaxNeighborsPerCell(MaxNeighborsPerCell);
    //Position in Unit space
    float3 UnitPos;
    Grid.SimulationToUnit(Position, SimToUnit, UnitPos);
    //Convert to XYZ grid index (current cell)
    int3 Index;
    Grid.UnitToIndex(UnitPos, Index.x,Index.y,Index.z);
    //Iterate through cells
    for (int xxx = 0; xxx < 27; xxx++)
    {
    //Index of cell to test
    const int3 IndexToUse = Index + IndexOffsets[xxx];
    //Ignore cells outside boundaries
    if (IndexToUse.x >= 0 && IndexToUse.x < NumCells.x &&
    IndexToUse.y >= 0 && IndexToUse.y < NumCells.y &&
    IndexToUse.z >= 0 && IndexToUse.z < NumCells.z)
    {
    //Convert index to linear
    int LinearIndex;
    Grid.IndexToLinear(IndexToUse.x, IndexToUse.y, IndexToUse.z, LinearIndex);
    //Number of particles in cell
    int NeighboursInCell;
    Grid.GetParticleNeighborCount(LinearIndex, NeighboursInCell);
    //Iterate through particles in cell
    for (int ind = 0; ind < NeighboursInCell; ind++)
    {
    //Linear index of current particle
    int NeighborLinearIndex;
    Grid.NeighborGridIndexToLinear(IndexToUse.x, IndexToUse.y, IndexToUse.z, ind, NeighborLinearIndex);
    //Execution index of current particle (ID)
    int CurrNeighborIdx;
    Grid.GetParticleNeighbor(NeighborLinearIndex, CurrNeighborIdx);
    //Ignore self
    if (CurrNeighborIdx == ExecIndex || CurrNeighborIdx==-1) continue;
    //Bool to store read results
    bool readResult;
    //Read Position
    float3 OtherPosition;
    ParticleReader.GetPositionByIndex(CurrNeighborIdx, readResult, OtherPosition);
    if (!readResult) continue;
    //Read Radius
    float OtherRadius;
    ParticleReader.GetFloatByIndex(CurrNeighborIdx, readResult, OtherRadius);
    //Calculate distance and push direction
    const float3 PushDirection = Position - OtherPosition;
    const float Dist = length(PushDirection);
    const float3 PushDirectionUnit = PushDirection/Dist;
    float Overlap = (In_CollisionRadius + OtherRadius) - Dist; // Use In_CollisionRadius
    if (Overlap > 0.f)
    {
    NeighbourCount ++;
    Collided = true;
    //Read Mass
    float OtherMass;
    ParticleReader.GetFloatByIndex(CurrNeighborIdx, readResult, OtherMass);
    if (!readResult) continue;
    float MassPercentage = Mass / (Mass + OtherMass);
    FinalOffsetVector += (1.0 - MassPercentage) * Overlap * PushDirectionUnit;
    }
    }
    }
    }
    if(Collided)
    {
    OutPosition += 1.f*FinalOffsetVector / NeighbourCount;
    }
    #endif

  • @xinpengliu
    @xinpengliu 6 місяців тому

    Awesome!👍

  • @GGKornis
    @GGKornis 8 місяців тому +1

    Thanks a lot for the amazing tutorial.
    Could you please tell us the params or show them to achieve the world around collisions, so that particles move as on the preview? can be placed somewhere as a bunch of balls in the box. How to set up this right way? cause currently my particles just disappear when I add Collision

    • @GGKornis
      @GGKornis 8 місяців тому

      help, please, that's crucial.

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  6 місяців тому

      Sorry for the late reply! If your particles are colliding between them, and the part that doesn't work is the collision against other geometry, the problem could be on the type of collision used (I recommend GPU distance fields); if that still doesn't work, you could have an issue with the distance field calculation itself; I'd recommend to check the project settings if that's the case

  • @enricobersani8948
    @enricobersani8948 4 місяці тому

    Great video, thank you. I have a question: how would you handle the precision of the collision for more complex meshes?

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  18 днів тому

      Since the collision is a bit of a hack using neighbor grids, I don't think it would be viable to implement collision using convex objects (you can check how complex the topic is, there's plenty of available documentation on the topic).

  • @thomaswesen7181
    @thomaswesen7181 3 місяці тому

    Could you perhaps make this available for download?

  • @neiko-bc6mh
    @neiko-bc6mh 9 місяців тому +1

    It's an amazing work!!
    I really want to get through it and checked my code for three times but still can't get the collision. Would you post the code at somwhere? That will really help!

    • @neiko-bc6mh
      @neiko-bc6mh 9 місяців тому +4

      omg, I know what's wrong. I have to untick the "neighbor grid3d'---'clear before non iteration stage' , and tick it again.

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  9 місяців тому +1

      Oh yeah that is a weird bug, it happens every now and then and it can be very annoying. Glad to hear you figured it out :)

    • @GGKornis
      @GGKornis 8 місяців тому

      I've checked everything three times before I got to your comment, thanks a lot for help.

    • @fredhooper6779
      @fredhooper6779 6 місяців тому

      @@neiko-bc6mh Where is the clear before non iteration stage located? I'm having some issues with this as well

    • @wvfx9078
      @wvfx9078 3 місяці тому

      Anyone figured this out yet? This is getting really frustrating....
      Followed this tutorial to a T to learn but nothing happened. I don't even know where to troubleshoot the issue...... Jesus can someone just point out where is this untick neighbor grid option at all instead of just giving such a vague description????

  • @jordanhey4818
    @jordanhey4818 9 місяців тому +2

    Hey would you be able to post the code some where? I've gone through it a bunch of times and still no luck :(

  • @mmariansky
    @mmariansky 6 місяців тому

    After populating the grid around 30:47, all my particles are red and not green.. Any idea wha could be wrong?

    • @mmariansky
      @mmariansky 6 місяців тому

      I changed the translation matrix (in init emitter) to -0.5,-0.5,-0.5 and now it works, they are all green...

    • @dj_helix
      @dj_helix 4 місяці тому

      @@mmariansky Stuck here myself - any better explanation of where exactly to find it? Been looking through from top to bottom of this and haven't found the spot.

  • @Mavuse47
    @Mavuse47 8 місяців тому +1

    ❤😂🎉 2:00

  • @lordmaddog6003
    @lordmaddog6003 10 місяців тому

    This is awesome! Thank you soo much!
    Is it possible for you to upload the finish system for us to reference?

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  10 місяців тому

      Thanks! I can look into it later, but cannot promise anything (cannot share the entire project file, and copying .uasset filles can produce errors if there are other file dependencies). I'll update the video description if I upload the asset somewhere.

    • @lordmaddog6003
      @lordmaddog6003 10 місяців тому

      @@EnriqueVenturaGames Thanks

  • @user-JangSaengNong
    @user-JangSaengNong 7 місяців тому

    thank you so much

  • @HavokBWR
    @HavokBWR 8 місяців тому

    Hey, do you know if it's possible to track the history of individual particles? Like if you had a container of red particles and then added some yellow particles, would you be able to make the ones that collide with the yellow turn orange?

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  7 місяців тому

      The basic setup for that is not that complicated: when Niagara is getting the neighbor particles, you can sample their color and then calculate a new average. Things start to get tricky once you add paint weights and other factors, but in a nutshell, yeah, that is perfectly doable.

  • @MrBrickulous
    @MrBrickulous 10 місяців тому

    Hello Enrique! Any plans to do job/portfolio advice for aspiring tech artists? That information seems to be scarce.

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  10 місяців тому +1

      I wasn't but you know what? It is not a bad idea! I'll add it to a list of potential content for future videos. Thanks for the suggestion!

  • @ShivamKaushik_Scribe
    @ShivamKaushik_Scribe 8 місяців тому

    Could this be done with sprites instead of meshes?

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  7 місяців тому

      Yeah absolutely! You just need to adjust the calculation for the particle radius, since it won't be based on the mesh scale, but other than that, there's no other changes needed

  • @jamesemory8781
    @jamesemory8781 10 місяців тому

    Can you do this with multiple meshes instead of a single mesh?

    • @EnriqueVenturaGames
      @EnriqueVenturaGames  10 місяців тому

      Yes, you can use multiple meshes in the Mesh Renderer module, and select them sequentially, randomly, or any other way via scripts

  • @lapotk.3150
    @lapotk.3150 9 місяців тому

    nice😍

  • @InternetPinapple
    @InternetPinapple 9 місяців тому +1

    puh lease make a tut on messy outlines like tmnt mutant mayhem

  • @ragataga_
    @ragataga_ 10 місяців тому

    :D