文章目录
前提说明
前提说明:作业不是自己完成的,从网上找的答案,因为刚接触这个,通过这个作业了解下unity以及代码逻辑书写
参考代码:https://github.com/Housz/GAMES103-labs
参考代码逻辑:Games 103 Lecture 04 刚体碰撞检测 - 冰点蓝的文章 - 知乎 https://zhuanlan.zhihu.com/p/465304063
总体概述:
有Rigid_Bunny和Rigid_Bunny_by_Shape_Matching两个脚本文件
Rigid_Bunny:
基于传统的刚体物理模拟方法,采用了冲量和惩罚两种碰撞响应方式。
Rigid_Bunny_by_Shape_Matching:
模拟了一个立体兔子在有初始速度前提下,受重力作用的运动和碰撞行为。
粒子碰撞相应算法(Impulse Method)
刚体碰撞相应算法(Shape Matching)
详细介绍Rigid_Bunny
FixedUpdate()

步骤:
1.受重力因素更新速度
2.受外力因素更新速度
3.计算Inertia惯性张量(公式见上图)
4.计算碰撞(代码中用的Collision Impulse),调用Collision函数
5.更新v和w(角速度)
6.通过动态调整阈值(punish_threshold)来控制速度和角速度的增长速度,以保持运动的稳定性
7.更新位置和方向
void FixedUpdate()
{
if (!launched)
{
return;
}
Mesh mesh = GetComponent<MeshFilter>().mesh;
Vector3[] vertices = mesh.vertices;
// ==============================
// Part I: Update velocities
// ==============================
v += dt * gravity; // add gravity acceleration
v += dt * inv_mass * ext_force; // add acceleration
ext_force = Vector3.zero; // clean external force
// Calculate inertia
Quaternion R = transform.rotation;
Matrix4x4 MR = Matrix4x4.Rotate(transform.rotation);
I = MR * I_ref * MR.transpose;
// ==============================
// Part II: Collision Impulse
// ==============================
Collision(new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0)); // floor
Collision(new Vector3(2, 0, 0), new Vector3(-1, 0, 0)); // wall
//Debug.Log("66666666666");
/*
if (ECOLLISION_TYPE.IMPULSE == collision_type)
{
// Debug.Log("Hello World");
CalTorque();
}
*/
v *= linear_decay; // add velocity decay
w *= angular_decay; // add angular decay
// trick for oscillation
if (v.magnitude < 0.16f)
{
punish_threshold = Mathf.Max(punish_threshold - 0.025f, 0.0f);
}
else
{
if (punish_threshold < 1.0f)
{
punish_threshold = Mathf.Min(punish_threshold + 0.025f, 1.0f);
}
}
if (punish_threshold < 1.0f)
{
v *= punish_threshold;
w *= punish_threshold;
}
// ==============================
// Part III: Update position & orientation
// ==============================
// Update linear status
Vector3 x = transform.position;
x += dt * v;
Quaternion q = new Quaternion(half_dt * w.x, half_dt * w.y, half_dt * w.z, 0.0f) * R;
q = AddQuaternion(R, q).normalized;
// ==============================
// Part IV: Assign to the object
// ==============================
transform.position = x;
transform.rotation = q;
}
Collision函数
步骤:
1.计算哪些点发生碰撞,并求平均
2.计算质心发生旋转后的位置,以及到边界的距离
3.更新速度vi


