Open busyoGG opened 7 months ago
请问除了射线类的 其他类的比如OBB和OBB的 怎么获得碰撞面的法线或者说碰撞点呢
@Parano111d 请问除了射线类的 其他类的比如OBB和OBB的 怎么获得碰撞面的法线或者说碰撞点呢
AABB之间判断xyz轴中深入距离最小的轴,两个包围盒最大值和最小值相减一下就能得到6个方向的深入距离,判断最小值就好了,这个轴就是碰撞面的法线
OBB之间原理和AABB差不多,每个分离轴都检测深入距离。一个OBB包围另一个的情况就判断里面OBB分离轴投影的边界和外面投影的边界距离最小的一边为深入距离;相交不包围就取投影相交距离最短的为深入距离。深入距离最小的那个分离轴就是碰撞面的法线
如果有出现距离相同的话就自己看怎么处理了
请问有源码吗 刚开始手撸物理 算法不精还有点抽象 用的是您unity碰撞检测的那个demo
---- Replied Message ---- | From | @.> | | Date | 02/19/2024 18:23 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 请问除了射线类的 其他类的比如OBB和OBB的 怎么获得碰撞面的法线或者说碰撞点呢
AABB之间判断xyz轴中深入距离最小的轴,两个包围盒最大值和最小值相减一下就能得到6个方向的深入距离,判断最小值就好了,这个轴就是碰撞面的法线
OBB之间原理和AABB差不多,每个分离轴都检测深入距离。一个OBB包围另一个的情况就判断里面OBB分离轴投影的边界和外面投影的边界距离最小的一边为深入距离;相交不包围就取投影相交距离最短的为深入距离。深入距离最小的那个分离轴就是碰撞面的法线
如果有出现距离相同的话就自己看怎么处理了
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
@Parano111d 请问有源码吗 刚开始手撸物理 算法不精还有点抽象 用的是您unity碰撞检测的那个demo
---- Replied Message ---- | From | @.> | | Date | 02/19/2024 18:23 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 请问除了射线类的 其他类的比如OBB和OBB的 怎么获得碰撞面的法线或者说碰撞点呢
AABB之间判断xyz轴中深入距离最小的轴,两个包围盒最大值和最小值相减一下就能得到6个方向的深入距离,判断最小值就好了,这个轴就是碰撞面的法线
OBB之间原理和AABB差不多,每个分离轴都检测深入距离。一个OBB包围另一个的情况就判断里面OBB分离轴投影的边界和外面投影的边界距离最小的一边为深入距离;相交不包围就取投影相交距离最短的为深入距离。深入距离最小的那个分离轴就是碰撞面的法线
如果有出现距离相同的话就自己看怎么处理了
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
//AABB获取法线
public static Vector3 GetCollideNormal(AABBData data1, AABBData data2, out float len)
{
Vector3 normal = Vector3.zero;
Vector3 len1_2 = data1.max - data2.min;
Vector3 len2_1 = data2.max - data1.min;
float[] depth = new float[6] {
len1_2.x,
len1_2.y,
len1_2.z,
len2_1.x,
len2_1.y,
len2_1.z
};
float min = depth[0];
List<int> index = new List<int>() { 0 };
for (int i = 1; i < depth.Length; i++)
{
if (depth[i] < min)
{
min = depth[i];
if (index.Count > 1)
{
index.Clear();
}
if (index.Count == 0)
{
index.Add(i);
}
else
{
index[0] = i;
}
}
else if (depth[i] == min)
{
index.Add(i);
}
}
len = min;
//判断法线方向
for (int i = 0; i < index.Count; i++)
{
switch (index[i])
{
case 0:
normal.x = -1;
break;
case 1:
normal.y = -1;
break;
case 2:
normal.z = -1;
break;
case 3:
normal.x = 1;
break;
case 4:
normal.y = 1;
break;
case 5:
normal.z = 1;
break;
}
}
return normal.normalized;
}
//OBB获取碰撞法线
//有部分防止重复计算的内容,从其他项目拿来的,看着改吧
public static Vector3 GetCollideNormal(OBBData data1, OBBData data2, out float len)
{
string id = data1.GetHashCode() + "-" + data2.GetHashCode();
Vector3 normal = Vector3.zero;
Vector3[] axes;
_seperatingAxes.TryGetValue(id, out axes);
List<Vector2[]> limitList;
_limitObb.TryGetValue(id, out limitList);
if (axes == null)
{
int len1 = data1.axes.Length;
int len2 = data2.axes.Length;
axes = new Vector3[len1 + len2 + len1 * len2];
int k = 0;
int initJ = len2;
for (int i = 0; i < len1; i++)
{
axes[k++] = data1.axes[i];
for (int j = 0; j < len2; j++)
{
if (initJ > 0)
{
initJ--;
axes[k++] = data2.axes[j];
}
axes[k++] = Vector3.Cross(data1.axes[i], data2.axes[j]);
}
}
_seperatingAxes.Add(id, axes);
}
if (limitList == null || limitList.Count != axes.Length)
{
if (limitList == null)
{
limitList = new List<Vector2[]>();
_limitObb.Add(id, limitList);
}
else
{
limitList.Clear();
}
for (int i = 0; i < axes.Length; i++)
{
Vector2[] limit;
bool isNotInteractive = NotInteractiveOBB(data1.vertexts, data2.vertexts, axes[i], out limit);
if (isNotInteractive)
{
//有一个不相交就退出
len = 0;
return normal;
}
else
{
limitList.Add(limit);
}
}
}
float minOverlap = float.MaxValue;
for (int i = 0; i < limitList.Count; i++)
{
if (axes[i].x == 0 && axes[i].y == 0 && axes[i].z == 0) continue;
Vector2[] limit = limitList[i];
float overlap;
//看别人代码抄过来的
//理论上第一个if和第二个else if应该是一样的,还没合并试过
if (limit[0].y > limit[1].y && limit[0].x < limit[1].x)
{
overlap = Mathf.Min(limit[0].y - limit[1].x, limit[1].y - limit[0].x);
}
else if (limit[1].y > limit[0].y && limit[1].x < limit[0].x)
{
overlap = Mathf.Min(limit[1].y - limit[0].x, limit[0].y - limit[1].x);
}
else
{
overlap = Mathf.Min(limit[0].y, limit[1].y) - Mathf.Max(limit[0].x, limit[1].x);
}
if (overlap >= -0.001)
{
if (overlap < minOverlap)
{
minOverlap = overlap;
normal = axes[i];
}
}
else
{
len = 0;
limitList.Clear();
return normal;
}
}
len = minOverlap / normal.magnitude;
//len = minOverlap;
Vector3 dis = data1.position - data2.position;
float amount = normal.x * dis.x + normal.y * dis.y + normal.z * dis.z;
if (amount < 0)
{
normal = -normal;
}
if (len > 0.5f)
{
ConsoleUtils.Log("超长");
}
return normal.normalized;
}
/// <summary>
/// 计算投影是否不相交
/// </summary>
/// <param name="vertexs1"></param>
/// <param name="vertexs2"></param>
/// <param name="axis"></param>
/// <returns></returns>
public static bool NotInteractiveOBB(Vector3[] vertexs1, Vector3[] vertexs2, Vector3 axis, out Vector2[] limit)
{
limit = new Vector2[2];
//计算OBB包围盒在分离轴上的投影极限值
limit[0] = GetProjectionLimit(vertexs1, axis);
limit[1] = GetProjectionLimit(vertexs2, axis);
//两个包围盒极限值不相交,则不碰撞
bool res = limit[0].x - limit[1].y >= 0.001 || limit[1].x - limit[0].y >= 0.001;
return res;
}
/// <summary>
/// 计算顶点投影极限值
/// </summary>
/// <param name="vertexts"></param>
/// <param name="axis"></param>
/// <returns></returns>
public static Vector2 GetProjectionLimit(Vector3[] vertexts, Vector3 axis)
{
Vector2 result = new Vector2(float.MaxValue, float.MinValue);
for (int i = 0, len = vertexts.Length; i < len; i++)
{
Vector3 vertext = vertexts[i];
float dot = Vector3.Dot(vertext, axis);
result.x = Mathf.Min(dot, result[0]);
result.y = Mathf.Max(dot, result[1]);
}
return result;
}
可以参考一下,不能保证百分百没错
好的!感谢大佬 有空我修改了测试一下
---- Replied Message ---- | From | @.> | | Date | 02/19/2024 18:37 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 请问有源码吗 刚开始手撸物理 算法不精还有点抽象 用的是您unity碰撞检测的那个demo
---- Replied Message ---- | From | @.> | | Date | 02/19/2024 18:23 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 请问除了射线类的 其他类的比如OBB和OBB的 怎么获得碰撞面的法线或者说碰撞点呢
AABB之间判断xyz轴中深入距离最小的轴,两个包围盒最大值和最小值相减一下就能得到6个方向的深入距离,判断最小值就好了,这个轴就是碰撞面的法线
OBB之间原理和AABB差不多,每个分离轴都检测深入距离。一个OBB包围另一个的情况就判断里面OBB分离轴投影的边界和外面投影的边界距离最小的一边为深入距离;相交不包围就取投影相交距离最短的为深入距离。深入距离最小的那个分离轴就是碰撞面的法线
如果有出现距离相同的话就自己看怎么处理了
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
//AABB获取法线publicstatic Vector3 GetCollideNormal(AABBDatadata1,AABBDatadata2,outfloatlen){Vector3normal= Vector3.zero;Vector3len1_2= data1.max - data2.min;Vector3len2_1= data2.max - data1.min;float[]depth=newfloat[6]{
len1_2.x,
len1_2.y,
len1_2.z,
len2_1.x,
len2_1.y,
len2_1.z
};floatmin= depth[0];List
Vector3[]axes;
_seperatingAxes.TryGetValue(id,out axes);List<Vector2[]>limitList;
_limitObb.TryGetValue(id,out limitList);if(axes==null){intlen1= data1.axes.Length;intlen2= data2.axes.Length;axes=new Vector3[len1+len2+len1*len2];intk=0;intinitJ= len2;for(inti=0;i<len1;i++){
axes[k++]= data1.axes[i];for(intj=0;j<len2;j++){if(initJ>0){initJ--;
axes[k++]= data2.axes[j];}
axes[k++]= Vector3.Cross(data1.axes[i], data2.axes[j]);}}
_seperatingAxes.Add(id, axes);}if(limitList==null|| limitList.Count != axes.Length){if(limitList==null){limitList=newList<Vector2[]>();
_limitObb.Add(id, limitList);}else{
limitList.Clear();}for(inti=0;i< axes.Length;i++){
Vector2[]limit;boolisNotInteractive= NotInteractiveOBB(data1.vertexts, data2.vertexts, axes[i],out limit);if(isNotInteractive){//有一个不相交就退出len=0;returnnormal;}else{
limitList.Add(limit);}}}floatminOverlap=float.MaxValue;for(inti=0;i< limitList.Count;i++){if(axes[i].x ==0&& axes[i].y ==0&& axes[i].z ==0)continue;
Vector2[]limit= limitList[i];floatoverlap;//看别人代码抄过来的//理论上第一个if和第二个else if应该是一样的,还没合并试过if(limit[0].y > limit[1].y && limit[0].x < limit[1].x){overlap= Mathf.Min(limit[0].y - limit[1].x, limit[1].y - limit[0].x);}elseif(limit[1].y > limit[0].y && limit[1].x < limit[0].x){overlap= Mathf.Min(limit[1].y - limit[0].x, limit[0].y - limit[1].x);}else{overlap= Mathf.Min(limit[0].y, limit[1].y)- Mathf.Max(limit[0].x, limit[1].x);}if(overlap >= -0.001){if(overlap<minOverlap){minOverlap=overlap;normal= axes[i];}}else{len=0;
limitList.Clear();returnnormal;}}len=minOverlap/ normal.magnitude;//len = minOverlap;Vector3dis= data1.position - data2.position;floatamount= normal.x * dis.x + normal.y * dis.y + normal.z * dis.z;if(amount<0){normal=-normal;}if(len>0.5f){
ConsoleUtils.Log("超长");}return normal.normalized;}/// <summary>/// 计算投影是否不相交/// </summary>/// <param name="vertexs1"></param>/// <param name="vertexs2"></param>/// <param name="axis"></param>/// <returns></returns>publicstaticboolNotInteractiveOBB(Vector3[]vertexs1, Vector3[]vertexs2,Vector3axis,out Vector2[]limit){limit=new Vector2[2];//计算OBB包围盒在分离轴上的投影极限值
limit[0]= GetProjectionLimit(vertexs1, axis);
limit[1]= GetProjectionLimit(vertexs2, axis);//两个包围盒极限值不相交,则不碰撞boolres= limit[0].x - limit[1].y >= 0.001|| limit[1].x - limit[0].y >= 0.001;returnres;}/// <summary>/// 计算顶点投影极限值/// </summary>/// <param name="vertexts"></param>/// <param name="axis"></param>/// <returns></returns>publicstatic Vector2 GetProjectionLimit(Vector3[]vertexts,Vector3axis){Vector2result=new Vector2(float.MaxValue,float.MinValue);for(inti=0,len= vertexts.Length;i<len;i++){Vector3vertext= vertexts[i];floatdot= Vector3.Dot(vertext, axis);
result.x = Mathf.Min(dot, result[0]);
result.y = Mathf.Max(dot, result[1]);}returnresult;}
可以参考一下,不能保证百分百没错
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
在修改并测试了您的代码以后,发现基本是可用的 但是在角色移动碰撞到障碍物的过程中,如果一直往障碍物的方向移动,还是有几率会穿透,出现代码中碰撞深度“超长”的状态,请问有办法处理吗
@Parano111d 在修改并测试了您的代码以后,发现基本是可用的 但是在角色移动碰撞到障碍物的过程中,如果一直往障碍物的方向移动,还是有几率会穿透,出现代码中碰撞深度“超长”的状态,请问有办法处理吗
碰撞之后要设置角色位置到碰撞前的地方,法线和深度就是用来做这个的
假设红色是碰撞时候的包围盒位置,碰撞检测到之后就把包围盒移动到蓝色的位置
对的 我目前就是这样做的 用角色预计位置+=碰撞法线(方向*深度),但还是会有几率穿透
好像发现了点端倪, 当物体A的正面和被碰撞物体B的受碰撞面平行的时候,就可以直接穿过去了 请问这是啥问题啊
@Parano111d 好像发现了点端倪, 当物体A的正面和被碰撞物体B的受碰撞面平行的时候,就可以直接穿过去了 请问这是啥问题啊
改成这样试试,换成碰撞深度在分离轴的占比进行比较
真的可以了!感谢大佬 大佬威武
---- Replied Message ---- | From | @.> | | Date | 02/20/2024 17:43 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 好像发现了点端倪, 当物体A的正面和被碰撞物体B的受碰撞面平行的时候,就可以直接穿过去了 请问这是啥问题啊
改成这样试试,换成碰撞深度在分离轴的占比进行比较
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
抱歉又打扰您了 我在使用这个碰撞法线的过程中 我发现两个碰撞体在紧挨并保持相对静止的时候,碰撞法线的方向会在正负之间来回切换 也就是方法中的碰撞深度len会在正负之间切换 请问这是什么原因
---- Replied Message ---- | From | @.> | | Date | 02/20/2024 17:43 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 好像发现了点端倪, 当物体A的正面和被碰撞物体B的受碰撞面平行的时候,就可以直接穿过去了 请问这是啥问题啊
改成这样试试,换成碰撞深度在分离轴的占比进行比较
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
@Parano111d 抱歉又打扰您了 我在使用这个碰撞法线的过程中 我发现两个碰撞体在紧挨并保持相对静止的时候,碰撞法线的方向会在正负之间来回切换 也就是方法中的碰撞深度len会在正负之间切换 请问这是什么原因
---- Replied Message ---- | From | @.> | | Date | 02/20/2024 17:43 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 好像发现了点端倪, 当物体A的正面和被碰撞物体B的受碰撞面平行的时候,就可以直接穿过去了 请问这是啥问题啊
改成这样试试,换成碰撞深度在分离轴的占比进行比较
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
如果是这样的小数字可能是浮点数的精度问题吧
我这基本上是正常的
具体情况我也不太清楚
好的 那我在看一下是不是浮点的精度问题 感谢了
---- Replied Message ---- | From | @.> | | Date | 02/22/2024 19:05 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 抱歉又打扰您了 我在使用这个碰撞法线的过程中 我发现两个碰撞体在紧挨并保持相对静止的时候,碰撞法线的方向会在正负之间来回切换 也就是方法中的碰撞深度len会在正负之间切换 请问这是什么原因
---- Replied Message ---- | From | @.> | | Date | 02/20/2024 17:43 | | To | @.> | | Cc | Sirui @.>, Mention @.> | | Subject | Re: [busyoGG/busyoGG.github.io] 碰撞检测之OBB - Busyo's Blog (Issue #12) |
@Parano111d 好像发现了点端倪, 当物体A的正面和被碰撞物体B的受碰撞面平行的时候,就可以直接穿过去了 请问这是啥问题啊
改成这样试试,换成碰撞深度在分离轴的占比进行比较
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
如果是这样的小数字可能是浮点数的精度问题吧
我这基本上是正常的
具体情况我也不太清楚
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
https://busyo.buzz/article/3c9cb66ca768/
利用SAT分离轴检测实现OBB检测的原理 - Busyo - Busyo's Blog