Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung
Nächste Überarbeitung
Vorherige Überarbeitung
talit:fluid_simulation [2025-11-16 15:37] scatalit:fluid_simulation [2025-12-15 14:45] (aktuell) – [2. Smoothed Particle Hydrodynamics (SPH)] sca
Zeile 450: Zeile 450:
  
 myDict.ContainsKey(someKey); // checks if myDict contains a certain key myDict.ContainsKey(someKey); // checks if myDict contains a certain key
 +</code>
 +
 +== Hash ==
 +
 +<code csharp>
 +int CalculateCellHash(int xCell, int yCell)
 +{
 +    return xCell * 73856093 ^ yCell * 19349663;
 +}
 </code> </code>
  
Zeile 460: Zeile 469:
 using UnityEngine; using UnityEngine;
 using System.Collections.Generic; // required for dictionary using System.Collections.Generic; // required for dictionary
 + 
 public enum CollisionDetectionType{ public enum CollisionDetectionType{
     BruteForce,     BruteForce,
     SpatialHashingDict     SpatialHashingDict
 } }
 + 
 public class MainScript : MonoBehaviour public class MainScript : MonoBehaviour
 { {
Zeile 471: Zeile 480:
     public GameObject prefabParticle; // drag particle prefab on this field in Inspector     public GameObject prefabParticle; // drag particle prefab on this field in Inspector
     // and all other game settings     // and all other game settings
 + 
     // PRIVARTE FIELDS     // PRIVARTE FIELDS
     // screen boarders     // screen boarders
Zeile 478: Zeile 487:
     private float yMin = -5;     private float yMin = -5;
     private float yMax = 5;     private float yMax = 5;
 + 
     // particles     // particles
     private bool giveParticlesInitVel = true;     private bool giveParticlesInitVel = true;
     private float vCompMax = 2.5f;     private float vCompMax = 2.5f;
-    private float particleDiameter = 0.05f; // 0.05f +    private float particleDiameter = 2f; // 0.05f 
-    private float particleInitSep = 0.01f; // 0.01f +    private float particleInitSep = 2f; // 0.01f 
-    private int particleCount = 5000; // 500, make public later s.t. can adjust in Inspector+    private int particleCount = 10; // 500, make public later s.t. can adjust in Inspector
     private float widthToHeightRatioInitState = 2.0f; // radio of rectangle in which particles are placed initially     private float widthToHeightRatioInitState = 2.0f; // radio of rectangle in which particles are placed initially
     private float particleInitSepRandRange = 0.0f; // should be smaller than particleInitSep / 2 to avoid overlap     private float particleInitSepRandRange = 0.0f; // should be smaller than particleInitSep / 2 to avoid overlap
 + 
     // spatial hashing stuff     // spatial hashing stuff
     private CollisionDetectionType collisionDetectionType = CollisionDetectionType.SpatialHashingDict;      private CollisionDetectionType collisionDetectionType = CollisionDetectionType.SpatialHashingDict; 
     Dictionary<int, List<ParticleModel>> particleModelDict = new Dictionary<int, List<ParticleModel>>();     Dictionary<int, List<ParticleModel>> particleModelDict = new Dictionary<int, List<ParticleModel>>();
     private float gridSize; // should equal diameter of largest particle     private float gridSize; // should equal diameter of largest particle
 + 
     // fields used in code below     // fields used in code below
     private ParticleModel[] particleModels;     private ParticleModel[] particleModels;
Zeile 501: Zeile 510:
     private Color colorNoCollision = Color.green;     private Color colorNoCollision = Color.green;
     private Color colorCollision = Color.red;     private Color colorCollision = Color.red;
 + 
     void Start()     void Start()
     {     {
Zeile 507: Zeile 516:
         InitParticles(); // create particles         InitParticles(); // create particles
     }     }
 + 
     void InitParticles()     void InitParticles()
     {     {
         Debug.Log("Instantiate Particles");         Debug.Log("Instantiate Particles");
         Debug.Log("Collision Detection Type: " + collisionDetectionType);         Debug.Log("Collision Detection Type: " + collisionDetectionType);
 + 
         // some calcs         // some calcs
         minDistanceSquared = particleDiameter * particleDiameter;         minDistanceSquared = particleDiameter * particleDiameter;
         particleRadius = particleDiameter / 2;         particleRadius = particleDiameter / 2;
         gridSize = particleDiameter; // for spatial hashing         gridSize = particleDiameter; // for spatial hashing
 + 
         // Check if prefab is assigned         // Check if prefab is assigned
         if (prefabParticle == null)         if (prefabParticle == null)
Zeile 524: Zeile 533:
             return;             return;
         }         }
 + 
         // Create new arrays for particle models and views         // Create new arrays for particle models and views
         particleModels = new ParticleModel[particleCount];         particleModels = new ParticleModel[particleCount];
         particleViews = new ParticleView[particleCount];         particleViews = new ParticleView[particleCount];
         particleRenderers = new SpriteRenderer[particleCount];         particleRenderers = new SpriteRenderer[particleCount];
 + 
         // Setup stuff for inital positions         // Setup stuff for inital positions
         int nx = Mathf.FloorToInt(Mathf.Sqrt((float)particleCount) * Mathf.Sqrt((float)widthToHeightRatioInitState));         int nx = Mathf.FloorToInt(Mathf.Sqrt((float)particleCount) * Mathf.Sqrt((float)widthToHeightRatioInitState));
Zeile 538: Zeile 547:
             // throw; // new Exception("initial positions wrongly calculated!");             // throw; // new Exception("initial positions wrongly calculated!");
         }         }
