GAMES103——作业1 刚体碰撞

任务

        1.更新位置、姿态与速度

        2.碰撞检测

        3.碰撞反馈

实现

        更新位置、姿态与速度

        对于速度的更新,采用显式的方法,对于位置的更新,采用隐式的方法。就是103中讲的两只青蛙的例子。

         需要同时更新线速度和角速度。线速度受到重力的影响,因此每次更新都要加上重力造成的速度影响,同时还需要乘上速度的衰减量。角速度的更新,也是乘上衰减量。

        位置和姿态的更新。对于位置的更新较简单。

        姿态的更新,涉及到四元数的运算。更新四元数时,和矩阵相乘叠加旋转效果一样,左乘一个四元数。而四元数的求解是通过下面的式子求得。因为计算三角函数性能开销会变大,这里的角度非常小,因此可以通过近似的方法求得近似的q = [1 , Δt/2*w]

 

叠加两次旋转,为 [1 , Δt/2*w] *q ,将[1 , Δt/2*w]分解成 1 + [0 , Δt/2*w],再将q乘进来就得到了103给的式子。这里代码就直接按103的式子实现了

         

			v += dt * gravity;
			v *= linear_decay;
			w *= angular_decay;
	//Update linear status
			Vector3 x    = transform.position + dt * v;
			//Update angular status
			Vector3 dw = w * dt / 2.0f;
			Quaternion qw = new Quaternion(dw.x,dw.y,dw.z,0.0f);
			Quaternion q = transform.rotation;
			Quaternion qw_q = qw *q;
			q = new Quaternion(q.x + qw_q.x , q.y + qw_q.y , q.z + qw_q.z , q.w + qw_q.w);

			// Part IV: Assign to the object
			transform.position = x;
			transform.rotation = q;
        碰撞检测

        先判断点是否已经进入了物体内部,如果在物体内部,再考虑是否还有往内部运动的趋势,说明需要进行碰撞反弹处理。这里较简单。

        当有多个点满足需要碰撞反弹处理时,需要对这些点求一个平均位置,再用平均的位置来进行之后的计算。

MeshFilter meshFilter = GetComponent<MeshFilter>(); 
			Mesh mesh = meshFilter.mesh;
			Vector3[] vertices = mesh.vertices;

			Vector3 sum = new Vector3(0,0,0);
			int cnt = 0;

			Matrix4x4 R = Matrix4x4.Rotate(transform.rotation);
			Vector3 T = transform.position;

			for(int i = 0;i<vertices.Length;i++){
				Vector3 ri = vertices[i];
				Vector3 Rri = R.MultiplyVector(ri);
				Vector3 xi = T + Rri;

				Vector3 dir = xi - P;
				float d = Vector3.Dot(dir,N);
				if(d < 0.0f){
					Vector3 vi = v + Vector3.Cross(w,Rri);
					float viN = Vector3.Dot(vi,N);
					if( viN < 0.0f){
						sum += ri;
						cnt++;
					}
				}
			}	
        碰撞反馈

        因为不能只修改某个点的速度。因此需要通过冲量,连接点的速度和整体的速度。

        碰撞后速度的修改:根据弹性系数和摩擦系数来确定反弹后的速度。注意这里的速度是已经加上了角速度了。对

        对于垂直与平面方向的速度,直接取反并乘以弹性系数就好了。

        对于平行于平面方向的速度,根据库伦摩擦定律 F=μN,水平方向速度的改变量是由摩擦力F造成的。水平方向的摩擦系数为μT,因此有

        ΔvT = dt * F / m

        将摩擦定律代入

        ΔvT = dt * μT * N / m

        N是竖直方向施加的力的大小,而这里 dt * N / m 刚好对应了竖直方向的速度变化量,因此就能得到第一条式子

        ΔvT = μT * ΔvN

        第二条是根据Δv的定义得到的。avT是下一个状态的水平速度,速度差自然是(1-a)|vT|。右边因为竖直方向速度都取反了,因此变化量就是(1+μN)|vN|。最后得到a的表示。

        得到速度变化后,接着就是计算冲量大小了。

        先求出物体于某个姿态时的惯性张量,而下面的推导告诉了我们,可以预计算初始姿态的惯性张量,想要求某个姿态的惯性张量时,再使用R乘入即可。

         

         假设一个冲量j,作用于该点,那么该点的线速度和角速度会发生如下变化。

         向量叉乘时,可以将向量写成矩阵形式

        因此将v都移到左边,右边的将j提取出来,就能得到一个K和j。此时有三个量,分别是Δv,j,K,而我们已知Δv和K,因此能求出冲量j。

        得到冲量j后,将其作用于物体的整理,便能更新物体新的速度。 

 

        具体反馈的流程如下 

         

            Vector3 r_i = sum / cnt;
			Vector3 Rr_i = R.MultiplyVector(r_i);
			Vector3 x_i = T + Rr_i;

			Vector3 v_i = v + Vector3.Cross(w,Rr_i);
			Vector3 v_N_i = Vector3.Dot(v_i,N) * N;
			Vector3 v_T_i = v_i - v_N_i;

			float a = Math.Max(1.0f - u_t * ( 1.0f + u_n) * v_N_i.magnitude / v_T_i.magnitude , 0.0f);

			Vector3 v_N_i_new = -u_n * v_N_i;
			Vector3 v_T_i_new = a * v_T_i;
			Vector3 v_i_new = v_N_i_new + v_T_i_new;


			Matrix4x4 Rr_i_matrix = Get_Cross_Matrix(Rr_i);
			Matrix4x4 I = R * I_ref * Matrix4x4.Transpose(R);
			Matrix4x4 I_inv = Matrix4x4.Inverse(I);

			Matrix4x4 mass_inv = Matrix4x4.identity;
			for(int ii=0;ii<4;ii++){
				for(int jj = 0;jj<4;jj++){
					mass_inv[ii,jj] *= 1.0f / mass;
				}
			}
			Matrix4x4 RIR = Rr_i_matrix * I_inv * Rr_i_matrix;

			Matrix4x4 K =  Matrix4x4.identity;
			for(int ii=0;ii<4;ii++){
				for(int jj=0;jj<4;jj++){
					K[ii,jj] = mass_inv[ii,jj] - RIR[ii,jj];
				}
			}

			Vector3 j = Matrix4x4.Inverse(K) * ( v_i_new - v_i );

			v += 1.0f/mass * j;
			w += I_inv.MultiplyVector(Vector3.Cross(Rr_i,j));
    全部代码

        

