Canbitwell Products

Yuri'Blog +[基于冲量物理实时模拟] [碰撞检测]
posts - 12, comments - 8, trackbacks - 0, articles - 1
   :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

2007年11月16日

暑假没事写的,当时一直没发布……


  最近弄下来Xenocode Postbuild 2007的破解版,加密.Net程序集挺好,而且它能把程序编译成x86的,选Suppress ILDASM and other external reflection tools或者Compile managed application to native x86 executable image就行,后者可以让程序脱离.Net Framework。只是,自己程序一旦转为本机代码下的,它运行前就会弹出个消息框说:“This application was created using an evalution version of Xenocode products.” 用其他的功能比如扰乱就没这个问题,但还是觉的编译成x86更安全些。可是这个消息框太讨厌……


  用IDA pro打开postbuild的主程序会发现他的程序里有一个段无法反汇编,我估计这个段是加密了的代码。我估计这些加密的代码就是自己写的程序集的代码。用Postbuild处理自己的.net程序也会产生这样一个段,结构和postbuild的主程序相似。

在这个段后面有另一个段,不管是自己的exe还是postbuild的主程序IDA都能反汇编出一部分代码,而且它们基本相同。这些代码我估计是在加载exe时执行的,它用来解密并启动加密了的原始.Net程序。
所以我想Postbuild应该原先也是.Net的,然后Xenocode Postbuild发布时,自己将自己处理了一遍,就成了现在这个样子。

弹出消息框很可能会直接调用MessageBox这个API函数 ,可以在最后找到:

然后那个消息框的调用就非常好找:

我本以为,这些代码会被复制到postbuild处理出的每一个编译成x86的程序中,所以,我就用UE把这几条指令对应的字节全改成0x90,即nop,
结果不起作用。。。
显然,这就证明了刚才:
 这些代码是postbuild将自己编译成x86而产生的,postbuild自己将自己处理过
 “这些代码我估计是在加载exe时执行的,它用来解密并启动加密了的原始.Net程序。”
所以postbuild处理程序时,以上这些代码是由那个被加密了的段中的程序产生的,这就不好说了。。。。

  不过,用postbuild处理过的程序,倒是可以用这种方法除去那个消息框。
修改有两种方法,要么都换成nop指令,要么把调用堆栈里的hWnd参数换成任意值,只要不是0,也不和别的程序的窗体的Handle相同即可(后者不是很好,但通常不会碰巧对应上一的窗体的Handle,一般Handle的值都较大,设成1应该差不多,这个只需改一个字节,实在不行把那条消息内容改了)

posted @ 2007-11-16 13:19 Yuri 阅读(97) | 评论 (0)编辑

2007年7月28日

http://yuri.cnblogs.com/

  貌似Tri-Mesh之间的CD好像不是很容易实现(尤其是三角形求交),而且时间也不多了,就这样,从头总结一下。另外放个演示程序,由于Tri-Mesh之间的CD未完成,没法放入那些物体,所以只有球和静态物体:

纯属无聊摆的……演示里没有用broad-phase碰撞检测(因为以后打算加上GraphicEngine的场景树),直接mid-phase
下载地址:http://www.cnblogs.com/Files/Yuri/Ball_Tri-mesh.rar
运行需要:.Net Framework 2.0
{
BTW:
抽空做了少部分D3D渲染子系统,暂时够用,这样就能体现图形层的特性了,两套3DAPI随便选(不过D3D好像有点慢)。
用D3D有些怪事,我发现设备一创建,数学运算就有误差了,有的就不正确比如Math.Round。你可以选择不同的渲染子系统渲染,这样你就会发现物理模拟结果也不同-_-b
}
操作:
WASD:前后左右平移
Space、Ctrl:上下平移
QE:翻滚
↑↓→← :转动视角
IJKL:给某个小球前后左右加速度
UM:给某个小球竖直上下加速度
为了改进控制手感,最好将全局恢复系数比率设为0%(选择“物理”,设置Elasity为0)

EDIT:
07.11.26
  大改了一顿物理引擎,加入CollisionShape,并且准备ContactGraph。
  D3D渲染子系统不再基于Microsoft Managed DirectX,而是SlimDX(支持.Net2.0)

好了,言归正传。

Broad-phase碰撞检测

Sweep and Prune:
1.将物体的AABB分离到三个坐标轴上。得到若干个区间。
2.根据区间的终点坐标由小到大排序。
3.逐个遍历排序结果,当遇到一个区间的起始点的时候,就将这个区间放到一个列表中;当遇到一个区间的终点时,就将这个区间从列表中清除。
 当在列表中存在区间,而又遇到一个新区间的起始点时,则遇到的区间与列表中的所有区间重叠。
4.如果一对物体在三个坐标轴上的区间都重叠,那么他们的AABB相叠。

