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-12-01 15:35] – [Unity Tipps] scatalit:fluid_simulation [2025-12-15 14:45] (aktuell) – [2. Smoothed Particle Hydrodynamics (SPH)] sca
Zeile 469: 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 480: 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 487: 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 510: 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 516: 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 533: 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 547: 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 557: 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 568: 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 583: 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 596: Zeile 596:
             }             }
         }         }
 + 
         // Set correct color         // Set correct color
         for (int i = 0; i < particleCount; i++)         for (int i = 0; i < particleCount; i++)
Zeile 602: 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 636: 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 +
-        // 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; +
-                } +
-            } +
-        } +
-    } +
     // get all particles in same and eight neighboring cells     // get all particles in same and eight neighboring cells
     // two options: same method once as IEnumerable and once returning a list. Same performance.     // two options: same method once as IEnumerable and once returning a list. Same performance.
Zeile 695: Zeile 641:
                 }                 }
             }             }
 + 
         }         }
     }     }
 + 
     List<ParticleModel> GetNeighborsList(Vector2 position)     List<ParticleModel> GetNeighborsList(Vector2 position)
     {     {
Zeile 717: Zeile 663:
                 }                 }
             }             }
 + 
         }         }
         return neigbours;         return neigbours;
     }     }
-    + 
     void Step()     void Step()
     {     {
Zeile 730: 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 736: 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 761: 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 796: 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.1764603357.txt.gz
  • Zuletzt geändert: 2025-12-01 15:35
  • von sca