Animating Moving Soil or Sand Notes prepared by Hui Gong and Howard Hamilton Introduction We describe animation techniques suitable for illustrating the interaction of machinery and soil (dirt, sand, etc.) on the ground. This is of particular interest when creating animated simulations for the purpose of training people to use specialized construction equipment. When implementing a virtual world where other models can interact with the ground, it is useful to convert a portion of, or the entire ground surface, into many particles, which represent dirt, sand, clay and similar substances. Large particles that we can distinctly perceive are called granular material [5]. Particles that represent granular material exist in a broad range of contexts. Granular material is the second most manipulated material in industrial contexts [6]. Granular material is different from other materials, such as liquids or solid objects, because of the variety of ways in which it can behave. Multiple particles frequently interact with each other. When they are scattered in the air, they can break into pieces. When they are pushed, they can flow like a fluid. When they are lying on the ground, they act like part of the surface. To simulate all of these behaviors, it is worthwhile to find an efficient method to simulate soil treated as either ground or particles, depending on the context. 1 The critical slope angle is the maximum slope such that soil does not slip. This angle depends on the cohesion of the soil, the size of the particles, and the moisture level [7, 12]. Height Maps A height map or heightfield is a 2D grid of heights with indices corresponding to individual locations. In graphics and animation, it is typically stored as an 8-bit greyscale image, where each individual pixel gives the height at the corresponding (X, Y) location. If the size of a 2D height map is n x n, then the height map resolution is n. A 2D height map with resolution n defines a grid of (n – 1) x (n – 1) cells (i.e., spaces between the grid points); here the n – 1 is the grid size. Height map methods are generally appropriate when the terrain is of significant size and does not have any overhangs or caves. These methods represent granular material as 2D terrain [23]. Height Map Approach to Animating Moving Sand In the Height Map Approach, terrain is based on a height map. Granular material that is not moving is represented in the height map, and granular material that is moving is represented separately. Sand in motion is simulated using many particles, which are represented as cubes or other shapes. Piles of sand are formed by moving the particles around. We ensure that the surface is sloped at no more than the critical slope angle θ after the sand comes to rest. Resting sand above the critical slope angle is moved to nearby locations and the height of the surface is changed accordingly. Deformation of the terrain where sand falls 2 Figure 3. Adding height distribution The goal is to change the height of the terrain as sand falls. If sand falls at a single point, the goal is to form a round pile with a slope of θ. The pile should be roughly cone shaped. Since our approach is based on a height map, the height of the terrain is represented as if it consisted of the heights of poles at individual grid coordinates, as shown in Figure 3. Imagine Suppose sand with height H0 is added at point P0 in 3D space, which is vertically aligned with coordinates (nx, ny) in the grid space. The variable wx represents the distance from the left boundary of the grid cell to the coordinates (nx, ny) in the x direction of the terrain, and wy is the distance from the bottom boundary of this grid cell to the coordinates (nx, ny) in the y direction of the terrain. Since the increased height can only be applied to the grid coordinates on the height map, we distribute the added height from position P0 to the nearest four grid coordinates, here called P00, P01, P10, and P11. To add the small height b from each sand particle to the height map, such that the height added to 3 each coordinate is related to its distance from the point of impact, we use the formulas below. convert nx from world coordinates to height map coordinate convert ny from world coordinates to height map coordinate wx = nx – floor(nx); wy = ny – floor(ny); // lower-left coordinate P00 = ( floor(nx), floor(ny) ) H00 = b * ( 1 - wx ) * ( 1 - wy ); P00.height += H00; // upper-right coordinate P01 = ( floor(nx) + 1, floor(ny) ) H01 = b * wx * ( 1 – wy ); P01.height += H01; // lower-right coordinate ( floor(nx), floor(ny) + 1 ) H10 = b * ( 1 - wx ) * wy; P10.height += H10; // upper-right coordinate ( floor(nx) + 1, floor(ny) + 1 ) H11 = b * wx * wy; P11.height += H11; To ensure that the sand is stable, we check that it does not exceed the critical slope angle. So after distributing the increase in height at the four coordinates as described above, we check the slope angle at each of these coordinates against the critical angle. Recall that the critical slope angle θ is the maximum slope angle for which the sand does not slip. We check the slope angle using the four height map coordinates instead of P0 because they are available in the representation. It easier to deal with the one special case of sand being added at P0 and then all subsequent cases are of moving sand between poles. Consider the pole at position Pij with height Hij to which we have added some sand. Suppose that H1, ..., Hn are the heights of n nearby poles at grid coordinates P1, ..., Pn. 4 The slope angle ak between any pair of grid coordinates (e.g. Pij and Pk) can be calculated from the difference Hk - Hij in the height of the poles as follows: H k - H ij , ak = arctan distance( P , P ) ij k where k = 1, 2, …, n. Next the method repeatedly chooses the nearby point Pk for which the angle ak is greatest. If the angle ak is greater than θ, we move sand from Pij to Pk according to the amount that will fit there. If the angle ak is not greater than θ, then we stop moving sand. The pseudocode for determining slippage is as follows: // move sand until all points are below critical angle // or no sand remains sand = some amount of sand to move do { Calculate all angles a1, a2, …, an k = indexOfMax( /* a1, a2, …, an */ ); // slip occurs? if (ak > θ) { /* move some amount d of sand from Pij to Pk */ sand -= d; } } while (sand > 0 && ak > θ); Overall, to deform terrain when sand falls, the above method adjusts the height of the surface at each grid point in a circle around the location where the sand falls. The effect is to change the heights of some poles near (nx, ny). 5 In practice, to avoid calculating the arctan repeatedly, we can approximate the task of finding the maximum angle by finding the maximum distance from the expected angle at the distance. This approximation is described shortly. As well, to ensure more uniform response time, the “while” statement in the above can be replaced by an “if” statement. If there is some sand that is not moved to the first candidate neighbor selected above, it is left at its current position. 6 (a) Calculation of slope angles at Pij (b) Offset heights of neighbors Figure 4. Calculation of slope angles based on the height map 7 We now describe the approach to adding sand to terrain in more detail. In Section 3.1.1, we explain the preprocessing steps and in Section 3.1.2, we give the detailed algorithm. 3.1.1. Preprocessing 1) Get the height map 2D coordinates (nx, ny) from the 3D location P0 = (x, y, z) where the sand is falling; nx = (P0.x / WORLD_WIDTH) * (heightmap.Width - 1); ny = (P0.z / WORLD_LENGTH) // 3D z --> 2D y * (heightmap.Length - 1 ); 2) Set the fractional parts of the coordinates (nx, ny) as weight parameters wx and wy; wx = nx - floor(nx); wy = ny - floor(ny); 3) Let b be a variable representing the total height of all sand particles deposited at location P0 in a given frame. Set b to be a fixed value related to a particle’s volume (for details see Section 4.4) when initializing the terrain to ensure that the terrain changes exactly by the same amount when each sand particle falls. Note that if b is too small, there will be no change to the height of the height map. float b = 1 * WORLD_WIDTH / (heightmap.Width - 1); 4) Add height H00, H01, H10, and H11 to the four grid points in the height map. 8 3.1.2. Sand placement Figure 5. Pile of soil with r = 2.3 To ensure that the method applies to any shape of terrain, we first distribute the height to the nearest grid points. Then we distribute it again from those points and so on. By using these local distributions, we avoid the need to consider the overall shape of the terrain. To form a (nearly) round pile, we examine all grid points in a circle of radius r from H0, for example r = 4. The most appropriate value for r depends on the height map resolution and the detail we want to achieve. Values of less than 5 tend to result in square piles. Let n represent the number of neighbors located in a circle of radius r. The best value for r can then be determined by solving the Gauss circle problem [29]. 9 Figure 6. Gauss Circle Problem As Figure 6 shows, Gauss proved that the number of integer coordinates N(r) located in a circle of radius r have the following relationship: N(r) = π r2 + E(r), where |E(r)| ≤ 2 2 π r. We define n = N(r) – 1, since we do not count the center as a neighbor of itself. For all neighbors, we determine the angle between the top of the neighbor’s pole and the pole at the current point Pij. If any angle is greater than the critical slope angle, then move one unit height of sand toward one of the directions where the angle is greatest. Then repeat the process until no angle is greater than the critical slope angle. An alternative method would be to equally distribute the amount among all poles where the angle is greatest. If a sufficient number of neighbors are used, then the result will be a pile shaped roughly like a cone. Table 1 shows the number of neighbors and the resulting pile shape for a range of possible r. For example, if r is 4, then n is 46 and the shape has 20 edges. 10 Table 1. Radius r vs. pile shape r n Base Shape 1 ≤r < 2 4 Square 8 Square 12 Square 20 Octagon 2 2 ≤r < 3 24 Square 3 ≤r < 10 28 16-gon 2 ≤r < 2 2 ≤r < 5 5 ≤r < 2 2 (16-edge Polygon) 10 ≤r < 13 36 Octagon 13 ≤r < 4 42 Octagon 4 ≤r < 17 46 20-gon (20-edge Polygon) The algorithm for creating cone-shaped sand piles, given as function CreatePile, is as follows. The inputs include: heightmap: a 2D height map, 11 (x, y): the pole position where sand has been added (the source pole) height: the height of the source pole r : the radius of the circle of neighbors 1) Initialize some useful constants based on the In the code below, HORIZONTAL_CONVERSION is the conversion factor used to convert between world (terrain) coordinates (in say meters) and height map array indices. We assume that this conversion factor is the same for both horizontal dimensions. CRITICAL_SLOPE_ANGLE is the critical slope angle θ (in radians). WORLD_WIDTH = 200; // in meters CRITICAL_SLOPE_ANGLE = (30.0 / 180.0 * PI); HORIZONTAL_CONVERSION = WORLD_WIDTH / (heightmap.Width – 1); SLOPE = tan(CRITICAL_SLOPE_ANGLE) * HORIZONTAL_CONVERSION; 2) Generate a roughly circular shape of radius r to cover all nearby integer coordinates. The poles at these locations are called the neighbors. Consider the offset height of all neighbors, where the offset height is the amount by which the height of the neighbor is less than the expected (cone-line) height of a pole at this distance from the source pole if the source pole were the center of a cone with slope θ, given that the neighbor is at its current distance from the source pole (see Figure 4(b)). Select the neighbor with the maximum offset height and name it the best neighbor. We will store best neighbor’s position and height in a structured variable named bestNeighbor for temporary use. The fields of the structure are named x, y, and offset. In the loop below, chr is the cone height reduction, i.e., the reduction in height that would be expected at this distance from the source pole if it were the center of a cone with slope θ. 12 // initialize neighbor at which maximum offset height occurs bestNeighbor.x = x; // dummy value bestNeighbor.y = y; // dummy value bestNeighbor.offset = 0; ny = floor(r); // process all points across length of circle for (j = y - ny; j <= y + ny; j++) { nx = floor( sqrt(r*r - (j-y)*(j-y)) ); // process all points across the width of circle // at this y level of the circle for (i = x – nx; i <= x + nx; i++) { // ignore any point (i,j) that is at the // center of the circle or beyond the edge of // the terrain or beyond the edge of the circle if((j == y && i == x) || j < 0 || j > heightmap.Length – 1 || i < 0 || i > heightmap.Width - 1 || (j - y) * (j - y) + (i - x) * (i - x) > r * r) continue; // assuming a cone centered at (x, y) should exist, // determine the cone height reduction for // the neighbor at (i, j) based on the distance // between it and the source pole at (x, y) distance = sqrt( (i-x)*(i-x) + (j-y)*(j-y) ); chr = SLOPE * distance; expectedHeight = height – chr; // the offset of this neighbor is the amount its // current height is less than its expected height curHeight = heightmap.Data[j, i] * VERTICAL_CONVERSION; offset = expectedHeight – curHeight; // // // if { if this offset is greater than the highest known offset, then update the selection of the best neighbor (offset > bestNeighbor.offset) bestNeighbor.x = i; 13 bestNeighbor.y = j; bestNeighbor.offset = offset; } } } 3) Determine whether or not to let the sand at this coordinate flow to the neighbor found above or not. If so, do each of the steps below using this neighbor. In the following code, c is the offset height of the neighbor found above. assert(y >= 0 && y <= heightmap.Length – 1); assert(x >= 0 && x <= heightmap.Width - 1); deltaHeight = height – heightmap.GetHeight(x, y); // positive offset? if (bestNeighbor.offset > 0) { // Determine additional height to add to best neighbor. // Only give a neighbor enough to bring it up to its // expected height. if (bestNeighbor.offset < deltaHeight) extraHeight = bestNeighbor.offset; else extraHeight = deltaHeight; // increase height of pile at best neighbor CreatePile( bestNeighbor.x, bestNeighbor.y, heightmap.GetHeight(bestNeighbor.x, bestNeighbor.y) + extraHeight); // any remaining height goes to (x, y) in height map heightmap.Data[y, x] = (height - extraHeight) / VERTICAL_CONVERSION; } else { heightmapData[y, x] = height / VERTICAL_CONVERSION; } 4) 14 Texturing of the terrain where sand falls We should also change the texture of the terrain when sand is falling on the ground. When the height at a neighboring location is above that allowed according the maximum slope, we assume that the location is already solid. In this case, we do not change the textures on it. if (heightmap.GetHeight(x, y) < heightmap.GetHeight(bestNeighbor.x, bestNeighbor.y) + chr) // process points (i,j) around current point (x,y) for (j = -1; j <= 0; j++) for (i = -1; i <= 0; i++) for (k = 0; k < heightmap.alphaMapLayers; k++) { // is sand falling outside of the map? if(y + j < 0 || x + i < 0 || y + j >= heightmap.Length – 1 || x + i >= heightmap.Width – 1) continue; // // // if change texture layer number to the maximum, if it represents the texture of sand; otherwise, change it to the minimum (k == 0) splatMapData[y + j, x + i, k] = 1; else splatMapData[y + j, x + i, k] = 0; } 3.3.4. Pouring Sand To achieve realistic effects, some parameters of the sand particles are adjusted before the particles hit the ground. When particles fall from the edge of the bucket, the speed in the XOZ plane is set to a smaller value, producing a visual result that seems more like sand than ice balls. At the moment that a falling cube hits the ground, the height of its highest point is at most 3 2 b , where b is the side length of the cube. To avoid 15 recalculating the square root, 3 2 is represented as 0.86 in the code. The movement of the sand particle is implemented in the FixedUpdate function since a smaller time step is needed to increase accuracy of the movements. The code follows below. // NOTATION IN THE FOLLOWING PARTS OF THIS PAPER HAS // NOT YET BEEN MADE CONSISTENT WITH THE FIRST PART #define LEVEL_VEL_CONS 1.1f #define DOWNWARD_VEL_CONS -10.0f void drop() { // process each sand particle foreach(GameObject d in mylist.ToList()) { d.rigidbody.velocity = new Vector3 (d.rigidbody.velocity.x / LEVEL_VEL_CONS, d.rigidbody.velocity.y, d.rigidbody.velocity.z / LEVEL_VEL_CONS); // if particle is moving upwards, change it to moving // downwards if(d.rigidbody.velocity.y > 0) d.rigidbody.velocity = new Vector3(0, DOWNWARD_VEL_CONS, 0); // convert the 3D world position of object d to // 2D terrain height map coordinates Vector3 p = d.transform.position; int nx = wtonx(p); int ny = wtony(p); // point outside of terrain? if(nx > MAPGRIDSIZE || ny > MAPGRIDSIZE) continue; // find the height of the terrain at the neighboring // grid point that has the greatest height and the // height of the lowest part of the bucket float max = 0; // maximum height float min = MAX_TERRAIN_HEIGHT; // minimum height for(int j = 0; j <= 1; j++) for(int i = 0; i <= 1; i++) 16 { tmp = heightmapData[ny + j, nx + i] * td.heightmapScale.y; if (tmp > max) // highest neighboring point max = tmp; tmp = low[ny + j, nx + i]; // lowest part of bucket if (tmp < min) min = tmp; } // if the sand particle is sufficiently far under the bucket // (i.e., it is no longer touching the bucket) and it is // touching the terrain, then it becomes part of the // terrain. The maximum height of a cube with side length // b stood on its corner is sqrt(3)/2 * b. const float SQRT_3_OVER_2 = 0.86; if ((p.y <= min – SQRT_3_OVER_2 * b) && (p.y <= max + SQRT_3_OVER_2 * b)) { hit(p); Destroy(d); mylist.Remove(d); } } } 3.3.5. Hitting To simulate the action of fallen soil becoming a part of the ground, we remove the particles representing this soil from the simulation, as described at the end of the previous section, and adjust the terrain height map at the exact positions of the particles. Here we describe the details of the hit function that performs the adjustment. We apply a hit function to every position where a sand particle lands on the ground. The hit function deforms the ground slightly for each particle. It implements the approach described in Section 3.1. The only parameter used is the (x, y, z) position of the hit. 17 void hit(Vector3 pos) { // compute standard local positions and offsets Vector3 terrainLocalPos = pos - terr.transform.position; float nx = Mathf.InverseLerp( 0, td.size.x, terrainLocalPos.x ) * (td.HeightMapWidth - 1); float ny = Mathf.InverseLerp( 0, td.size.z, terrainLocalPos.z ) * (td.HeightMapLength - 1); float fx = Mathf.Floor(nx); float fy = Mathf.Floor(ny); float wx = nx - fx; float wy = ny - fy; // deform each neighboring grid point in the height map for(int j = 0; j <= 1; j++) for(int i = 0; i <= 1; i++) { // compute increased height resulting from impact float addHeight = (float)((1 - i) + (2 * i - 1) * wx) * ((1 - j) + (2 * j - 1) * wy) * b; float oldHeight = heightmapData [(int)fy+j, (int)fx+i] * td.heightmapScale.y; // add increased height to current point float[,] newHeightData = new float[1, 1] { { oldHeight + addHeight } }; CreatePile((int)fx+i , (int)fy+j, newHeightData[0, 0]); } } 18 4.4. Volume conversion When the sand is dug up, a unit of sand is converted from terrain to sand particles. Since the change of terrain is calculated by changing the height map, we can get the total changed volume by accumulating all the changes from every 2D-coordinate of the height map. For each change in a coordinate, we obtain the volume change by considering the nearest six coordinates as the terrain height map using a tree structure to represent the change. Figure 15. Heightmap structure of terrain Figure 15 shows the wireframe view of a tree structured height map of terrain after changes by the excavator. One typical quadrilateral on the terrain surface is indicated in the figure. The grid size for the 2D height map is l. 19 The volume change at each coordinate (x, y) actually consists of six tetrahedrons shared with the same height h(x,y), one for each two neighboring coordinates. The formula for the change is as follows: V(x,y) 1 1 = 6 [ h(x, y) ( l 2 )] 3 2 = h( x, y ) l 2 , where l= Terrain si ze Heightmsp resolution From Figure 16(a), we can see the six tetrahedrons generated by increasing the height of the center coordinate on a flat surface. The tetrahedrons have three side faces. They also have triangle bases, as shown in Figure 16(b), the area of which is l2/2, as shown in Figure 16(c). Six similar tetrahedrons are generated when we decrease the height of the center coordinate. If the surface is not flat, we consider six pairs of tetrahedrons, representing the old and new surfaces. Overall, the result of changing a height always involves calculations with the same shape and formula. As the size of the particles being generated is based on the same length l, the converted volume is only affected by . So at runtime, we can calculate the number of cubes to be generated by considering only the changes in the height values. 20 (a) 3D view of six tetrahedrons (b) base view of six tetrahedrons (c) area of base of one Figure 16. Nearest six tetrahedrons When a sand cube drops on the ground, we distribute the height of sand to the nearest four height map coordinates by parameters which add up to equal the sand height. Thus, 21 the total height and the total volume are not changed by a completed movement of some sand. While some sand is in the excavator, the total height of the terrain is reduced, but when the sand is dropped on the ground, it is restored to its initial value. By examining the total height of terrain height map over time, one can examine the accuracy of the implementation. Figure 17 shows one result after a terrain change. The total change to height, displayed in the upper left corner, is zero. Figure 17. Total height map height change is 0 22 References [1] M. Zyda. From visual simulation to virtual reality to games. Computer, 2005, 38(9): 25-32. [2] T. A. Galyean and J. F. Hughes. An interactive volumetric modeling technique. Proceedings of the ACM SIGGRAPH Computer Graphics, 1991: 267-274. [3] Y. Oda, K. Muraoka, and N. Chiba. Particle-based visual simulation of virtual clay. Information Processing Society of Japan, 2001, 42(5): 1142-1150. [4] M. Matsumiya, H. Takemura, and N. Yokoya. An immersive modeling system for 3D free-form design using implicit surfaces. Proceedings of the ACM symposium on virtual reality software and technology, New York, 2000: 67-74. [5] H. M. Jaeger, S. R. Nagel, and R. P. Behringer. Granular solids, liquids, and gases. Reviews of Modern Physics, 1996, 68(4): 1259-1273. [6] P. Richard, M. Nicodemi, R. Delannay, P. Ribière, and D. Bideau. Slow relaxation and compaction of granular systems. Nature Materials, 2005, 4 (2): 121-128. [7] X. Li and J. M. Moshell. Modeling soil: Realtime dynamic models for soil slippage and manipulation. ACM Computer Graphics (SIGGRAPH ’93 Proceedings), 1993, 361-368. [8] B. Chanclou, A. Luciani, and A. Habibi. Physical models of loose soils dynamically marked by a moving object. Computer Animation ’96, 1996: 27-35. [9] R. W. Sumner, J. F. O’Brien, and J. K. Hodgins. Animating sand, mud, and snow. Computer Graphics Forum, 1999, 18(1): 3-15. 23 [10] Y. L. Zeng, C. I. Tan, W. K. Tai, M. T. Yang, C. C. Chiang, and C. C.Chang. A Momentum Based Deformation System for Granular Material. Computer Animation and Virtual Worlds - CASA 2007, 2007, 18(4-5): 289-300. [11] T. Nishita, H. Iwasaki, Y. Dobashi, and E. Nakamae. A modeling and rendering method for snow by using metaballs. Computer Graphics Forum, 1997, 16(3): 357364. [12] P. Fearing. Computer modelling of fallen snow. ACM Computer Graphics (SIGGRAPH ’00 Proceedings), 2000: 37-46. [13] R. A. Gingold and J. J. Monaghan. Smoothed particle hydrodynamics: Theory and application to non-spherical stars. Monthly Notices of the Royal Astronomical Society, 1977, 181: 375-389. [14] M. Müller, D. Charypar, M. Gross. Particle-based fluid simulation for interactive applications. Proceedings of the 2003 ACM SIGGRAPH/Eurographics Symposium on Computer Animation, San Diego: Eurographics Association, 2003: 154-159. [15] M. Müller, R. Keiser, and A. Nealen. Point based animation of elastic, plastic and melting objects. Proceedings of the 2004 ACM SIGGRAPH/Eurographics Symposium on Computer Animation, Aire-la-Ville: Eurographics Association, 2004: 141-151. [16] K. Hegeman, N.A. Carr, and G.S.P. Miller. Particle-based fluid simulation on the GPU. Computational Science–ICCS 2006, 2006, 3994: 228-235. [17] W. Rungjiratananon, Z. Szego, Y. Kanamori, and T. Nishita. Real-time animation of sand-water interaction. Computer Graphics Forum, 2008, 27(7): 1887-1893. 24 [18] P.A. Cundall, and O.D.L. Strack. A discrete numerical model for granular assemblies. Geotechnique, 1979, 29: 47-65. [19] T. Lenaerts, and P. Dutré. Mixing fluids and granular materials. Computer Graphics Forum, 2009, 28: 213-218. [20] M. Ihmsen, A. Wahl, and M. Teschner. High-resolution simulation of granular material with SPH. Proceedings of the VRIPHYS, 2012, 53-60. [21] M. Ihmsen, A. Wahl, and M. Teschner. A Lagrangian framework for simulating granular material with high detail. Computers & Graphics, 2013, 37(7): 800-808. [22] I. Alduán, and M. A. Otaduy. SPH granular flow with friction and cohesion. Proceedings of the 2011 ACM SIGGRAPH/Eurographics Symposium on Computer Animation, 2011, 25-32. [23] K. Onoue, and T. Nishita. Virtual sandbox. 11th Pacific Conference on Computer Graphics and Applications, Washington: IEEE Computer Society, 2003, 252-262. [24] J. P. Bouchaud, M. Cates, J. Ravi Prakash, and S. Edwards. A model for the dynamics of sandpile surfaces. Journal de Physique I, 1994, 4(10): 1383-1410. [25] P. A. Langston, U. Tüzün, and D. M. Heyes. Discrete element simulation of granular flow in 2D and 3D hoppers: Dependence of discharge rate and wall stress on particle interactions. Chemical Engineering Science, 1995, 50(6): 967-987. [26] N. Bell, Y. Yu, and P. J. Mucha. Particle-based simulation of granular materials, SCA '05 Proceedings of the 2005 ACM SIGGRAPH/Eurographics Symposium on Computer animation. New York, 2005, 77-86. 25 [27] M. Pla-Castells, I. García-Fernández, and R. J. Martinez-Dura. Physically-based interactive sand simulation. Eurographics 2008, Crete: Eurographics Association, 2008, 21-24. [28] B. Zhu, and X. B. Yang. Animating sand as a surface flow. Eurographics 2010. Norrkoping: Eurographics Association, 2010, 1-4. [29] Gauss circle problem, http://en.wikipedia.org/wiki/Gauss_circle_problem#cite_ref-2 [30] TurboSquid, Inc., http://www.turbosquid.com [31] Mohr–Coulomb theory, http://en.wikipedia.org/wiki/Mohr–Coulomb_theory [32] Heightmap, http://en.wikipedia.org/wiki/Heightmap 26 27