-        + 
         // CREATE A PARTICLE AND ITS VIEW, THEN ASSIGN TO ARRAY         // CREATE A PARTICLE AND ITS VIEW, THEN ASSIGN TO ARRAY
         for (int i = 0; i < particleCount; i++)         for (int i = 0; i < particleCount; i++)
Zeile 548: Zeile 557:
             float y = ny / 2 * (particleRadius + particleInitSep) - iy * (particleRadius + particleInitSep);             float y = ny / 2 * (particleRadius + particleInitSep) - iy * (particleRadius + particleInitSep);
             Vector2 position = new Vector2(UnityEngine.Random.Range(x - particleInitSepRandRange, x + particleInitSepRandRange),UnityEngine.Random.Range(y - particleInitSepRandRange, y + particleInitSepRandRange));             Vector2 position = new Vector2(UnityEngine.Random.Range(x - particleInitSepRandRange, x + particleInitSepRandRange),UnityEngine.Random.Range(y - particleInitSepRandRange, y + particleInitSepRandRange));
 + 
             Vector2 velocity;             Vector2 velocity;
             // initial velocity             // initial velocity
Zeile 559: Zeile 568:
                 velocity = new Vector2(0, 0);                 velocity = new Vector2(0, 0);
             }             }
 + 
             ParticleModel particleModel = new ParticleModel(position, velocity, 1.0f);             ParticleModel particleModel = new ParticleModel(position, velocity, 1.0f);
 + 
             // create game object for particle with prefab as image, will be added to Unity Hierarchy:             // create game object for particle with prefab as image, will be added to Unity Hierarchy:
             GameObject particleGameObject = Instantiate(prefabParticle, particleModel.position, Quaternion.identity);             GameObject particleGameObject = Instantiate(prefabParticle, particleModel.position, Quaternion.identity);
Zeile 574: Zeile 583:
                     Debug.LogWarning("ParticleView component was missing on prefab, automatically added for particle");                     Debug.LogWarning("ParticleView component was missing on prefab, automatically added for particle");
                 }                 }
 + 
                 particleView.Bind(particleModel);                 particleView.Bind(particleModel);
 + 
                 // assign particle model, view and renderer to array                 // assign particle model, view and renderer to array
                 particleModels[i] = particleModel;                 particleModels[i] = particleModel;
Zeile 587: Zeile 596:
             }             }
         }         }
 + 
         // Set correct color         // Set correct color
         for (int i = 0; i < particleCount; i++)         for (int i = 0; i < particleCount; i++)
Zeile 593: Zeile 602:
             particleRenderers[i].color = colorNoCollision;             particleRenderers[i].color = colorNoCollision;
         }         }
 + 
     }     }
 + 
     void Update()     void Update()
     {     {
         Step(); // one simulation step         Step(); // one simulation step
     }     }