void Collision(Vector3 P, Vector3 N)
{
Mesh mesh = GetComponent<MeshFilter>().mesh;
Vector3[] vertices = mesh.vertices;
Vector3 pos = transform.position;
Vector3 avgPos = Vector3.zero; // average position
int count = 0; // collision count
float dist = 0.0f;
Quaternion R = transform.rotation;
// test collision
for (int i = 0; i < vertices.Length; i++)
{
Vector3 vert = pos + (R * vertices[i]);
dist = Vector3.Dot(vert - P, N);
if (dist > collision_epsilon)
{
continue;
}
avgPos += vertices[i];
++count;
}
// check collision
if (count < 1)
{
return;
}
// calculate average position
avgPos /= (float)count;
Vector3 avgRi = R * avgPos;
dist = Vector3.Dot((avgRi + pos) - P, N);
// caculate velocity
Vector3 vi = v + Vector3.Cross(w, avgRi);
switch (collision_type)
{
case ECOLLISION_TYPE.IMPULSE:
Collision_Impulse(dist, ref vi, ref N, ref avgRi);
break;
case ECOLLISION_TYPE.PENALTY:
Collision_Penalty(dist, ref vi, ref N);
break;
default:
break;
}
}
刚体碰撞相应算法(Impulse Method)
步骤:
1.更新每个顶点的位置,检测是否有碰撞
2.碰撞点的速度算出来,计算是否还继续往下掉(往内穿透)?
3.计算
v
i
n
e
w
v_i^{new}
vinew,求法向量和切向量的速度,然后关于摩擦的系数a计算,求新的两个方向的速度,然后加起来(向量的相加)
v
i
n
e
w
v_i^{new}
vinew是不存在的,只是中间变量,实际上应该更新v(线速度)和ω(角速度),用冲量来更新它们
4.计算K,求出冲量j
5.通过冲量来计算v和ω
总结:
因为一个模型的v是质心的速度,vi是模型上某一点的速度。我们不能直接去修改模型上某一点vi的速度,所以要借助j去修改v和w,达到修改vi的目的,求出整个兔子其他点的变化(即修改整体的运动状态)


前半部分用冲量法更新速度
void Collision_Impulse(float dist, ref Vector3 vi, ref Vector3 N, ref Vector3 avgRi)
{
Debug.Log("Hello World");
// Check velocity dir
if (Vector3.Dot(vi, N) >= 0.0f)
{
return;
}
// -------------------------
// compute the wanted Vi new
// -------------------------
Vector3 vn = Vector3.Dot(vi, N) * N;
Vector3 vt = vi - vn;
float a = 0.0f;
if (0.0f != vt.magnitude)
{
a = 1.0f - mu_t * (1.0f + restitution) * vn.magnitude / vt.magnitude;
a = Mathf.Max(a, 0.0f);
}
// chagne velocity
Vector3 vn_new = -restitution * vn;
Vector3 vt_new = a * vt;
Vector3 vi_new = vn_new + vt_new;
// -------------------------
// compute the impulse j
// -------------------------
Matrix4x4 Ri = Get_Cross_Matrix(avgRi);
Matrix4x4 inv_m = MulMatrix4x4(Matrix4x4.identity, inv_mass);
Matrix4x4 rir = Ri * I.inverse * Ri;
Matrix4x4 K = SubMatrix4x4(ref inv_m, ref rir);
J = K.inverse * (vi_new - vi);
v += inv_mass * J;
Vector3 dw = I.inverse * Ri * J;
w += dw;
// -------------------------
// change position
// -------------------------
Vector3 pos = transform.position;
pos -= (dist - collision_epsilon) * N;
transform.position = pos;
}
Penalty Method(版本3 Log-Barrier)

void Collision_Penalty(float dist, ref Vector3 vi, ref Vector3 N)
{
Debug.Log("Hello World2");
Vector3 vn = Vector3.Dot(vi, N) * N;
Vector3 vt = vi - vn;
v = -restitution * vn + mu_t * vt;
float diff = dist - collision_epsilon;
// Quadratic Penalty Method
if (log_barrier_flag)
{
if (dist > 0.0f)
{
return;
}
const float penalty_rho = 4.0f; // for Log-Barrier penalty method
float rho = penalty_rho / (diff);
ext_force += rho * mass * N;
return;
}
const float penalty_k = 2000.0f; // for quadratic penalty method
ext_force += penalty_k * diff * mass * N;
}
详细介绍Rigid_Bunny_by_Shape_Matching
Get_Rotation()函数具体实现,暂且不管
粒子碰撞相应算法(Impulse Method)

