Cloth

Introduction

Realistic movement of character clothing greatly improves the player experience. The PhysX 3 cloth feature is a complete and high-performance solution to simulate character clothing. It provides local space simulation for high accuracy and stability, new techniques to reduce stretching, collision against a variety of shapes, as well as particle self-collision and inter-collision to avoid the cloth penetrating itself or other cloth instances. The simulation can be offloaded to CUDA capable GPUs for better performance or to run assets at higher resolutions than the CPU is able to handle.

PhysX 3 cloth is a rewrite of the PhysX 2 deformables, tailored towards simulating character cloth. Softbodies, tearing, two-way interaction, and world collision have been removed, while behavior and performance for cloth simulation have been improved.

Cloth Creation Overview

The cloth constraint connectivity and rest values are stored in a fabric instance (PxClothFabric), separate from the cloth actor (PxCloth). The separation of the constraints from particles allows the same fabric data to be reused for multiple cloth instances, reducing cooking time and storage requirements. PxClothFabricCreate, in the extensions library, creates a fabric from a triangle or quad mesh (see PxClothMeshDesc). The cloth actor itself is created through the physics instance (PxPhysics) and needs to be added to a scene (PxScene) in order to be simulated.

Once the cloth actor is created, users can assign simulation settings such as collision data, constraint stiffness, solver frequency and self-collision. The following sections detail some of the available parameters and show how to configure them.

Filling in PxClothMeshDesc

The first task to create a cloth is to fill in the PxClothMeshDesc structure. Below is an example on how to create a cloth from a simple mesh consisting of a single quad:

PxClothParticle vertices[] = {
    PxClothParticle(PxVec3(0.0f, 0.0f, 0.0f), 0.0f),
    PxClothParticle(PxVec3(0.0f, 1.0f, 0.0f), 1.0f),
    PxClothParticle(PxVec3(1.0f, 0.0f, 0.0f), 1.0f),
    PxClothParticle(PxVec3(1.0f, 1.0f, 0.0f), 1.0f)
};

PxU32 primitives[] = { 0, 1, 3, 2 };

PxClothMeshDesc meshDesc;
meshDesc.points.data = vertices;
meshDesc.points.count = 4;
meshDesc.points.stride = sizeof(PxClothParticle);

meshDesc.invMasses.data = &vertices->invWeight;
meshDesc.invMasses.count = 4;
meshDesc.invMasses.stride = sizeof(PxClothParticle);

meshDesc.quads.data = primitives;
meshDesc.quads.count = 1;
meshDesc.quads.stride = sizeof(PxU32) * 4;

Each particle is defined by its position in local coordinates and its inverse mass. Setting the inverse mass to zero indicates that the particle is not simulated. Instead, the particle is fixed in local space or kinematically constrained to user specified positions. The inverse mass of simulated particles can normally be set to any fixed positive value.

The PxClothMeshDesc structure allows positions and inverse masses stored in separate arrays or interleaved like in the code above. The mesh can consist of quads or triangles, or both. The cooker prefers quad meshes over triangle meshes when creating constraints and classifying constraint types. The extensions library therefore provides the PxClothMeshQuadifier helper class to extract quads from a triangle mesh.

Creating Fabric

Given the mesh descriptor, a call to PxClothFabricCreate in the extensions library wraps the generation of constraints and the creation of the PxClothFabric structure:

PxClothFabric* fabric = PxClothFabricCreate(physics, meshDesc, PxVec3(0, -1, 0));

The third parameter indicates the direction of gravity, which is used as a hint to determine the direction of 'horizontal' or 'vertical' constraints.

The PxClothFabric class describes internal solver data for a cloth. For example, distance constraints consisting of two particle indices and a rest-length are created by the cooker and stored in the fabric data. Multiple cloth instances of the same mesh can share a single fabric instance.

Creating Cloth

A PxCloth object is created using a fabric instance and the initial particle configuration. Like all actors, the cloth instance is simulated as part of a scene:

