GAMES103——作业1 刚体碰撞

news2024/9/23 21:26:44

任务

        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;
		}
	}
}

结果

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2122100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenGL软光栅化效果图

1.在测试Mesa库画正方形时&#xff0c;看到三角形光栅化过程&#xff0c;分享出来便于理解图形化过程。 2.在此应该和电脑的配置有关系&#xff0c;配置高的话应该可以画的更快。 需要下载的&#xff0c;下面 https://download.csdn.net/download/huzhifei/89734620 。

【机器学习】迁移学习概论

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 迁移学习概论什么是迁移学习?为什么需要迁移学习?迁移学习的应用场景和优势迁…

渲染农场是什么,怎么收费?

什么是渲染农场&#xff1f; 渲染农场是一组连接在一起以完成大型任务的计算机。在 3D 渲染的情况下&#xff0c;渲染农场通常会将动画的帧分发到多台计算机。您可以让 100 台计算机工作 1 天&#xff0c;而不是让一台计算机工作 100 天。 成都渲染101渲染农场如何工作&#…

【JS逆向学习】大学竞争力2021排行榜(md5加密)

逆向目标 网址&#xff1a;https://www.jizhy.com/44/rank/school接口&#xff1a;https://www.jizhy.com/open/sch/rank-list参数&#xff1a; sign 逆向过程 老一套先分析网络请求 经过比对 payload 参数发现&#xff0c;除了 page、ts、sign 三个参数外&#xff0c;其他…

操作系统 --- 进程通信(IPC)

目录 一、进程间的通信&#xff1f; 二、为什么进程间通信需要操作系统支持&#xff1f; 三、进程间通信的方法 3.1 共享存储 3.2 消息传递&#xff08;消息队列&#xff09; 3.2.1 直接通信方式【点名道姓的消息传递】 3.2.2 间接通信方式【以“信箱”作为中间实体进…

leetcode:2710. 移除字符串中的尾随零(python3解法)

难度&#xff1a;简单 给你一个用字符串表示的正整数 num &#xff0c;请你以字符串形式返回不含尾随零的整数 num 。 示例 1&#xff1a; 输入&#xff1a;num "51230100" 输出&#xff1a;"512301" 解释&#xff1a;整数 "51230100" 有 2 个尾…

合宙低功耗4G模组Air780EX——硬件设计手册02

在上文我们介绍了合宙低功耗4G模组Air780EX的主要性能和应用接口&#xff0c; 上文链接&#xff1a;合宙低功耗4G模组Air780EX——硬件设计手册01-CSDN博客 本文我们将继续介绍Air780EX的射频接口&#xff0c;电气特性&#xff0c;实网功耗数据&#xff0c;结构规格等内容。 …

如何解决户用光伏项目管理难题?

户用光伏作为分布式能源的重要组成部分&#xff0c;正迎来前所未有的发展机遇。户用光伏项目的复杂性和多样性也给项目管理带来了诸多挑战&#xff0c;包括客户分散、安装周期长、运维难度大、数据监控不及时等问题。为解决这些难题&#xff0c;构建一套高效、智能的户用光伏业…

降维打击 华为赢麻了

文&#xff5c;琥珀食酒社 作者 | 积溪 真是赢麻了 华为估计都懵了 这辈子还能打这么富裕的仗&#xff1f; 其实在苹果和华为的发布会召开之前 我就知道华为肯定会赢 但我没想到 苹果会这么拉胯 华为这是妥妥的降维打击啊 就说这苹果iPhone 16吧 屏幕是变大了、颜色…

银行用电安全管理难点及解决方案

1引言 科技进步带来丰富的电力资源和更多电气设备&#xff0c;但同时也增加了火灾风险。2020年&#xff0c;中国共发生25.2万起火灾&#xff0c;其中电气原因导致的火灾占33.6%&#xff0c;共8.5万起。电气问题引发的较大火灾占55.4%。线路问题如短路、过负荷和接触不良是主要…

FastAPI 深入学习:利用__call__方法实现动态依赖项

