APEX uses a user-supplied total resource budget, through the NxApexScene function
virtual void setLODResourceBudget(physx::PxF32 totalResource) = 0;
The resource budget has units of milliseconds, as does the cost calculated for actors in the scene.
Budget is allocated depending on the relative “benefit” of various actors in the scene. A common factor for benefit is distance from the render viewpoint, which is calculated using the NxApexScene functions allocViewMatrix, allocProjMatrix, setViewMatrix, setProjMatrix, etc.
This automatic way of assigning LOD to various actors has the drawback that it gives the application programmer less control over how LOD is set for various actors. If the user wishes to have more control, for example just setting various LOD levels for an actor at different distances, then an override function is supplied, the NxApexActor method:
virtual void forcePhysicalLod(physx::PxF32 lod) = 0;
One may query the actor’s current LOD setting using the NxApexActor method:
virtual physx::PxF32 getActivePhysicalLod() = 0;
as well as query the valid LOD range for an actor using the NxApexActor method:
virtual void getPhysicalLodRange(physx::PxF32& min, physx::PxF32& max, bool& intOnly) = 0;
Note that automatic LOD will be used if forcePhysicalLod is called with a negative number,
Each module calculates a “benefit” for the NxApexActors it owns within the scene. Benefit is usually a function of several parameters, and is module-dependent. Benefit does NOT account for level of detail, only the potential benefit that an actor may provide to a scene.
For example, screen area, age, and speed are typical parameters used to calculate benefit. These are used in various “importance functions.” By using commonly defined importance functions in the calculation of benefit, the meaning of LOD weights (multipliers to the importance functions) becomes unified across all modules.
See individual module documentation for the benefit parameters used.
Regardless of the module, the LOD weights should total 1. The importance functions are clamped in the range [0,1], so this keeps the total benefit for each actor “normalized,” so that it’s on the order of one or less. Using this normalization prevents some modules’ actors from hogging the budget by having benefits (and therefore assigned budges) which are orders of magnitude different from others.
Some of the importance functions commonly used by the different modules are described below.
Solid angle on the surface of a unit sphere is the area of the sphere covered. This function only approximates the solid angle, using the rough size of an object described by a radius R. This radius may be the radius of its bounding sphere, for example. If the object is far away, that is its bounding sphere is at distance r from the viewer and r >> R, then from the perspective of the viewer, this bounding sphere covers a solid angle of approximately:
- I(r) = pi*(R^2/r^2), if r^2 > pi*R^2
- = 1, otherwise
As the distance r approaches R, this approximation breaks down. However, it is still useful to use this function, and it’s simple to calculate. Notice that the solid angle importance is capped at the value 1.
In some cases we may wish for the benefit term involving distance to become zero at some finite maximum distance D. One way to accomplish this is to let the importance function take the form
I’(r) = a/r^2 + b.
Using the conditions
I’(R*sqrt(pi)) = 1, I’(D) = 0,
We arrive at the importance function
- I’(r) = 1, if r^2 <= pi*R^2,
- = pi*(R^2/r^2)*(D^2-r2)/(D^2-R^2), if pi*R^2 < r^2 < D^2, = 0, if r^2 >= D^2
Notice that as D increases to infinity, I’(r) becomes I(r), which is required if I’(r) is to be a reasonable extension of I(r).
Age (time t) is normalized by a maximum age T, with importance decreasing linearly. The importance function is clamped in the range [0,1], giving
- A(t) = 1, if t < 0,
- = 1-t/T, if 0 <= t <= T, = 0, if t > T.
For any given LOD, each module calculates a cost in the the scene for each NxApexActor belonging to the module. Units of cost are milliseconds. This is obviously a rough calculation, as the true cost of an actor will depend on many factors including platform, other objects in the simulated environment, number of threads dedicated to PhysX and APEX. The idea is that APEX will not attempt to calculate cost accurately. Rather, it will start with an approximate default budget scale factor, the “LOD unit cost.” This can be queried and set using the NxModule functions
virtual physx::PxF32 getLODUnitCost() const = 0;
and
virtual void setLODUnitCost(physx::PxF32) = 0;
Internally, this number is used to multiply a cost calculation for an actor. This cost calculation may include number of rigid bodies, cloth vertices, number of particles, etc. Ideally the cost calculated per actor is the time in milliseconds spent in computation for that actor.
The user has the chance to measure this and adjust the cost accordingly using the API above.
Cost is obviously a function of an actor’s LOD.
LOD for an actor can be calculated automatically, or set manually. Automatic calculation uses the following scheme. Given the actor’s calculated benefit (see above), a budget is assigned to the actor using a “benefit value” multiplier. This can be queried and set using the NxModule functions
virtual physx::PxF32 getLODBenefitValue() const = 0;
and
virtual void setLODBenefitValue(physx::PxF32) = 0;
Each actor’s benefit is multiplied by this value to obtain a target budget. If the total budget for all modules is less than the total set by setLODResourceBudget (see above), then each module and therefore each actor will receive its target budget. The module will calculate what LOD it may assign to each actor in order to fit its budget.
If the total resource budget is exceeded, however, then the budget allocated to each module is scaled down so that the total assigned to each module fits within the resource budget. This will result in lower LODs being assigned to actors in the scene.
To override this automatic calculation, either use the NxApexActor function
virtual void forcePhysicalLod(physx::PxF32 lod) = 0;
or disable LOD for a module using the NxModule function
virtual void setLODEnabled(bool) = 0;
In the latter case every actor will be set to its highest LOD, unless the forcePhysicalLod function is used to lower an actor’s LOD.
Manual setting of LOD may result in resources being drained from other actors in the scene, or even the total budget for that scene being exceeded by the actor’s cost functions.