PxTransform pose = PxTransform(1.0f);
PxCloth* cloth = physics.createCloth(pose, fabric, vertices, PxClothFlags());
scene.addActor(cloth);

The first parameter specifies the initial pose. The second input is the fabric instance created by the cooker. The third input provides initial particle positions and inverse masses. Typically this array is the same as the one referenced by the mesh descriptor used to created the fabric. Note that the rest configuration (such as the rest-length for a distance constraint) is computed from PxClothMeshDesc, so the initial particle positions do not affect rest configuration. The last parameter is a set of flags that allow GPU simulation and continuous collision detection to be enabled. The default is to turn off both options.

Simulation Overview

For one PhysX simulation frame, the cloth solver runs for multiple iterations. The number of iterations is determined by the solver frequency parameter and the simulation frame time. Each iteration integrates particle positions, solves constraints, and performs character and self-collision. Cloth inter-collision is performed once per-frame after all cloth instances in the scene have been stepped forward. Local frame, motion constraints and collision shapes are interpolated per iteration from the per-frame values specified by the user.

Particle Integration

A particle state consists of the current position and the position before the last iteration. The particle velocity in local space can be computed by dividing the position delta by the delta time of the previous iteration. The size of time step is inversely proportional to the number of solver iterations:

cloth.setSolverFrequency(240.0f);

The solver frequency is specified as iterations per second. A solver frequency value of 240 corresponds to 4 iterations per frame at 60 frames per second. In general, simulation will become more accurate if higher solver frequency value is used. However, simulation time grows roughly linearly with solver frequency. Typically this value is between 120 and 300.

The number of iterations for each frame is derived using the simulation frequency and the simulation time-step. PhysX tries to handle variable time-steps carefully, by taking variations of the time-step into account during position integration and when applying damping parameters like constaint stiffness. While this reduces the possible jittering artifacts due to varying time step sizes, use of variable time step size is generally not recommended.

Distance Constraints

One of the most important roles for the cloth solver is to maintain distance between particles so that the cloth does not stretch. This is achieved by applying distance constraints between pairs of particles. The way particles are connected affects how the cloth stretches, compresses, shears, rotates, and bends. PhysX classifies distance constraints into 4 types (see PxClothFabricPhaseType), each of which can be configured with different stiffness parameters.

Note that stretch settings for horizontal and vertical directions are specified separately. This can be used to handle stretching along the gravity (vertical) direction differently.

Below is an example of stiffness settings for each constraint type:

cloth.setStretchConfig(PxClothFabricPhaseType::eVERTICAL, PxClothStretchConfig(1.0f));
cloth.setStretchConfig(PxClothFabricPhaseType::eHORIZONTAL, PxClothStretchConfig(0.9f));
cloth.setStretchConfig(PxClothFabricPhaseType::eSHEARING, PxClothStretchConfig(0.75f));
cloth.setStretchConfig(PxClothFabricPhaseType::eBENDING, PxClothStretchConfig(0.5f));

Sometimes it is desirable that distance constraints are not enforced rigorously. The stiffness parameter allows only correcting a portion of the edge length residual per iteration, for example to reduce the strength of bending constraints. A separate, lower stiffness can be used for edges that are only moderately stretched or compressed. For example, a dress can be made to stretch when the character is taking large steps, but still behave correctly during pirouettes.

The following code sets up the vertical constraints such that when edges are compressed more than 60% or stretched more than 120%, a stiffness of 0.8 will be used. Otherwise a stiffness of 0.4 = 0.8 * 0.5 will be used:

PxClothStretchConfig stretchConfig;
stretchConfig.stiffness = 0.8f;
stretchConfig.stiffnessMultiplier = 0.5f;
stretchConfig.compressionLimit = 0.6f;
stretchConfig.stretchLimit = 1.2f;
cloth.setStretchConfig(PxClothFabricPhaseType::eVERTICAL, stretchConfig);

Cloth Collision

Each cloth object supports collision with spheres, capsules, planes, convexes (groups of planes) and triangles. By default these shapes are all treated separately from the main PhysX rigid body scene, however collision against other PxScene actors can be enabled using the PxClothFlag::eSCENE_COLLISION.