- + 
-    void CollisionDetectionBruteForce() +
-    { +
-        for (int i = 0; i < particleCount; i++) +
-        { +
-            ParticleModel particleModel1 = particleModels[i]; +
-            for (int j = i + 1; j < particleCount; j++) +
-            { +
-                if (i == j) continue; +
-                ParticleModel particleModel2 = particleModels[j]; +
- +
-                Vector2 delta = particleModel1.position - particleModel2.position; +
-                float distSq = delta.sqrMagnitude; +
-                if (distSq < minDistanceSquared) +
-                { +
-                    particleModel1.isColliding = true; +
-                    particleModel2.isColliding = true; +
-                } +
-            } +
-        } +
-    } +
     // SPATIAL HASING METHODS     // SPATIAL HASING METHODS
     int GetGridCellCoord(float x)     int GetGridCellCoord(float x)
Zeile 627: Zeile 615:
         return Mathf.FloorToInt(x / gridSize);         return Mathf.FloorToInt(x / gridSize);
     }     }
-    + 
     int CalculateCellHash(int xCell, int yCell)     int CalculateCellHash(int xCell, int yCell)
     {     {
         return xCell * 73856093 ^ yCell * 19349663;         return xCell * 73856093 ^ yCell * 19349663;
     }     }
- +  
-    void CollisionDetectionHashTableDict(){ +  
-        // BUILD PARTICLE DICT +    // get all particles in same and eight neighboring cells 
-        // clear dict +    // two options: same method once as IEnumerable and once returning a list. Same performance. 
-        particleModelDict.Clear(); +    IEnumerable<ParticleModel> GetNeighbors(Vector2 position) 
-        // iterate over all particles, determine hash and add to dict +    { 
-        foreach (var particleModel in particleModels)+        int xCell = GetGridCellCoord(position.x); 
 +        int yCell = GetGridCellCoord(position.y); 
 +        for (int dx = -1; dx <= 1; dx++)
         {         {
-            int xCell = GetGridCellCoord(particleModel.position.x)+            int x = xCell + dx
-            int yCell GetGridCellCoord(particleModel.position.y); +            for (int dy -1dy <1dy++)
-            int hash CalculateCellHash(xCell, yCell); +
-            if (!particleModelDict.ContainsKey(hash))+
             {             {
-                particleModelDict[hash] new List<ParticleModel>()+                int y yCell + dy
-            } +                int hash = CalculateCellHash(x, y); 
-            particleModelDict[hash].Add(particleModel); +                if (!particleModelDict.ContainsKey(hash)) continue; 
-        } +                foreach (var particle in particleModelDict[hash])
- +
-        // CHECK FOR COLLISIONS +
-        foreach (var particleModel in particleModels) +
-        { +
-            foreach (var neighbour in GetNeighbors(particleModel.position)+
-            { +
-                if (particleModel == neighbour) continue; +
- +
-                float distanceSquared = (particleModel.position - neighbour.position).sqrMagnitude; +
-                if(distanceSquared < minDistanceSquared)+
                 {                 {
-                    particleModel.isColliding = true; +                    yield return particle;
-                    neighbour.isColliding = true;+
                 }                 }
             }             }
 + 
         }         }
     }     }