Mid-phase碰撞检测

碰撞检测树就是将要碰撞的网格分离成多个部分,并将这些部分按树的结构组织起来。
我的碰撞检测树就是一BVH树,和标准的AABB碰撞检测树相比,AABB树是2叉树,我的树的节点数是不定的。
两种建树过程如下:

AABB:

1.计算三角形集合的AABB包围体
2.找出一个平面,这平面能最大限度的分割三角形集合(一般在中心即可),这个平面必须与坐标平面平行(因为包围体是AABB)
3.穿过这个平面的三角形按中点分配到平面一侧,如果中点还在平面上,随便分配
4.将分出的这两部分作为子节点继续分割(步骤1),直到分出的部分只有一个三角形

       我的碰撞检测树:
1.计算三角形集合的包围体
2.计算三角形集合的几何重心,将几何重心视为原点,按三个坐标平面将三角形集合分成8部分
3.穿过这个坐标平面的三角形按中点分配到平面一侧,如果中点还在平面上,随便分配
4.如果有的部分中没三角形,那么去掉它。如果只有一部分且这部分包含多个三角形,强行随便的将这部分分成多部分(防止死递归)。
5.将分出的n部分作为子节点继续分割(步骤1),直到分出的部分只有一个三角形
我的树可能不如AABB树平衡,可能性能稍差。

包围体有:

包围球
AABB
OBB
……

注:
 碰撞检测树一般在物体定坐标系下建立出来
 可以根据情况,为不同节点选择不同类型的包围体,使得包围体包围得最紧凑

碰撞检测:
一个几何图形vs树
  按深度优先或广度优先遍历树,对于非最末级节点,判断几何图形是否和节点包围体相交,如果相交那么继续遍历它的子节点。对于最末级节点,还要进行几何图形和最末节点所对应的三角形求交。这样,遍历完一次即可找到所有碰撞。

注:
 1.碰撞检测需检测出碰撞点,碰撞法向量,和刺穿深度
 2.球和三角形碰撞:
  以下过程按顺序执行,一旦相交就return,停止和这个三角形继续检测:
   (1).判断球心与三角面垂直且过三边的平面的位置关系,如果被包围,那么直接判断球心到三角面所在平面距离是否<=半径
   (2).根据(1)的结果,如果球心在哪条边外,那么试着和那条线段求交
   (3).判断球心到三角形某顶点距离<=半径
 3.球和棱、点碰撞,碰撞法向量为那个面在碰撞点的切平面的法向量
 4.通过更精确的包围体测试,会遍历更少节点,但更精确的包围体测试一般会花更多时间。

树vs树
  对于一对节点,判断两个节点的包围体是否相交。如果相交,那么测试它们的子节点包围体是否相交。
如果有一个节点是最末级节点,它对应着一个三角形,另一个是一个非最末级节点,那么问题转化为上面“一个几何图形vs树”。
如果两个节点都是最末级节点,那么进行三角形求交。

注:
 对于刚体,它们会转动,转动了的AABB可以用AABB加上一个刚体变换矩阵表示。也可懒一些,直接把树放入全局坐标系下,更新树。


树的更新:
  如果物体不是刚体,可能会形变,这就需要更新树。一般自下而上的更新树。先根据最末级节点对应的三角形,更新最末级节点。然后一级一级更新上面的节点,使它们的包围体包住子节点。


以后安排(先把想法记下来)
物体破裂 :
将物体网格表示成一张无向图(不用邻接矩阵表示),在压强足够大的地方细化网格,细化出的部分作为图的一部分,然后从压强足够大的几个顶点遍历图。遍历时根据物体物理材质选择图的边(第一条边应选择最和切向量正交的),将其并入断裂线,必要时就细化网格,直到这条线首尾封闭。这样做是因为不同材质的物体,其断裂口有的光滑,有的粗糙,甚至有尖刺。这样应该能生成一条满意的断裂线。然后取出子图,成为分裂出的物体的网格的一部分。不过断口的填充就暂无想法了……

脚本:
设想了一种托管脚本。先写好脚本,经过编译器将其编译为自定的较低级中间语言,或者直接IL代码。运行前加载时,对其进行JIT编译,除了.Net的JIT以外,还有自己的一些,当然肯定是自己先处理后再交给.Net……这样脚本最终就是本机代码,执行速度比解释型的快,而且异常也不用一下一下的考虑。
一开始编译为直接IL代码时,不能直接转为它的字节码,而是自己规定的字节码,但其指令还是一一对应的(就是把IL代码用2进制形式表示了一下)。这主要是因为代码中有一些东西,还不能直接转为标准的IL字节码