void Collision(float inv_dt)
{
Collision(new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0)); // floor
Collision(new Vector3(2, 0, 0), new Vector3(-1, 0, 0)); // wall
}
void Collision(Vector3 P, Vector3 N)
{
for (int i = 0; i < Y.Length; i++)
{
float dist = Vector3.Dot(Y[i] - P, N);
if (dist > collision_epsilon)
{
continue;
}
// -------------------------
// compute the wanted vi new
// -------------------------
Vector3 vn = Vector3.Dot(V[i], N) * N;
Vector3 vt = V[i] - vn;
float a = 0.0f;
if (0.0f != vt.magnitude)
{
a = 1.0f - mu_t * (1.0f + restitution) * vn.magnitude / vt.magnitude;
a = Mathf.Max(a, 0.0f);
}
// chagne velocity
Vector3 vn_new = -restitution * vn;
Vector3 vt_new = a * vt;
V[i] = vn_new + vt_new;
// change position
Y[i] -= (dist - collision_epsilon) * N;
}
}
部分2 Shape Matching-刚体碰撞响应的仿真管线

FixedUpdate()函数
void FixedUpdate()
{
if (!launched)
{
return;
}
const float dt = 0.015f;
const float inv_dt = 1.0f / dt;
//Step 1: run a simple particle system.
for (int i = 0; i < V.Length; i++)
{
V[i] = V[i] + dt * gravity; // add gravity acceleration
V[i] *= linear_decay; // add linear_decay
Y[i] = X[i] + dt * V[i];
}
//Step 2: Perform simple particle collision.
Collision(inv_dt);
// Step 3: Use shape matching to get new translation c and
// new rotation R. Update the mesh by c and R.
//Shape Matching (translation)
Vector3 c = Vector3.zero;
for (int i = 0; i < Y.Length; i++)
{
c += Y[i];
}
c /= (float)Y.Length;
Matrix4x4 YCRt = Matrix4x4.zero;
for (int i = 0; i < Q.Length; i++)
{
Vector3 d = Y[i] - c;
YCRt[0, 0] += d[0] * Q[i][0];
YCRt[0, 1] += d[0] * Q[i][1];
YCRt[0, 2] += d[0] * Q[i][2];
YCRt[1, 0] += d[1] * Q[i][0];
YCRt[1, 1] += d[1] * Q[i][1];
YCRt[1, 2] += d[1] * Q[i][2];
YCRt[2, 0] += d[2] * Q[i][0];
YCRt[2, 1] += d[2] * Q[i][1];
YCRt[2, 2] += d[2] * Q[i][2];
}
YCRt[3, 3] = 1.0f;
Matrix4x4 A = YCRt * QQt.inverse;
//Shape Matching (rotation)
Matrix4x4 R = Get_Rotation(A);
Update_Mesh(c, R, inv_dt);
}
Update_Mesh函数()
void Update_Mesh(Vector3 c, Matrix4x4 R, float inv_dt)
{
for (int i = 0; i < Q.Length; i++)
{
Vector3 x = (Vector3)(R * Q[i]) + c;
V[i] += (x - Y[i]) * inv_dt;
X[i] = x;
}
Mesh mesh = GetComponent<MeshFilter>().mesh;
mesh.vertices = X;
}

本文详细介绍了Unity中的两个脚本,Rigid_Bunny采用传统刚体物理模拟,涉及冲量和惩罚方法;而Rigid_Bunny_by_Shape_Matching则使用形状匹配算法处理碰撞,包括粒子碰撞响应。着重解析了FixedUpdate中的碰撞检测和响应过程,以及ShapeMatching在更新位置和旋转中的应用。

被折叠的 条评论
为什么被折叠?