using UnityEngine;
using System.Collections;
using System;

public class Rigid_Bunny : MonoBehaviour 
{
	bool launched 		= false;
	float dt 			= 0.015f;
	Vector3 v 			= new Vector3(0, 0, 0);	// velocity
	Vector3 w 			= new Vector3(0, 0, 0);	// angular velocity
	
	float mass;									// mass
	Matrix4x4 I_ref;							// reference inertia

	float linear_decay	= 0.999f;				// for velocity decay
	float angular_decay	= 0.98f;				
	float restitution 	= 0.5f;					// for collision

  Vector3 gravity = new Vector3(0.0f, -9.8f, 0.0f);

	float u_t = 0.2f;	//摩擦系数
	float u_n = 0.5f;	//弹性系数

	// Use this for initialization
	void Start () 
	{		
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		Vector3[] vertices = mesh.vertices;

		float m=1;
		mass=0;
		for (int i=0; i<vertices.Length; i++) 
		{
			mass += m;
			float diag=m*vertices[i].sqrMagnitude;
			I_ref[0, 0]+=diag;
			I_ref[1, 1]+=diag;
			I_ref[2, 2]+=diag;
			I_ref[0, 0]-=m*vertices[i][0]*vertices[i][0];
			I_ref[0, 1]-=m*vertices[i][0]*vertices[i][1];
			I_ref[0, 2]-=m*vertices[i][0]*vertices[i][2];
			I_ref[1, 0]-=m*vertices[i][1]*vertices[i][0];
			I_ref[1, 1]-=m*vertices[i][1]*vertices[i][1];
			I_ref[1, 2]-=m*vertices[i][1]*vertices[i][2];
			I_ref[2, 0]-=m*vertices[i][2]*vertices[i][0];
			I_ref[2, 1]-=m*vertices[i][2]*vertices[i][1];
			I_ref[2, 2]-=m*vertices[i][2]*vertices[i][2];
		}
		I_ref [3, 3] = 1;
	}
	
	Matrix4x4 Get_Cross_Matrix(Vector3 a)
	{
		//Get the cross product matrix of vector a
		Matrix4x4 A = Matrix4x4.zero;
		A [0, 0] = 0; 
		A [0, 1] = -a [2]; 
		A [0, 2] = a [1]; 
		A [1, 0] = a [2]; 
		A [1, 1] = 0; 
		A [1, 2] = -a [0]; 
		A [2, 0] = -a [1]; 
		A [2, 1] = a [0]; 
		A [2, 2] = 0; 
		A [3, 3] = 1;
		return A;
	}

