GPU Computing Particle Simulation
GPU Computing Particle Simulation
Dipl.-Ing. Jan Novák∗ Dipl.-Inf. Gábor Liktor† Prof. Dr.-Ing. Carsten Dachsbacher‡
Abstract trade speed for quality. We will use a basic integration scheme
called Verlet integration.
In this assignment we will learn how to implement two simple par-
ticle systems on the GPU. In the first simulation the particles do not The Verlet velocity integration defines the new position x(t + ∆t)
interact with each other. However, we will allow particles to be re- and velocity v(t + ∆t) of a particle as:
moved and added to the system, hence we have to perform a stream
compaction that will remove the empty fields in the array. The sec- 1
x(t + ∆t) = x(t) + v(t)∆t + a(t)∆t2
ond assignment will then introduce local constraints that will affect 2
the movement of particles to simulate materials such as cloth. 1
v(t + ∆t) = v(t) + (a(t) + a(t + ∆t))∆t
2
The deadline for the assignment is on 22th of June.
The ∆t refers to a discrete time step that we take between two (cur-
1 Scientific Simulation rent and the new) simulation steps. a(t) refers to the acceleration
(i.e. first derivation of the velocity, second derivation of the posi-
Particle systems are widely used to simulate natural phenomena like tion) of the particle. As a thorough explanation of the underlying
smoke, fire, or water. In numerical simulation, we distinguish two motivation for using this integration scheme is beyond the scope of
approaches that conceptually differ in how they handle the simula- this text, we refer you to Wikipedia for a more detail description of
tion domain. the velocity Verlet integration.
Lagrangian Approach The Lagrangian approach is based on the 1.2 Collision Detection
discretization of the domain into a set of finite mass elements.
These particles are then allowed to move freely, or with respect to Since the particles can interact with the environment, e.g. collide
defined rules, through the environment. Each particle is usually de- with a wall, we need to simulate these interactions. Consider for
scribed by its position and velocity. According to our needs, it can instance a particle that is on one side of a wall at time t and on the
also have any other quantity such as mass, temperature, or radius. opposite side of the same wall at t + ∆t. Obviously, the veloc-
Methods based on tracking particles over time and altering their ity and acceleration of the particle have moved it through the wall.
quantities according to the prescribed laws are called particle Sys- We should try to detect such interactions and correct the position
tems. Since they directly track chunks of matter through the space, of the particle accordingly. The most robust approach is to advance
particle systems can easily guarantee the conservation of mass. On only by ∆t that is collision free: there is no collision within the
the other hand, handling of incompressibility (e.g. in simulation of whole system until ∆t. This is called continuous collision detec-
fluids) might be more difficult to achieve. tion, which requires computing the time to the next collision for
each particle, and setting ∆t to the minimum of these values. In
systems with frequent collisions, this can make the time step very
Eulerian Approach Instead of simulating the phenomena using small considerably slowing down the overall simulation.
chunks of matter, we can partition the domain into a set of small
symmetric cells: voxels. This approach is called Eulerian dis- We will take a simplified approach: we always advance by a con-
cretization and the main idea resides in allowing the matter to freely stant time step and correct for all previously occurring collisions in
move through the fixed grid, while tracing the simulated quantities. a post process. Furthermore, we will only account for one collision
In other words, we do not track the matter itself, but we simulate the during a single time step. Therefore, given the old and new particle
quantities at fixed positions in the domain. The discretization is in position, x(t) and x(t + ∆t), we will search for an intersection of
the simplest case a regular Cartesian grid, however, more advanced the line between these two points and all the objects in the scene. If
techniques often employ hierarchical or multi-resolution structures there is such an intersection, we should correct the position of the
to devote more computation and resolution to areas, where the particle using one of the approaches shown in Figure 1.
quantities change with higher frequency.
In this assignment we will use the Lagrangian approach simulating normal x(t) x(t)
a number of particles that do not interact with each other (first task), ∆t ∆t
v(t) v(t+∆t) v(t)
and a system where the particles represent a cloth and interact with v(t+∆t)
their neighbors under some constraints (e.g. springs). x(t+∆t) surface x(t+∆t) surface
During the simulation the particles will move along complex paths • Possibly create new particles
in 3-dimensional space and collide with solid surfaces. To evaluate • Store new particle data
the results, we need to provide real-time visual feedback about the
status of the simulation. OpenCL is suitable for general-purpose 3.1.1 Integration
programming, but we should employ one of the standard rendering
APIs to efficiently display 3D content on the screen. In order to compute the acceleration of the particle, use the
This assignment demonstrates the basics of interoperability. We gravitational acceleration and the acceleration defined by the
will use OpenCL to update the state of the simulated world (particle mass and the force at the current position of the particle.
positions, velocities, etc.) and OpenGL to display them. In this For fetching the force from the 3D texture use the func-
case, interoperability means that the same resources will be used tion read imagef(gForceField, sampler, lookUp),
in multiple contexts. For example, triplets of floats in an OpenCL where the gForceField and the sampler are parameters that
buffer memory object can be reinterpreted as vertex positions by the kernel obtains and the lookUp is a float4 with the first
OpenGL and displayed as a set of points in space. xyz componets specifying the position and the w defines the mip
level (in our case it should be set to 0). These are already initial-
OpenGL 2.0 defines buffer objects to hold rendering data. Based ized to perform the trilinear interpolation. Since you will have to
on their usage, we can talk about vertex (VBO), pixel (PBO) and perform some arithmetic operations on vectors, we advise you to
texture (TBO) buffer objects. If we want an OpenCL kernel to be use float4 data type for the 3-component position and velocity
able to modify the rendered geometry, we can create a buffer mem- with the fourth component set to 0. You can also define a custom
ory object from a VBO. As OpenCL and OpenGL coexist on the float3 type and write a few basic functions for them, however,
same device in parallel, there can be conflicts when accessing the you will not be able to define custom operators for them, as OpenCL
shared resources. Therefore, before executing OpenCL kernels that does not support C++ constructs.
use the shared buffers, the OpenCL must place a lock these buffers.
3.1.2 Collision Detection
As graphics programming using OpenGL is not covered by our
course, the implementation of the rendering part will be already In order to compute the collision of particles with the scene ob-
provided. To complete this assignment, you only need a basic un- jects, the kernel is also given a pointer to a global array with all
derstanding about the OpenGL context sharing. triangles in the scene. The triangles are stored as a triangle soup:
the triangles are stored in a consecutive chunk of memory, each tri-
3 Task 1: Simple Particle System angle is represented by three vertices, and each vertex is defined
as a float4 variable. Given the old and the new position of the
In this task we will implement a simple particle system that is driven particle, you will construct a ray segment and try to intersect this
by a force field. As there are no interactions or collisions between segment with each of these triangles. The straightforward solution
particles, the algorithm can be trivially parallelized using one thread is to iterate over all vertices in the global memory, construct a trian-
for each particle. We will use a 3D vector field to define the force gle, and call a function that will determine whether the ray segment
field within the simulation domain. This force field is loaded from intersects the triangle.
a file and uploaded to the GPU as a 3D texture. Using a 3D tex- Since all threads are iterating over the same values, there is a great
ture instead of a regular linear array has two advantages: first, we chance to use the local memory to cache the triangles. One of the
can use a 3D vector to conveniently address the 3D texture, second, possible approaches is to read one vertex from the triangle soup by
the hardware can automatically perform a trilinear interpolation of each thread and store it in the local memory. Then all threads can it-
the eight nearest neighbors. In other words, if we address a point erate over the cached triangles in the local memory and perform the
within the texture that is not exactly one of the discrete positions collision test. If the number of triangles is higher then the number
at which the texture samples the signal, we would have to load the of cached triangles, we have to repeat the process of loading and
eight nearest neighbors and perform a trilinear interpolation manu- testing the triangles multiple times. Notice, that if the number of
ally. As this is a frequently used operation in rendering, GPUs have threads is not a multiple of 3 (the number of vertices for one trian-
a hardware implementation that significantly speeds up the trilinear gle), it can happen that we will not read the whole last triangle: the
filtering. threads might load only one or two vertices of the triangle. Unless
we want to take a special care of this case, we have to make sure
3.1 Integration and Collision Detection that the number of threads within a work-group is divisible by 3.
Each particle in our system is defined using a position, velocity, We provide you with a LineTriangleIntersection func-
mass, and age. The first two characteristics are 3-component vec- tion, which computes an intersection of a line with a triangle. Your
tors, whereas the mass and age are scalars, therefore, we can pack task is to call this function for each triangle and find the closest in-
all the particle data into two float4 arrays. This will enable coa- tersection, as the particle should collide with the nearest triangle.
lesced accesses and minimize the fillrate of the application. Once you have it, you can adjust the new position and velocity of
the particle using one of the approaches illustrated in Figure 1.
In order to implement the Verlet integration and collision de-
tection, add you implementation in the Integrate kernel in 3.1.3 Removing and Adding Particles
[Link]. This kernel should perform the follow-
ing steps: So far the problem could be very easily parallelized. In order to
introduce a little bit of complexity, we will allow the particles to
• Load the particle data die and be reborn. After you account for the collisions, you should
• Perform the Verlet integration decrease the age of the particle and check if it is less or equal to
zero. If yes, the particle should be removed. In order to mark the
• Check for collisions particle as dead, use the gAlive buffer. Each record in this array
maps to exactly one particle (as in the case of the gPosAge and Particle data
gVelMass that store the position and the age, and the velocity and
the mass).
Flag array
We will also allow new particles to be born. However, we do not 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
want to just regenerate the dead particles. We want the new parti-
cles to be born whenever some criterion is met. For instance, we Parallel Prefix Sum
want to generate a new particle whenever another particle exceeds
some velocity. In such case, the fast particle will be virtually split Rank array
into two slower particles. Another example of generating newborns 0 1 1 2 3 3 4 5 6 7 8 8 9 10 11 1213131313131313131414141414141414
can be when a particle strongly bounces into an obstacle. The cri-
terion for splitting the particles is left up to you. The important fact
Compaction
is that each current particle can possibly generate a new particle.
Therefore, the arrays for holding the particle data are initialized to
have double size. If a particle k generates a new particle, the new- 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
born will be stored at position N + k, where N is the number of
0 1 1 2 3 3 4 5 6 7 8 8 9 10 11 1213131313131313131414141414141414
particles. Figure 2 shows an example, where three particles die and
one particle is split into two.
[...]
If you run the simulation at this point, the cloth should perform a Figure 9: Construction of triangles and normals during the recom-
realistic waving motion according to the applied forces, but keeping putation of per-vertex normals.
the distances among particles by satisfying the cloth constraints.
4.5 Evaluation