在Python中&#xff0c;__init__ 和 __call__ 是两个特殊的方法&#xff0c;它们在类的上下文中有特定的用途&#xff1a; __init__ 方法&#xff1a; 这是类的初始化方法&#xff0c;当一个实例被创建时&#xff0c;它会被自动调用。它通常用于接收初始化参数并设置实例的初始…

【828华为云征文|华为云Flexus X实例:一键助力中小企业,快速部署个性化网站!】

文章目录 前言搭建自己专属网站准备工作具体操作服务器环境确认进入宝塔软件商店JTBC网站内容管理系统一键部署填写域名放行80端口JTBC安装初始页数据库信息配置管理员信息配置完成安装网站管理后台网站前台 验证后台配置内容前台访问的效果 结语 前言 在云计算盛行的时代&…

宠物空气净化器哪个好?希喂、352、有哈宠物空气净化器测评分享

朋友在三个月前养了一只银渐层&#xff0c;从此进入了幸福的养猫生活&#xff0c;但她最近也跟我说&#xff0c;最近因为猫咪和她男朋友吵架了。 每天下班她男朋友回得都比她早&#xff0c;每次开门看到的就是猫咪的毛发掉得很多&#xff0c;地板上、沙发上甚至厨房里都能看到…

WebGL系列教程四(绘制彩色三角形)

目录 1 前言2 varying变量介绍3 开始绘制3.1 声明顶点着色器3.2 声明片元着色器3.3 创建顶点和颜色的缓冲区3.4 指定变量从缓冲区获取值3.5 效果3.6 varying的内涵3.7 完整代码 4 总结 1 前言 上一篇中我们介绍了如何使用缓冲区来绘制三角形&#xff0c;这一篇我们来讲讲如何给…

《PneumoLLM:利用大型语言模型的力量进行尘肺病诊断》|文献速递--基于深度学习的医学影像病灶分割

Title 题目 PneumoLLM: Harnessing the power of large language model for pneumoconiosis diagnosis 《PneumoLLM&#xff1a;利用大型语言模型的力量进行尘肺病诊断》 01 文献速递介绍 在计算机辅助诊断领域&#xff0c;对医学数据的处理和分析能力至关重要。这不仅有助…

【教师节视频制作】飞机降落飞机机身AE模板修改文字软件生成器教程特效素材【AE模板】

教师节祝福视频制作教程飞机降落飞机机身AE模板修改文字特效广告生成神器素材祝福玩法AE模板工程 怎么如何做的【教师节视频制作】飞机降落飞机机身AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤&#xff1a; 下载AE模板 安装AE软件 把AE模板导入AE软件 …

紫色UI趣味测试小程序源码,包含多种评测

紫色UI趣味测试小程序源码&#xff0c;包含多种评测。 该源码里面包含了多种评测,每一种评测都包含大多小细节。 代码下载

springboot网上租房系统---附源码79833

摘 要 如今&#xff0c;房屋作为人类生活的重要场所&#xff0c;在城市中扮演着至关重要的角色。随着城市化进程的加速和流动人口的增多&#xff0c;房屋租赁产业迎来了巨大的发展机遇。然而&#xff0c;在房屋租赁过程中存在着许多繁琐的手续和信息搜索的问题&#xff0c;需要…

抖音电商商品采集接口api 店铺商品列表sku返回值

如今&#xff0c;抖音已经成为国民最受欢迎的APP。因为抖音的存在&#xff0c;我们的生活开始变得更加有趣&#xff0c;同时&#xff0c;抖音带货&#xff0c;抖音duan等等呼之欲出&#xff0c;越来越多的人开始加入到抖音大战中去。在抖音进行带货或者进行短视频创作&#xff…

骨传导耳机哪个牌子值得买?推荐五款表现出色的骨传导耳机!

随着骨传导耳机技术的不断发展&#xff0c;市场呈现出多元化的趋势&#xff0c;但这也使得消费者在挑选时面临更多挑战&#xff0c;特别是如何避免因选择不当而引发的听力问题。目前市场上&#xff0c;部分由非专业厂商或网红快速推出的产品&#xff0c;因技术积累不足、材料选…