"There's a way to do it better--find it."

-Thomas Edison


Real-Time Flow Map Generation


In Portal 2, flow maps are used to create flow patterns for the water elements in the game (i.e. the poisonous swamps), which have proven to improve gaming experience. By sampling from the flow map, every point on the water surface gets a unique 2D vector for normal perturbation. Although we don’t need high resolution flow maps, it’s impractical to hand-draw a flow map for each level. A better way than hand-drawing would be generating from 3D modeling packages such as Houdini, which is what was done in flow map generation in the Portal 2 game: each level has a static flow map for the pixel shader to sample from. However, these static flow maps do not provide interactions between the players and the water surfaces, nor are they able to handle the situation when new geometries are dynamically added to the level.

A flow map is, by its definition, a texture representation of the velocity field. In our application, the velocity field is updated every timestep by solving the Navier-Stokes equations, flow map texture is generated by blending the colors of four vertices in a quad, whose value is determined by the two dimensions of the corresponding velocity vector.


Combining physically accurate fluid solvers and solid textures is promising method of achieving complex fluid behaviors in computer graphics. The incompressible Navier-Stokes equations has the following form:


The fluid solver used in the application is based on Jos Stam’s Stable Fluids paper. Four key functions are implemented: (a) Source: initialize the velocity field with user input. (b) Diffusion: discretize the diffusion operator and solve the linear system. (c) Projection: divide the velocity field into a divergent part and a divergent free part; subtracting the divergent part of the velocity field and the resulting field is divergent free. (d) Advection: the velocity field is advected by itself; if there is any other field in the simulation (texture coordinate or fluid density), this new field is advected by the velocity field.

As shown in Fgure 1, the velocity field can be rendered as a collection of quads, with the color at each vertex being the two dimensions of the 2D velocity vector, and the third channel 0.

render quads
Figure 1: Render to a quad: the colors at the four vertices are the value of the velocity field

We store the velocity field to a texture which clamps the pixel value in each channel to range [0, 1], and in the fragment program, the sampled colors are normalize to the range [-1, 1]. Without external force, the inital flow should be static, although the normalized vector produces a fluid flow with a constant negative velocity, because a zero vector, (0; 0), in the texture produces a vector of (1; 1) after normalization. The solution to this artifact is to transform the velocity vector by an offset of (0:5; 0:5), which renders the initial flow map to brown (shown in Figure 2).

Figure 2: Initial flow map without disturbence

When adding new objects into the simulation environment, we register the objects and update the boundary conditions in the simulation accordingly. For a regularly shaped object, for instance, a box, the velocity vector closed to the edge of the box is set to change to the opposite direction (Figure 3).

flow map after adding objects
Figure 3: Flow map when adding objects


Our fluid solver is based on the Navier-Stokes equations, so the behavior of the fluid flow is physically accurate (Figure 4).

demo snapshot
Figure 4: Use the flow map for fluid surface normal perturbation: left: flow map; right: simulation environment

download paper download source code