Collision shapes are specified in local coordinates for the next frame before simulating the scene. An independent and complete collision stage is performed as part of each solver iteration, using shape positions interpolated from the values at the beginning and the end of the frame. Sphere and capsule collision supports continuous collision detection and use an acceleration structure to cull far-away particles early in the collision pipeline. Spheres and capsules are therefore the prefered choice to model the character shape, and convexes and triangles should only be used sparingly.

Spheres are defined as center and radius. Note that the radius is specifically allowed to change from frame to frame.

Capsules are defined by a pair of indices into the spheres array and each sphere may have a different radius thus forming a tapered capsule. Spheres can be shared between multiple capsules, which can be useful for modelling characters (upper and lower leg made up from capsules can share the sphere at the knee). Sharing of spheres also makes the simulation more efficient and robust, so is highly encouraged. The following code illustrates how to add two spheres of different radius and create a tapered capsule between them:

// Two spheres located on the x-axis
PxClothCollisionSphere spheres[2] =
{
    PxClothCollisionSphere( PxVec3(-1.0f, 0.0f, 0.0f), 0.5f  ),
    PxClothCollisionSphere( PxVec3( 1.0f, 0.0f, 0.0f), 0.25f )
};

cloth.setCollisionSpheres(spheres, 2);
cloth.addCollisionCapsule(0, 1);

Planes can be added through PxCloth::addCollisionPlane() method but will not be considered for collision unless they are referenced by a convex shape. For example, the following code shows how to setup a typical upward facing ground plane through the origin:

cloth.addCollisionPlane(PxClothCollisionPlane(PxVec3(0.0f, 1.0f, 0.0f), 0.0f));
cloth.addCollisionConvex(1 << 0); // Convex references the first plane

Planes may be efficiently updated after construction using the PxCloth::setCollisionPlanes() function.

Triangle colliders can be set through PxCloth::setCollisionTriangle() method. Triangles are specified in counter-clockwise winding order. The triangles should form a closed patch near the cloth for consistent collision handling (each particle collides against its closest triangle expanded to an infinite plane).

The order of planes and triangles should remain unchanged (apart from removing them through the PxCloth::removeCollisionPlane/Triangle() method) as their positions are interpolated between simulation frames.

Tether Constraints

The distance constraints are solved only once per iteration without converging completely. The most visible artifact of this approximation is that the cloth becomes stretchy. Increasing solver frequency reduces the stretching, but results in increased simulation time.

PhysX 3.3 introduces tether constraints as a solution to avoid stretchiness under gravity or fast motion. Tether constraints prevent stretching by limiting the distance a particle can move away from their anchor particles. This constraint adds very small amount of computation to the solver, so it is more effective than increasing the number of iterations.

The tether constraints are automatically generated by the cooker when some PxClothMeshDesc::invMasses values are set to zero, telling the cooker that the corresponding particles are non-simulated anchor particles whose positions are provided solely from users. Changing inverse masses after the fabric has been created does not affect which anchor particles are used for the tether constraints.

Local Space Simulation and Inertia Scale

Each PxCloth actor has a transformation that transforms particles from its local space to world space positions. For example:

