WADS
WADS
Dan Toškan,
March 2020
c 2020
Project Borealis
ALL RIGHTS RESERVED
ii
ABSTRACT
Fluid simulation has been around for a long time, but has mostly been absent from video
games. The fundamental problem keeping this from happening is the fact that fluids
(and their close relatives, gases) cannot be simulated accurately without splitting them
up into very tiny voxels (3D pixels) of space, and using some behavior observed in the
real world, to accurately predict what will happen next with the fluid or gas - like how
will it flow around an obstacle, and how much force it acts with. But games don’t need
to be hyper-realistic simulations, and as such don’t need the level of detail provided
by these methods. Attempting less intensive approaches (by, for example, increasing
the size of the individual voxels), substantially reduces the accuracy and gives unnatural
results. Simulations of this detail are a significant overhead to run in real time while also
rendering modern video games, and have therefore rarely been implemented with great
accuracy. There are a number of methods we can use to optimize the voxel approach, but
for our purposes, this was never going to work both efficiently and accurately. Instead,
we make use of the fact that all objects in a video game are neatly defined with triangles
(meshes), which we can leverage to solve our questions: how does airflow, from a given
direction, affect a triangle? With what force is it pushed back? How large is the torque
exerted on it, and around which axis does that torque act? If we answer these questions
for a general triangle in a general orientation - and if we can do that every frame,
for every triangle of each objects mesh - then we have created a real-time model of
air drag. Add in a 3D input for the speed of air at a specified point, and we’ve also
got a wind system. This may sound counterintuitive, but compared to dividing the
space around the triangles into tiny voxels, and running calculations for each of those,
it results in a substantial performance improvement. This paper provides an overview
of how the model predicting drag forces and torques works in Project Borealis, what
shortcuts and optimisations we implemented and why, how we deal with a large number
of both triangles and objects, and methods for avoiding strange edge-cases.
iii
Contents
1 Introduction 2
1.1 Basis of the drag equation . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2 Drag model 3
2.1 The pillow effect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.1 Air speed falloff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Rotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.1 Radial airspeed distribution . . . . . . . . . . . . . . . . . . . . . . 10
2.2.2 Integration axis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.3 Incoming air . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.4 Applying the force . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.5 Tilt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3 Wind system 13
3.1 Trilinear interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Modular control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 Wind gust profile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.4 Mesh-less objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4 Optimization 16
4.1 Air drag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.2 Wind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
5 Conclusion 16
1
1 Introduction
In physics, the amount of drag exerted onto a body is approximated with the drag
equation:
FD = 21 ρv 2 CD A
Here, FD denotes the drag force in the direction of the flow velocity v. The force also
scales with the density of the substance, ρ, the cross-sectional area of the object A,
and a drag coefficient CD , which in this equation is assumed to be constant, varying
depending on the shape of the object and its orientation. In general this acts as a sort of
”estimation multiplier”. For demonstration purposes, the following are drag coefficients
of some common shapes:
Shape [orientation] CD
Ball 0.47
Cube 1.05
Cube at 45◦ 0.8
Cone 0.5
Teardrop 0.04
But a table with all the possible orientations of every possible triangle sadly doesn’t
exist. As such, we need a way of calculating CD based on the shape and position of any
given triangle. This equation is still an important learning opportunity.
T = 21 mv 2
where m is the total mass of the objects, and v is the velocity they are traveling with.
This is the amount of energy they carry along with them. It is also the amount of
energy you would need to stop them. Now if we imagine a plane of some shape oriented
head-first into the wind (or, in equal effect, traveling at some speed head-first), air is
impacting it every unit of time. How much air? Well, if the air is traveling with velocity
v relative to the object, then, in a unit of time t and on a surface of area A, a mass
m = ρAvt of air will hit the surface, if ρ is the density of air. That air carries a kinetic
energy of T = 21 (ρAvt) v 2 . To stop it, we need to exert a force F on it for a period of
time t. The work done by this force is W = F s, where s is the path the force has acted
along. In the reference frame of incoming air, it is not moving anywhere, and the object
is the one moving around, with the opposite velocity −v. Also, due to changing reference
frame, the drag force F felt by the object translates a force of −F in the reference frame
of air. The work done by this force is thus W = −F (−vt) = F vt. So, back in our object
reference frame, to stop the incoming air, the work W done by the drag force will have
to be equal to the kinetic energy of the air.
2
W =T
F vt = 12 (ρAvt) v 2
F = 12 ρv 2 A
Looks familiar? This is the base of the drag equation, comparing it to the original
reveals the stand out CD drag coefficient, which is the thing I earlier named ”estimation
multiplier”. I hope this shows what I meant.
2 Drag model
From the previous example, we see that drag is really just the result of changing the
kinetic energy of incoming air. So to have a general solution for a triangle, we have to
know two things:
• How much air is being affected?
• How much is its kinetic energy changed by?
The first is simple, since we already found that the mass m of affected air is directly
proportional to the area A of the object m = ρAvt. However, this is not simply the
area of the obstacle (the triangle), but the area as seen by incoming air - since the air
doesn’t care about areas that it will not hit. So our surface area A has to be the triangle
projected onto the plane of incoming air (the normal of this plane is the velocity of
air). So, say we have the sides a~l and b~l of our triangle in local mesh space, and the
transform G to world space that gives us G~ al = a~g and Gb~l = b~g respectively, we can
find the projected area of the triangle by simply doing a cross product of the triangle
sides, projected onto the incoming air plane, which has the normal ~n = k~~vvk .
AP = 1
~ ~
− ~n (a~g · ~n)) × bg − ~n bg · ~n
2
(a
~g
Now that we have the exact area the air will encounter in its path, we can focus on the
second question, which is: How much is this triangle changing the kinetic energy by?
Say we change the air velocity by ∆v, that means we change its kinetic energy by
2
∆T = 12 m(v 1
+ ∆v) − 2 mv
2
∆T = 21 m (v + ∆v)2 − v 2
∆T = 12 m 2v∆v + ∆v2
∆T = m∆v v + ∆v 2
We can speculate that if the triangle is facing the air directly, it will completely stop the
air, thereby changing its velocity by ∆v = −v, and if it is parallel to the air flow, it will
not change the air speed and so ∆v = 0. There is no way to exactly calculate this, so
we have to make an assumption which retains physical behavior. We considered several
options here, but eventually, some key assumptions have to be made:
3
• The change in velocity ∆v is directly proportional to the initial velocity v, such
that ∆v = f v, where f is some function. This makes sense, because if nothing else
changes, but the air speed doubles, we will also slow it down twice as much, if the
triangle doesn’t move.
• The function declared above is f = cos φ, where φ is the angle between the incoming
~ , meaning cos φ = ~n · N
air direction ~n and the triangle (surface) normal N ~
While the first point is quite intuitive, then second assumption is a little more difficult to
justify. Besides having the neat property of being quickly calculable, since we have both
~n and N ~ available, it does have some physics backing it up. If you imagine a coordinate
system originating at the triangle’s center, with the x axis being the direction ~n, and
the z axis aligned such that the face normal N ~ lies in the xz plane, cos φ is the x com-
ponent of the surface normal N ~ . Because the surface normal N ~ is the direction in which
impacting air will be deflected towards, it makes sense to use the x component of it as
a velocity change factor, because the x axis also happens to be direction of incoming air
~n, and that is what we used to define this imagined coordinate system.
∆v = v cos φ
Putting all of this to use, and repopulating our general equation that the work done by
our drag force F is equal to the change in kinetic energy of incoming air, we get:
W = ∆T
F s = m∆v v +∆v
2
v cos φ
F vt = ρ (AP vt) v cos φ v + 2
2 cos φ
F = ρAP v cos φ 1 + 2
Keep in mind, that because the surface normal N ~ will (generally) point towards the
direction of incoming air, the value cos φ will always be negative. So no need to worry
about the right parenthesized expression being more than 1. Now the final part is to
interpret the meaning of the result we got. As mentioned above, the surface normal N ~
is the direction in which the triangle deflects air, so this is the direction our drag force
acts in. And neatly, the cos φ factor in the middle of the expression for F , makes sure
the result will always be negative. That’s good, since the drag will push the triangle
inward, in the opposite direction of the surface normal N ~ . The final drag force is thus
F~ = ρAP v cos φ 1 +
2 cos φ
2
~
N
4
2.1 The pillow effect
Now that we have a good way to estimate the drag force on a triangle, we have to
find out where this force will act. If we just applied it to the center point, then things
wound never spin - for example, something like a piece of paper tilted by 45◦ , would just
travel in the direction it is tilted in and never change its orientation, which is completely
unrealistic. If we are going for air drag, and we want more than just linear damping, we
cannot ignore where the drag force should be applied. We call the phenomena of drag
having a non-central acting point, the ”pillow effect”. This is the effect of air that has
hit the leading edge of the triangle, flowing across the length of the triangle, providing
a cushion for the air that hits the trailing edge of the triangle. This is demonstrated in
Figure 1.
As can be seen, the air traveling across the surface causes the rest to swerve elegantly
out of the way of the surface. The force making it swerve is coming from the higher
pressure air that is in contact with the surface, so a drag force per area is still felt by
the obstacle. This seems very complicated to model. We assume the force dF acting on
an area dA is a function of the distance l that it traveled across the surface. The only
thing needed now, is a function f (l) that describes how v changes with l.
Z l
cos φ
F (l) = ρv 2 cos φ 1 + f (l)dA
2 0
Z l
cos φ
F (l) = ρv 2 cos φ 1 + f (l)h(l)dl
2 0
h(l) in the second equation is the thickness of the triangle at distance l along the the
surface.
5
Figure 2: Visual representation of integration
The solid blue line is the direction of incoming air, coming from above the triangle, and
~ . The green
~ = ~v − ~v · N
the dashed blue line is its projection onto the triangle, say vproj
dashed line shows the axis along which we will be integrating. The purple line is the
”thickness” of the triangle at distance l along the integration axis. If we split up the
integration into two parts - before and after the middle point - we can easily define two
functions h1 (l) and h2 (l) that describe the thickness of the triangle. Assuming L is the
length of integration and b is the distance along the integration axis at which the middle
point is reached, we define H as the thickness of the triangle at l = b. In such a setup
we obtain:
h1 (l) = H bl
l−b
h2 (l) = H 1 − L−b
Assuming we come up with an appropriate function f (l) that describes how force changes
along l, we can find the force origin along the green axis by calculating the average
distance along l that drag acts on:
cos φ
R b RL
2
ρv cos φ 1 + 2 0
lf (l)H bl dl
+ b lf (l)H 1 − dl l−b
L−b
lAvg = R
b L
cos φ l l−b
R
2
ρv cos φ 1 + 2 f (l)H b dl + b f (l)H 1 − L−b dl
0
Rb RL
lf (l) bl dl + l−b
lf (l) 1 − L−b
dl
lAvg = R0 b RL
b
f (l) bl dl l−b
0
+ b
f (l) 1 − L−b
dl
6
In a similar way, the average origin along the thickness can be found as well, if we
define c as the projection of the top-left triangle side onto a unit vector ~h that is in the
direction of the triangle thickness h(l) (upward). Also, let d be the projection of the
~ l
bottom triangle side onto h. The midpoint of the thickness is h1m (l) = b c − 2 and H
l−b
d − c + H2 for each integral, respectively.
h2m (l) = d + L−b
cos φ
R b l
RL l−b
ρv 2 cos φ 1 + 2
h
0 1m
(l)f (l)H b
dl + b
h 2m (l)f (l) 1 − L−b
dl
hAvg =
Rb RL
ρv 2 cos φ 1 + cos2 φ l l−b
0
f (l)H b
dl + b
f (l)H 1 − L−b
dl
Rb l H
RL
f (l) bl dl + b d + L−b l−b
d − c + H2 f (l) 1 − l−b
0 b
c− 2 L−b
dl
hAvg = Rb RL
l l−b
0
f (l) b
dl + b
f (l) 1 − L−b
dl
Now we just need a constant definition for f (l), and we are able to analytically find the
~ then
force origin. If the triangle point we started integration at has the coordinates A,
we can express the origin location as
7
The pressure zone in the
q frontresembles a circle, so we use a circular falloff as an
2
approximation: f (l) = 1 − Ll
q
l 2
f (l) = 1− L
f (l) 1
0
0 1
Distance along integration axis [multiple of L]
However, this falloff is not equal for all surfaces or objects in general. To allow for this
function to change with a parameter, we used a simple linear interpolation between it
and the constant function g(l) = 1. The interpolation parameter α is what we refer to
as the ”pillow effect factor”. So to conclude, the function we modulate force with is
q
l 2
f (l) = α 1 − L + (1 − α)
1
f (l)
α=0
α = 0.25
α = 0.5
α=1
0
0 1
Distance along integration axis [multiple of L]
8
Solving the equations for lAvg and hAvg gives us a pretty long polynomial.
Using a substitution
q S
b2
S= 1− L2
Gives us a result for lAvg as
2b3 (p(S − 2) + 2) + bL2 (p(4 − 5S) − 4) + 3L2 p(b − L) sin−1 Lb + 3bL2 p cos−1 b
L
lAvg =
4 b2 (p(S − 3) + 3) + bL(5p − 3) − 3bLp cos−1 Lb + 2L2 p(S − 1)
For hAvg , the result is a little more complicated. But with pre-computing some factors
and using substitution, calculating the result isn’t absurdly difficult.
hA1 = 6L4 pS(H − 2c) + 4b2 L2 (c(−2p(S + 10) + 6πp + 20) + d(p(S − 12π + 48) − 48)
9
2.2 Rotation
Linear movement isn’t enough to achieve realistic behavior, because in some cases, the
surface we want to calculate the drag of is moving edge-first. For example, imagine
a cube rotating around the vertical axis. For each of the faces, their velocity will lie
in the plane of the face, so in our model, we will skip these faces because they’re not
traveling head-first - meaning no drag would be applied to the cube. We are faced with
the problem of modeling drag for a plane rotating around an arbitrary axis. Immediately
we can deduce the axis of rotation is the center of mass of a specific triangle, because
anything else would be counterintuitive and would also be harder to model.
~ × ~r
~v = ω
It should be noted that for a given angle φ between vectors ~a and ~b:
~a × b
= k~ak
~b
sin(φ)
~
The approach used to solve the rotation problem is very similar to the previous solution
for linear velocity drag. In general, there will be an axis we will integrate along, and
a function that will describe how the speed changes with the distance along this axis.
Because the integration axis will start from the center of mass, and we defined ~r as the
offset from the center of mass, the distance along this axis will be k~rk in any case. As we
can tell from the above equation, the size of ~v changes linearly with k~rk and ω0 and has
a multiplicative factor of sin(φ) where φ is the angle between ~r and the axis of rotation
~ . Let’s define a function describing this change as k~v k = f(r) = k~rkω0 sin(φ).
ω
10
2.2.3 Incoming air
Along the axis ~x we selected, the direction of incoming air is constant, given by the
original definition of ~v . Because we know how the size of ~v changes with the distance
along the axis ~x, we can define a infinitesimal force dF felt at location x along the
integration axis.
2 cos φ
dF = ρh(x) v cos φ 1 + 2 dx
Where h(x) is the height of the triangle along axis ~h at distance x along the integration
axis, and φ is the angle between the incoming air ~v and the face normal - which is the
same as the angle φ between the face normal N ~ and the rotation axis ω
~ , due to the way
~v is defined and the axis ~x we chose. Using the size of ~v we found above and (optionally)
adding our falloff function f (x), the differential becomes:
2 2 cos φ
dF = ρh(x) x ω0 cos φ 1 + 2 f (x)dx
The only problem here, is that along the integration axis, h(x) is not a smooth function
(it is not smooth at the midpoint x = b - see Fig.3). To fix this, we may break up the
Rb R0 Rc
integration into three parts, a dx, b dx and 0 dx, where a < 0, c > 0 and a < b < c.
If b is more than zero, we may integrate from the opposite side to obtain a equal.
(R b R0 Rc
R a dx + b dx + 0 dx, if b < 0.
X dx = R −b R 0 R −a
− −c + −b + 0 , if b > 0.
In the case b = 0, any of the two approaches may be used. So, let’s just assume b < 0,
and if it isn’t we can always say a = −c, b = −b and c = −a.
~ = F~ × ~x|x|
M
And because the force F - which is in the direction of the face normal - is always
perpendicular to the lever ~x|x|, we can simplify
~ = ~h
R
M dF1 xdx
R
b RX0 Rc
~ = ~h
M dF1 xdx + dF1 xdx + dF1 xdx
a b 0
Rb
Inserting dF1 (explained below) into the equation, while using h1 (l) in a and h2 (l) in
~.
the other two integrals as functions for h(x) , we obtain a result for M
11
2.2.5 Tilt
Similarly to the linear problem from earlier, we have to consider that the axis we are
integrating along is not necessarily an axis of symmetry for the triangle. By this we
mean more of the surface could be, for example, on the upper half. This would induce
a separate torque M ~ i around the integration axis itself. The amount of torque on this
axis can be found by integrating over it and the mid-point height, expressed earlier as
h1m (l) and h2m (l), but slightly different now that the integration axis starts from the
mass center. h1 (l) and h2 (l) are also slightly different due to this.
If we look back at the definition of dF , we can see it contains the height of the triangle
h(x) , which neatly cancels out with our definition for dF2 .
R
~ i = ~xρω0 2 cos φ 1 + cos φ b 2 3 R0 2 3 Rc 2 3
M 2 a h1m (x) x dx + b h2m (x) x dx + 0 h2m (x) x dx
This equation shows why we can’t directly integrate from b to c - the function within the
integral is an odd function, so the integral of it over the zero point would start to cancel
itself out. This points out another problem: one side of the integration (x > 0) we are
basically calculating drag, since that part of the triangle is rotating into the air. The
other side (x < 0) on the other hand, rotates away from the air, or inward, towards the
center of the body. An important assumption we’re making here is that the force felt by
this inward-rotating part in the form of a pressure differential is equal to conventional
drag at the same speeds. However, because the force on this inward-rotating part works
in the opposite direction as the one on the other side, we have to negate it.
R
~ i = ~xρω0 2 cos φ 1 + cos φ − b h1m (x)2 x3 dx − 0 h2m (x)2 x3 dx + c h2m (x)2 x3 dx
R R
M 2 a b 0
12
3 Wind system
Now that we have a model for drag that takes an input velocity and applies forces to the
modeled body, we can create a system that supplies part of that input. It makes sense
for this to be distributed through 3D space and not just a top-down map, otherwise we
could forget about any small-scale wind-creating effects (eg. air vents, the whoosh of a
passing vehicle, downdraft from a helicopter, a burst pipe, etc.). The initial plan was to
create a level-scale uniformly distributed grid, with spaces of 0.1m for example. Then
we could add wind actors creating a certain type of disturbance around them - updating
the vectors on the grid around them. This way, it would be relatively easy to extend
the system to support occlusion as well - which would be really cool! However, we could
never drive particle systems this way, since calculations for those are done by the GPU.
The compromise was quite obvious at this point - use whatever the particle systems
are using, and adapt it to get wind samples on the game thread that is running on the
CPU. Luckily, Unreal Engine 4 has Global Vector Fields - actors describing an ordered
collection of uniformly spaced vectors that can be placed in world space and that can
affect particle systems, moving them in the direction of the closest vector (actually the
average of the closest 8). Great! Now how to get that same value when it’s not being
calculated by dedicated GPU functions?
Where N is the number of grid points along an axis. This gives a cube with corners at
indicies
13
~ ~ as
With those values can we find the vector VS
~ yz = V
X ~1 (1 − xd ) + V
~ 2 xd ~Y z = V
X ~3 (1 − xd ) + V
~ 5 xd
~ yZ = V
X ~4 (1 − xd ) + V
~ 6 xd ~Y Z = V
X ~7 (1 − xd ) + V
~ 8 xd
~z = (1 − yd )X
Y ~ yz + yd X
~Y z ~Z = (1 − yd )X
Y ~ yZ + yd X
~Y Z
14
The value of p can be used to dial in the intensity of the low pressure zone.
1.5
1
Intensity
0.5
0
p = 0.4
p = 0.6
−0.5
p = 0.75
p = 0.9
−1
0 0.2 0.4 0.6 0.8 1
Time
15
4 Optimization
With no optimizations, the system struggles with higher-poly objects or large numbers
of objects. It quickly became clear that some large steps needed to be made to improve
performance in such scenarios - no one wants frame rate drops in the midst of battle.
Each system - wind and air drag - has its own solution.
4.2 Wind
The wind system by itself does not require a lot of resources. Its only task is sampling
vector fields and moving, rotating and scaling them as animations dictate. However, if
there are many sample requests and large numbers of vector fields overlapping at sample
locations, the impact is measurable. Again, since one sample is unrelated to another,
the trilinear interpolation tasks are parallelized just like the air drag system.
5 Conclusion
We presented here a wind and air drag system based on simplified fluid dynamics models.
The system produces realistic results in Unreal Engine 4 for use in Project Borealis
without adding a substantial performance overhead.
There are plans in place to improve performance further, which include both software
and hardware level optimizations, as well as adding a model to describe the behaviour
of ellipsoids. This would be used to simulate debris or other small meshed objects, for
which high simulation accuracy isn’t required.
We intend to release the source code of this system, as implemented in Project Bo-
realis, under an open source license at some time in the future, following further feature
development and optimisation.
The Project Borealis team can be contacted with further comments or questions at
[email protected].
16