最终应该可以构建复杂的物体,比如一个人,由Joint、刚体(骨骼和关节)和柔体(头发和肥膘)组成。他做个什么动作要是碰到了什么倒是自然能模拟出来。不过他的行走,怎么维持重心不好说,我的想法是对已做好的动画解析,将其数值微分,最后算出各部分的作用力……维持重心不管他用那些肌肉,维持重心的力 应该包括在里面……
以前有外国人拟做过类似的东西,第六届全国中小学电脑制作活动上还有即将得一得奖的抄过——botz,一个2D质点弹簧系统模拟程序,它的欧拉法积分不严密(或者说就不是),碰撞检测就和边界检查了一下,但是要注意他的“肌肉”思想,质点弹簧系统组成机器人,然后肌肉伸缩运动,我这个应该和那个在这一点有着类似之处。更接近一点的东西就是Dr.Jan Bender的那些机器人了。
当然稍微遥远的东西别想得太多,毕竟还有不少东西未完成……

posted @ 2007-07-28 23:00 Yuri 阅读(500) | 评论 (4)编辑

2007年7月13日

最近调整碰撞检测,总结出以下:
(1).球和棱碰撞,碰撞法向量不是与棱相接的两个平面的法向量的和的单位向量,而是碰撞点到球心的向量。法向量要先求出,然后才能算碰撞点,法向量可以通过表示棱的向量 和 棱上一点到球心向量 的向量三重积求出。
球和点碰撞法向量好找,就是半径向量。
即:面和棱、点碰撞,碰撞法向量为那个面在碰撞点的切平面的法向量

(2)碰撞检测树可以改进成多种节点相结合的,让它们都实现一个接口ICollisionTreeNode,抽象出来method和property,分割时估算一下顶点线性相关程度,然后再选择子节点类型。

BTW:
发现最基础的问题比如几何体求交,别人都不怎么重视。也许是他们足够NB,不需要注意。一个球和三角形求交,还不是最精确的(边的求交不是直线和二次曲面求交那种,这种一个球和一条线段只能求出一个点),竟然被我弄了好几天才弄出来(+优化)。以后加上任意形状刚体那估计更不好说。
由于建碰撞树时的近似处理,当分割较BT的网格时,一个大的三角形组将会 分不成 多个小三角形组,造成递归至堆栈溢出。所以应该狠一下,精确处理。



至此,球和Tri-mesh的碰撞检测&处理就彻底OK了。接下来是Tri-mesh和Tri-mesh的……另外,最好实现OBB树。
当然,与平面有关的就比较简单,不提了。
球和Tri-mesh碰撞包括:
  球和静态物体的碰撞
  球和任意刚体的碰撞

留图


附:
球和三角形相交检测,以下过程按顺序执行,一旦相交就return,停止和这个三角形继续检测:
(1).判断球心与三角面垂直且过三边的平面的位置关系,如果被包围,那么直接判断球心到三角面所在平面距离是否<=半径
(2).根据(1)的结果,如果球心在哪条边外,那么试着和那条线段求交
(3).判断球心到三角形某顶点距离<=r

球和线段求交:
(1).计算线段包围球,检查求和这个包围球是否相交(好像可以省略判断,速度差不多)
(2).若相交,用一开始提到的向量三重积计算出法向量,用投影长算距离,算碰撞位置。
(3).判断距离是否小于半径 且 碰撞位置在线段包围球内

代码
(注:Intersect是包围球的一个方法,包围球有字段vCentre,dRange,
%为向量积运算符,&为两点距离运算符,^为两点距离平方运算符)
        /// <summary>
        
/// 判断线段是否和包围球相交
        
/// </summary>

        public bool Intersect(Vector vStart, Vector vEnd, out double dist, out Vector n, out Vector pos)
        
{
            Vector v 
= 0.5 * (vStart + vEnd);
            
double r = 0.5 * (vStart & vEnd);

            
if ((v & vCentre) <= (r + dRange))
            
{
                Vector v1 
= (vCentre - vEnd);
                Vector v2 
= (vStart - vEnd);

                n 
= (v2 % v1 % v2).UnitVector;
                dist 
= v1 * n;
                pos 
= vCentre - dist * n;

                
return (dist <= dRange) && ((pos ^ v) < r * r);
            }

            dist 
= 0;
            n 
= new Vector();
            pos 
= new Vector();
            
return false;
        }

posted @ 2007-07-13 23:54 Yuri 阅读(223) | 评论 (0)编辑

2007年7月9日

     摘要: 接触处理的本质是用冲量分离刺穿,而不是接触了就给个支持力。