cloth.setGlobalPose(PxTransform(PxVec3(1.0f, 0.0f, 0.0f));

will change the cloth's world space position to (1,0,0). Now compare that to this function:

cloth.setTargetPose(PxTransform(PxVec3(1.0f, 0.0f, 0.0f));

, which also changes the cloth's position to the same place. So what's different?

PxCloth::setGlobalPose() only moves the cloth, but PxCloth::setTargetPose() also generates acceleration (inertia) due to the position change. The amount by which the local frame acceleration affects the cloth particles can be controlled using an inertia scale, for example to impart half the local frame acceleration to the particles use:

cloth.setInertiaScale(0.5f);

Scaling inertia effects individually per translation and rotation axis is also possible through the family of PxCloth::set*IntertiaScale() methods. Limiting the amount that local frame accelerations affect particles can be especially useful for fast moving characters.

Note that using setGlobalPose() is equivalent to using setTargetPos() when intertia scale is 0. In this case, the cloth does not receive any acceleration due to frame changes.

Motion Constraints

One can fully constrain a point to user specified position with zero inverse mass. However, it is sometimes desirable to constrain a point within small region around the animated (user specified) position. This allows small details to be generated by simulation, while supressing any excessive deviation from the desired position.

Motion constraints lock the movement of each particle inside a sphere. For example, an animation system can sketch the overall movement of a cloth while the fine scale details are handled by the cloth simulation.

PxClothParticleMotionConstraint structure holds the position and radius of the sphere for each particle, and motion constraints can be specified as follows:

PxClothParticleMotionConstraints motionConstraints[] = {
    PxClothParticleMotionConstraints(PxVec3(0.0f, 0.0f, 0.0f), 0.0f),
    PxClothParticleMotionConstraints(PxVec3(0.0f, 1.0f, 0.0f), 1.0f),
    PxClothParticleMotionConstraints(PxVec3(1.0f, 0.0f, 0.0f), 1.0f),
    PxClothParticleMotionConstraints(PxVec3(1.0f, 1.0f, 0.0f), FLT_MAX)
};

cloth.setMotionConstraints(motionConstraints);

If the sphere radius becomes zero or negative, the corresponding particle is locked at the sphere center and the inverse particle mass is set to zero for the next iteration. In the above example, the first particle will fully lock to the constraint position, while the second and third particle will remain within the sphere radius. The last particle will not be constrained.

The motion constraint sphere radius can be globally scaled and biased, for example to transition between simulated and animated states. See PxClothMotionConstraintConfig for details.

Separation Constraints

Separation constraints work exactly the opposite way to the motion constraints, forcing a particle to stay outside of a sphere. When particle movement is moderately constrained by motion constraints (e.g. sleeves around an arm), separation constraints can be used to represent the character's collision shape more accurately than using capsules alone. For example, separation constraints can be placed slighly inside the character by setting the radius to be the distance from the sphere center to the surface of the character.

See PxClothParticleSeperationConstraint and PxCloth::setSeparationConstraints().

Continuous Collision Detection

Besides discrete collision which resolves particles inside shapes at the end of each iteration, continuous collision detection is supported and can be enabled by calling:

// Enable continuous collision detection
cloth.setClothFlag(PxClothFlag::eSWEPT_CONTACT, true);

Continuous collision is around 2x more computationally expensive than discrete collision, but it is necessary to detect collision between fast moving objects. Continuous collision analyzes the trajectory of particles and capsules to determine when a contact occurs. After the first time of contact, the particle is moved with the shape until the end of the iteration.

Note: The SIMD collision path handles sets of 4 particles in parallel. It is therefore advantageous to spatially group cloth particles so that they are likely to collide with the same set of shapes.

Virtual Particles

Virtual particles provide a way of improving cloth collision without increasing the cloth resolution. They are called 'virtual' particles because they only exist during the collision processing stage and do not have their position, velocity or mass explicitly stored like regular particles, they can be thought of as providing additional samples on the collision surface.

A virtual particle is defined by 3 particle indices and an index into a weights table, the weights table defines the barycentric coordinates used to create a virtual particle position from a linear combination of the referenced particles. The following is an example weights table that can be used to create a distribution of 4 virtual particles on a triangle:

static PxVec3 weights[] =
{
    PxVec3(1.0f / 3, 1.0f / 3, 1.0f / 3), // center point
    PxVec3(4.0f / 6, 1.0f / 6, 1.0f / 6), // off-center point
};

During collision processing each virtual particle is tested for discrete collision like a regular particle and the collision impulse is redistributed back to the original particles using reverse interpolation.

The code below shows an example of how to set up the virtual particles from a PxClothMeshDesc:

PxU32 numFaces = meshDesc.triangles.count;
assert(meshDesc.flags & PxMeshFlag::e16_BIT_INDICES);
PxU8* triangles = (PxU8*)meshDesc.triangles.data;

PxU32 indices[] = new PxU32[numFaces*4*4];
for (PxU32 i = 0, *it = indices; i < numFaces; i++)
{
    PxU16* triangle = (PxU16*)triangles;
    PxU32 v0 = triangle[0];
    PxU32 v1 = triangle[1];
    PxU32 v2 = triangle[2];

    // center
    *it++ = v0; *it++ = v1; *it++ = v2; *it++ = 0;

    // off centers
    *it++ = v0; *it++ = v1; *it++ = v2; *it++ = 1;
    *it++ = v1; *it++ = v2; *it++ = v0; *it++ = 1;
    *it++ = v2; *it++ = v0; *it++ = v1; *it++ = 1;

    triangles += meshDesc.triangles.stride;
}

cloth.setVirtualParticles(numFaces*4, indices, 2, weights);
delete[] indices;

Fricton and Mass Scaling

Coloumb friction can be enabled and will be applied for particle and virtual particle collisions by setting a friction coefficient between 0 and 1:

cloth.setFrictionCoefficient(0.5f);

Additionally, there is an option to artificially increase the mass of colliding particles, this temporary increase in mass can help reduce stretching along edges that are being tightly pulled over a collision shape. The effect is determined by the relative normal velocity of the particle and collision shape and a user defined coefficient. A value of 20 is reasonable starting point but users are encouraged to experiment with this value:

cloth.setCollisionMassScale(20.0f);

Self-collision of a Single Cloth Actor

The particles of a cloth actor can collide among themselves. To enable this self-collision behavior, one should set both self-collision distance and self-collision stiffness to non-zero values:

cloth.setSelfCollisionDistance(0.1f);
cloth.setSelfCollisionStiffness(1.0f);

Self-collision distance defines the diameter of a sphere around each particle, and the solver ensures that these spheres do not overlap during simulations. Self-collision stiffness defines how strong the separating impulse should be.

Self-collision distance should be smaller than the smallest distance between two particles in the rest configuration. If the distance is larger, self-collision may violate some distance constraints and result in jittering.

When such a configuration cannot be avoided (e.g. due to irregular input meshes, etc.), one can assign additional rest positions:

cloth.setRestPositions(restPositions);

Collision between two particles is ignored if their rest-positions are closer than the the collision distance. However, a large collision distance and use of rest positions will significantly degrade performance of self-collision, so should be used sparingly.

Self-collision performance for high-resolution cloth instances can be improved by limiting self-collision to a subset of all particles (see PxCloth::setSelfCollisionIndices()).

Inter-collision between Multiple Cloth Actors

Different cloth actors can be made to interact with each other when inter-collision is enabled. The parameters for inter-collision are set for all cloth instances of a scene:

scene.setInterCollisionDistance(0.5f);
scene.setInterCollisionStiffness(1.0f);

The definition of distance and stiffness values are the same as self-collision. Cloth instances that specify a particle subset for self-collision use the same subset for inter-collision.

Using GPU Cloth

Cloth can be simulated on a CUDA enabled GPU, by setting the corresponding flag:

cloth.setClothFlag(PxClothFlag::eGPU, true);

When no CUDA capable GPU is available PhysX will issue a warning, and subsequent simulations will be run on CPU.

To take full advantage of the GPU hardware there should be more cloth instances than streaming multiprocessors (SMs), it is also beneficial to have multiple cloth actors of similar number of vertices so that the GPU can simulate all of them in parallel. This means it is generally better to simulate clothing as multiple instances (e.g. shirts and skirt) rather than grouped into one instance.

GPU PhysX performance is better when the particle data of a cloth can fit in shared memory. The number of particles that fit into shared memory depends on the number of collision shapes, whether continuous collision or self-collisions are enabled, and also on the GPU version. For GPUs supporting SM 2.0 and above, about 2500-2900 particles fit into shared memory. If particles don't fit into shared memory the cloth solver will automatically stream particles through global memory, which incurs some performance cost.