- +  
-    // get all particles in same and eight neighboring cells +    List<ParticleModel> GetNeighborsList(Vector2 position)
-    IEnumerable<ParticleModel> GetNeighbors(Vector2 position)+
     {     {
 +        List<ParticleModel> neigbours = new List<ParticleModel>();
         int xCell = GetGridCellCoord(position.x);         int xCell = GetGridCellCoord(position.x);
         int yCell = GetGridCellCoord(position.y);         int yCell = GetGridCellCoord(position.y);
Zeile 682: Zeile 660:
                 foreach (var particle in particleModelDict[hash])                 foreach (var particle in particleModelDict[hash])
                 {                 {
-                    yield return particle;+                    neigbours.Add(particle);
                 }                 }
             }             }
 + 
         }         }
 +        return neigbours;
     }     }
 + 
     void Step()     void Step()
     {     {
Zeile 697: Zeile 676:
             particleModel.position += particleModel.velocity * dt;             particleModel.position += particleModel.velocity * dt;
         }         }
 + 
         for (int i = 0; i < particleCount; i++)         for (int i = 0; i < particleCount; i++)
         {         {
Zeile 703: Zeile 682:
             particleModels[i].isColliding = false;             particleModels[i].isColliding = false;
         }         }
 + 
         // COLLISION DETECTION         // COLLISION DETECTION
         if (collisionDetectionType == CollisionDetectionType.BruteForce){         if (collisionDetectionType == CollisionDetectionType.BruteForce){
-            CollisionDetectionBruteForce();+            // // COLLISION DETECTION BRUTE FORCE START 
 +            for (int i = 0; i < particleCount; i++) 
 +            { 
 +                ParticleModel particleModel1 = particleModels[i]; 
 +                for (int j = i + 1; j < particleCount; j++) 
 +                { 
 +                    if (i == j) continue; 
 +                    ParticleModel particleModel2 = particleModels[j]; 
 + 
 +                    Vector2 delta = particleModel1.position - particleModel2.position; 
 +                    float distSq = delta.sqrMagnitude; 
 +                    if (distSq < minDistanceSquared) 
 +                    { 
 +                        particleModel1.isColliding = true; 
 +                        particleModel2.isColliding = true; 
 +                    } 
 +                } 
 +            } 
 +            // // COLLISION DETECTION BRUTE END
         }         }
         else if (collisionDetectionType == CollisionDetectionType.SpatialHashingDict)         else if (collisionDetectionType == CollisionDetectionType.SpatialHashingDict)
         {         {
-            CollisionDetectionHashTableDict();+            // // COLLISION DETECTION WITH HASH TABLE START 
 +            // build particle dict 
 +            // clear dict 
 +            particleModelDict.Clear(); 
 +            // iterate over all particles, determine hash and add to dict 
 +            foreach (var particleModel in particleModels) 
 +            { 
 +                int xCell = GetGridCellCoord(particleModel.position.x); 
 +                int yCell = GetGridCellCoord(particleModel.position.y); 
 +                int hash = CalculateCellHash(xCell, yCell); 
 +                if (!particleModelDict.ContainsKey(hash)) 
 +                { 
 +                    particleModelDict[hash] = new List<ParticleModel>(); 
 +                } 
 +                particleModelDict[hash].Add(particleModel); 
 +            } 
 + 
 +            // check for collisions 
 +            foreach (var particleModel in particleModels) 
 +            { 
 +                foreach (var neighbour in GetNeighbors(particleModel.position)) 
 +                { 
 +                    if (particleModel == neighbour) continue; 
 + 
 +                    float distanceSquared = (particleModel.position - neighbour.position).sqrMagnitude; 
 +                    if (distanceSquared < minDistanceSquared) 
 +                    { 
 +                        particleModel.isColliding = true; 
 +                        neighbour.isColliding = true; 
 +                    } 
 +                } 
 +            } 
 +            // // COLLISION DETECTION WITH HASH TABLE
         }         }
         else{         else{
             Debug.Log("ERROR no valid collision detection selected");             Debug.Log("ERROR no valid collision detection selected");
         }         }
 + 
         // CHANGE COLOR IF COLLIDE         // CHANGE COLOR IF COLLIDE
         for (int i = 0; i < particleCount; i++)         for (int i = 0; i < particleCount; i++)
Zeile 728: Zeile 757:
             }             }
         }         }
 + 
         // COLLISION HANDLING: WALLS ONLY         // COLLISION HANDLING: WALLS ONLY
         for (int i = 0; i < particleCount; i++)         for (int i = 0; i < particleCount; i++)
Zeile 763: Zeile 792:
  
 ==== Auftrag A - Teil III ==== ==== Auftrag A - Teil III ====
 +
 +   1. Implementiere nun elastische Stösse für die Brute-Force Collision Detection (siehe Theorie unten).
 +   1. Füge nun einen Energie-/Geschwindigkeitsverlust bei jeder Kollision ein - dies simuliert Reibung innerhalb des 'Fluids'.
 +   1. Mache nun das Gleiche für die Collision Detection mit Spatial Hashing.
  
 Formel für elastischen Stoss in 2D: Formel für elastischen Stoss in 2D:
 [[https://sca.ksr.ch/lib/exe/fetch.php?media=talit:collisions.pdf|Slides Collision]] [[https://sca.ksr.ch/lib/exe/fetch.php?media=talit:collisions.pdf|Slides Collision]]
 +
 +===== - Smoothed Particle Hydrodynamics (SPH) =====
 +
 +**Quellen:**
 +
 +   * {{ :talit:sph_fluid_sim_mat_mueller.pdf |Particle-Based Fluid Simulation for Interactive Applications}}
 +   * {{ :talit:sph_tutorial.pdf |Smoothed Particle Hydrodynamics - Techniques for the Physics Based Simulation of Fluids and Solids}}
 +
  
  • talit/fluid_simulation.1763307477.txt.gz
  • Zuletzt geändert: 2025-11-16 15:37
  • von sca