	// In this function, update v and w by the impulse due to the collision with
	//a plane <P, N>
	void Collision_Impulse(Vector3 P, Vector3 N)
	{

		  MeshFilter meshFilter = GetComponent<MeshFilter>(); 
			Mesh mesh = meshFilter.mesh;
			Vector3[] vertices = mesh.vertices;

			Vector3 sum = new Vector3(0,0,0);
			int cnt = 0;

			Matrix4x4 R = Matrix4x4.Rotate(transform.rotation);
			Vector3 T = transform.position;

			for(int i = 0;i<vertices.Length;i++){
				Vector3 ri = vertices[i];
				Vector3 Rri = R.MultiplyVector(ri);
				Vector3 xi = T + Rri;

				Vector3 dir = xi - P;
				float d = Vector3.Dot(dir,N);
				if(d < 0.0f){
					Vector3 vi = v + Vector3.Cross(w,Rri);
					float viN = Vector3.Dot(vi,N);
					if( viN < 0.0f){
						sum += ri;
						cnt++;
					}
				}
			}	

			if(cnt ==0)return;

			Vector3 r_i = sum / cnt;
			Vector3 Rr_i = R.MultiplyVector(r_i);
			Vector3 x_i = T + Rr_i;

			Vector3 v_i = v + Vector3.Cross(w,Rr_i);
			Vector3 v_N_i = Vector3.Dot(v_i,N) * N;
			Vector3 v_T_i = v_i - v_N_i;

			float a = Math.Max(1.0f - u_t * ( 1.0f + u_n) * v_N_i.magnitude / v_T_i.magnitude , 0.0f);

			Vector3 v_N_i_new = -u_n * v_N_i;
			Vector3 v_T_i_new = a * v_T_i;
			Vector3 v_i_new = v_N_i_new + v_T_i_new;


			Matrix4x4 Rr_i_matrix = Get_Cross_Matrix(Rr_i);
			Matrix4x4 I = R * I_ref * Matrix4x4.Transpose(R);
			Matrix4x4 I_inv = Matrix4x4.Inverse(I);

			Matrix4x4 mass_inv = Matrix4x4.identity;
			for(int ii=0;ii<4;ii++){
				for(int jj = 0;jj<4;jj++){
					mass_inv[ii,jj] *= 1.0f / mass;
				}
			}
			Matrix4x4 RIR = Rr_i_matrix * I_inv * Rr_i_matrix;

			Matrix4x4 K =  Matrix4x4.identity;
			for(int ii=0;ii<4;ii++){
				for(int jj=0;jj<4;jj++){
					K[ii,jj] = mass_inv[ii,jj] - RIR[ii,jj];
				}
			}

			Vector3 j = Matrix4x4.Inverse(K) * ( v_i_new - v_i );

			v += 1.0f/mass * j;
			w += I_inv.MultiplyVector(Vector3.Cross(Rr_i,j));
			

	}

	// Update is called once per frame
	void Update () 
	{
		//Game Control
		if(Input.GetKey("r"))
		{
			transform.position = new Vector3 (0, 0.6f, 0);
			restitution = 0.5f;
			launched=false;
		}
		if(Input.GetKey("l"))
		{
			v = new Vector3 (5, 2, 0);
			launched=true;
		}

		if(launched){
			// Part I: Update velocities
			v += dt * gravity;
			v *= linear_decay;
			w *= angular_decay;

			// Part II: Collision Impulse
			Collision_Impulse(new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0));
			Collision_Impulse(new Vector3(2, 0, 0), new Vector3(-1, 0, 0));

			// Part III: Update position & orientation
			//Update linear status
			Vector3 x    = transform.position + dt * v;
			//Update angular status
			Vector3 dw = w * dt / 2.0f;
			Quaternion qw = new Quaternion(dw.x,dw.y,dw.z,0.0f);
			Quaternion q = transform.rotation;
			Quaternion qw_q = qw *q;
			q = new Quaternion(q.x + qw_q.x , q.y + qw_q.y , q.z + qw_q.z , q.w + qw_q.w);

			// Part IV: Assign to the object
			transform.position = x;
			transform.rotation = q;
		}
	}
}

结果

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值