Channy's blog

//Description: Physx Fix Own Project

//Create Date: 2022-05-23 18:17:39

//Author: channy

概述

使用和修改Physx应用到自己的项目中。

Debug颜色

PxRigidActor/PxBase 中增加颜色成员变量

// PxRigidActor.h
virtual PxVec3 getColor() { return PxVec3(colorx / 255.f, colory / 255.f, colorz / 255.f); }
virtual void setColor(const PxVec3& _color) { 
	colorx = static_cast<PxU8>(_color.x * 255);
	colory = static_cast<PxU8>(_color.y * 255);
	colorz = static_cast<PxU8>(_color.z * 255); 
}

snippet样例中通过renderActors修改color

减少overlap的pair数量

创建场景 createScene(PGS/TGS)

createScene (NpPhysics) 创建scene NpScene -> NpSceneQueries (create) Scene (create) createDynamicsContext/createTGSDynamicsContext DynamicsContext.create 创建上下文

场景属性 PxSceneDesc

  1. gravity 重力大小和方向,
  2. PxSimulationEventCallback 模拟事件回调,
  3. PxContactModifyCallback 碰撞解算修改回调,
  4. PxCCDContactModifyCallback CCD的碰撞解算修改回调,
  5. filterShader 全局的碰撞分类处理函数,
  6. cpuDispatcher Cpu线程分配器

场景 PxScene

PxScene -> NpScene -> Scene, 其中在构造中创建且后续步骤使用较多的有:

  1. PxsContext* mLLContext 和 Context* mDynamicsContext 上下文
  2. AABBManager* mAABBManager
  3. BroadPhase* mBP 碰撞检测宽阶段算法

Kinematic

Kinematic型的rigidbody质量相当于无穷大,它基本不受力的影响,也基本不参与碰撞解算流程,它的运动是直接设定位置的,而非Kinematic型Rigidbody运动是通过速度,或者力进行计算的,而且碰到东西穿插了会进行复杂的碰撞解算。

Kinematic型由于不受力和碰撞的影响,所以比较适合随着动画运动的碰撞盒,或者由服务器同步运动的碰撞盒,中间不需要额外的计算干扰,比较省性能,也不会出现抖动。

弹簧常数kp和阻尼常数kd

ERP = hkp/(hkp + kd)

CFM = 1/(hkp + kd)

其中h为步长。这些数值将产生与用隐式一阶积分模拟弹簧-阻尼系统相同的效果

多线程任务 Task

采用引用计数方式建立task依赖
A.setContinuation(B)表示A执行完后执行B,set时B.count+1, A结束时B.count-1, B.count = 0时执行B

物理模拟或碰撞计算 simulate

BroadPhase 三种算法

BroadPhase.update 输入参数BroadPhaseUpdateData中包含了add/remove/update的handle索引,毛数据存储于mBoxBounds中,输出BroadPhasePair

约束的不同类型

enum ConstraintType
{
	eCONTACT_CONSTRAINT,	//!< Defines this pair is a contact constraint
	eJOINT_CONSTRAINT		//!< Defines this pair is a joint constraint
};

enum SolverConstraintType

更新动态物体 DynamicsContext.update

PhysX 中的求解器

batch constraints对提供的constraints重新排序并生成 batchHeaders ,solver可以使用这些batchHeaders来加速约束求解(constraint solving),方法是将独立约束组合在一起并使用 SIMD 寄存器中的多个通道并行求解。

PGS vs TGS (Projected/Temporal Gauss-Seidel Solver)

TGS: 速度快

约束求解

//DySolverConstraintsShared.h/solveDynamicContacts

//KS - clamp the maximum force
const FloatV _deltaF = FMax(FNegScaleSub(normalVel, velMultiplier, biasedErr), FNeg(appliedForce));
const FloatV _newForce = FAdd(appliedForce, _deltaF);
const FloatV newForce = FMin(_newForce, maxImpulse);
const FloatV deltaF = FSub(newForce, appliedForce);

第一行是为了保证_deltaF与appliedForce的和总是大于等于0的,即保证了法线方向的总冲量不会有负值。后面三行计算出截断后真正的deltaF。

// DySolverConstraints.cpp/solveContact 

// appliedForce -bias * velMultiplier - a hoisted part of the total impulse computation
const FloatV tmp1 = FNegScaleSub(FSub(bias, targetVel),velMultiplier,appliedForce);				

// Algorithm:
// if abs(appliedForce + deltaF) > maxFrictionImpulse
//    clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse]
//      (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce]
//    set broken flag to true || broken flag

// FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier);
// FloatV potentialSumF = FAdd(appliedForce, deltaF);

const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1);

// On XBox this clamping code uses the vector simple pipe rather than vector float,
// which eliminates a lot of stall cycles

const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse);

const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse));

const FloatV newAppliedForce = FSel(clamp, totalClamped,totalImpulse);

broken = BOr(broken, clamp);

FloatV deltaF = FSub(newAppliedForce, appliedForce);

计算截断的时候要先与静摩擦的最大冲量比,如果比静摩擦的大才截断到动摩擦的。

Solver将contacts分组为friction patches;friction patches是一组contacts,它们共享相同的材料并具有相似的contact normals。但是,Solver允许每个contact manager(一对Shape)最多有 32 个friction patches。如果产生超过 32 个friction patches(这可能是由于非常复杂的碰撞几何Shape或非常大的contact offsets),Solver将忽略剩余的friction patches。发生这种情况时,checked/debug版本中将出现警告。

动态与静态的碰撞

PhysX 中的PGS求解器

//DyContactPrepShared.h/constructContactConstraint

FloatV scaledBias = FMul(velMultiplier, penetrationInvDtPt8);

const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt));

const BoolV ccdSeparationCondition = FIsGrtrOrEq(ccdMaxSeparation, penetration);

scaledBias = FSel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias);

const FloatV sumVRel(vrel);

FloatV targetVelocity = FAdd(cTargetVel, FSel(isGreater2, FMul(FNeg(sumVRel), restitution), zero));

//Note - we add on the initial target velocity
targetVelocity = FSub(targetVelocity, vrel);

const FloatV biasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(scaledBias));
const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FSel(isGreater2, zero, FNeg(FMax(scaledBias, zero))));
//DyContactPrep.cpp/setupFinalizeSolverConstraints

const FloatV velMultiplier = FSel(FIsGrtr(resp, zero), FDiv(p8, resp), zero);

FloatV targetVel = V3Dot(tvel, t0);

const FloatV vrel1 = FAdd(V3Dot(t0, linVel0), V3Dot(raXn, angVel0));
const FloatV vrel2 = FAdd(V3Dot(t0, linVel1), V3Dot(rbXn, angVel1));
const FloatV vrel = FSub(vrel1, vrel2);

targetVel = FSub(targetVel, vrel);

f0->normalXYZ_appliedForceW = V4SetW(t0, zero);
f0->raXnXYZ_velMultiplierW = V4SetW(raXnSqrtInertia, velMultiplier);
f0->rbXnXYZ_biasW = V4SetW(rbXnSqrtInertia, FMul(V3Dot(t0, error), invDt));
FStore(targetVel, &f0->targetVel);

PhysX的LCP求解器

回写结果

PhysX 其它笔记

PxFrictionType 摩擦求解模型

PxAggregate 多个actor合并

自定义约束

PxConstraintConnector 接口,PxConstraintShaderTable函数类

测试场景构建

  1. 高度地图
  2. Part: 7种基于PxGeometry的零件类型
  3. Constraint: 7种基于PxJoint的约束及各种组合约束
  4. Static、Dynamic、Kit

reference

PhysX

PhysX doc