由于碰撞检测使用的不是连续碰撞检测,所以检测到的时候总会已经发生一些刺穿,碰撞检测会检测出刺穿法向分量和切线分量,然后计算补偿冲量分离刺穿。
如果给个支持力,是没有道理的,碰撞处理给的冲量已经满全满足了它。
{
从式子上可以看出,比如质点和平面碰撞冲量:Pn=-(1+e)*Vrn*m,物体在平面上放着,每次积分后,物体会因重力造成Vrn=g*dt,代入即F=-(1+e)*mg,
取极端情况e=0或1,
最少e=0时,够提供支持力-mg,
如果e=1,物体会受到向上的合力-mg。假设我们的数值积分是理想的,和真的定积分一样,那么dt后,物体会在平面上0.5*g*dt*dt处。然后他又落下来,碰撞接触速度为g*dt,和前面Vrn一样,所以它会不断地这样下去。由于理想化了积分,即dt的d是真的d, :-),所以(0.5*g*dt*dt)→0,所以物体会不动。
现在面对现实,dt也就是个1~5毫秒,质点位置就会处于数值动态平衡中,应该基本看不出来。。。

  阅读全文

posted @ 2007-07-09 23:30 Yuri 阅读(92) | 评论 (0)编辑

2007年7月5日

 假设:你已经熟悉单点带摩擦冲量问题。

  对于有多对碰撞点的碰撞处理工作如下。

  首先,根据牛顿碰撞定律将每对碰撞点的碰后相对速度算出来 。然后碰撞冲量将会在一个迭代循环中计算出来。在第i次迭代时,对于每一组碰撞点,我们将测试碰前相对速度 是否已经达到了之前求出的相应的碰后相对速度 。如果是,我们就直接处理下一对碰撞点。否则就按老样子计算冲量 ,并且作用在刚体上。直到所有对碰撞点都不需要计算。

另外,为了防止碰撞物体相互sticking,必须保证在每次迭代中,每对碰撞点的合冲量必须在正法向量方向,即:

 

如果上式在i次迭代时成立,而在第i-1 次迭代时不成立,那么则需

 

就是说

 

  对了,别忘了摩擦冲量。摩擦冲量是在碰撞法向冲量的基础上产生的,因此,摩擦冲量要和碰撞冲量在同一个迭代循环中求出。它的计算是基于Coulomb摩擦力法则的。如果切线相对速度分量 不是0。我们会计算一个冲量来模拟动摩擦力,方向是切线速度的相反方向,切线向量t我们用法向量n和相对速度的向量三重积算出来。在第i次迭代中,一对碰撞点的摩擦冲量可以由下式算出:


其中 就是动摩擦系数。在我们把这个冲量作用到刚体上碰撞位置前,必须保证切线速度t的相反方向,因为摩擦力是使接触点的相对速度减小的。那么允许的极端情况就是摩擦冲量使切线相对速度为0。即:

 

好了,考虑上 ,即:

 

posted @ 2007-07-05 17:00 Yuri 阅读(202) | 评论 (2)编辑

2007年7月3日


后续安排:
基本物体之间的碰撞检测&处理(柔体除外)
完善接触处理
BallJoint、StickJoint等
刚体破碎

EDIT:
弄了个更复杂的测试


三角形数约:4300
动摩擦系数=静摩擦系数=0.5
恢复系数=0.5

结论:
最终碰撞 & 剪枝 仍需完善。尤其是最终碰撞——球和三角形的求交比较麻烦,我没考虑和边碰撞。由此造成:三角形数越多越好(减少没检测出的可能性),但也别太多。
AABB更高效,尽管AABB的剪枝更复杂(不像包围球考虑个半径就行了),但它剪枝剪得更彻底
我用包围球树时,按物理fps上限为10000的速度跑(物理单独MTA线程),在有些时候——球临近静态物体时会有卡的现象,用AABB树按每秒10000次模拟步没什么问题……由此我们也可以看到效率瓶颈在球和三角形求交上。

posted @ 2007-07-03 03:06 Yuri 阅读(144) | 评论 (0)编辑

2007年7月1日


非标准物理重心,以前每次都是简化了模型,让物理重心和几何重心一样,这次一般化,物理重心由以下式子可算出

演示视频中的小球的重心靠下(下方密度较大),所以它像个不倒翁一样晃……

如果一开始产生的水平分速度再大点,那么小球就应该会一快一慢的滚动起来 ,然后滚出地面,掉下去。。

posted @ 2007-07-01 21:25 Yuri 阅读(110) | 评论 (0)编辑

2007年6月17日

     摘要: 用鼠标选择网格上的三角形。主要步骤: 1. 每次渲染设置摄像机后,用gluUnProject得到拾取射线,然后和每个三角形所在平面求交。 2. 若拾取起点到交点的向量 和 拾取射线向量的数量积大于0(即三角形在观察者前方),则判断是点是否在三角形内。 3. 若在,则 这个三角形被选择。由于要不断求交,实际用的时候应该考虑粗略判断,以便减少求交次数。 代码如下(c#): namespaceRayPi... 阅读全文

posted @ 2007-06-17 23:57 Yuri 阅读(359) | 评论 (0)编辑