posts - 15, comments - 11, trackbacks - 0, articles - 1
   :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

2010年1月14日

在超大型场景下,远近裁剪平面一半要很远的距离,使用普通的深度缓冲经常会因为误差,造成一些很难看的锯齿,本来不相交的图元相互穿透,Z-fighting等现象。

普通的Z/W深度缓冲,深度值不是线性的。在近处,深度比较精确,所使用的深度区间也比较大。在远处,仅会使用很小一部分深度区间,误差就会变得很大,产生难看的现象。

例如,在近裁剪平面为10,远裁剪平面为10000时:

Z Z/W深度
90.09009 0.900901
490.4905 0.980981
990.991 0.990991
10000 1.0

可见近处的所用到的区间大,90以内的就用了0-0.9,越远越小,精度越低。

 

一种办法就是将深度转化为线性的,在D3D中用Shader自己算一个深度。

由于Z最终会被除以W,所以,可以在VS中,完成WorldViewProj之后,先对Z上乘以W,等最后被除以W的时候,就相当于什么事情也没有发生。

当然,也要除以远裁剪平面距离。使深度值映射到0-1之间。也可以通过修改投影矩阵来完成。

线性的深度分布,会使远处和近处的精度一样。即传说中的W缓冲。在超大场景下,远处就没有那么多锯齿了。但是,这样有时候,近处的精度就会不足。

还有一种办法就是在VS中用对数计算深度。

公式z = log(C*z + 1) / log(C*Far + 1) * w

其中,C是常量,不同的值,会影响深度的精度。

在z处的分辨率为

log(C*Far + 1) /  (2^n * C/(C*z+1))

 

一下是一个参考的精度表,使用24位深度缓冲,远裁剪平面10000km

  1 10 100 1k 10k 100k 1M 10M
C=1 1.9e-6 1.1e-5 9.7e-5 0.001 0.01 0.096 0.96 9.6
C=0.001 0.0005 0.0005 0.0006 0.001 0.006 0.055 0.549 5.49

使用对数深度问题是深度是被线性插值的,而不是对数。所以网格必须足够精确……或者直接在PS中算深度,不是很费性能,如果可以接受的话……

这种办法效果非常好,做真实比例的星球系统没啥问题。

 

 

使用这些非标准的深度,虽然效果好,但也是有坏处的。

首先,普通Z/W深度,在屏幕空间是线性的,如图所示:

depth_grad

这是使用ddx和ddy对Z/W深度求的导数图。

由颜色相同可以看出,深度是线性的(在屏幕空间)。原始图片为无损png,可以用画图颜料桶测试颜色是否相同。

 

 

对线性深度使用相同办法,效果如图。

depth_grad_nonlinear

可见是非线性的。

这对于一些基于屏幕空间深度的Post Effect来说不利。例如SSAO,HDAO,景深等。

posted @ 2010-01-14 03:02 Yuri 阅读(18) | 评论 (0)编辑

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 阅读(497) | 评论 (1)编辑

2007年7月28日

http://yuri.cnblogs.com/

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

纯属无聊摆的……演示里没有用broad-phase碰撞检测(因为以后打算加上GraphicEngine的场景树),直接mid-phase
下载地址:http://files.cnblogs.com/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 阅读(1144) | 评论 (5)编辑

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 阅读(724) | 评论 (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 阅读(165) | 评论 (0)编辑

2007年7月5日

     摘要: 假设:你已经熟悉单点带摩擦冲量问题。  对于有多对碰撞点的碰撞处理工作如下。  首先,根据牛顿碰撞定律将每对碰撞点的碰后相对速度算出来 。然后碰撞冲量将会在一个迭代循环中计算出来。在第i次迭代时,对于每一组碰撞点,我们将测试碰前相对速度 是否已经达到了之前求出的相应的碰后相对速度 。如果是,我们就直接处理下一对碰撞点。否则就按老样子计算冲量 ,并且作用在刚体上。直到所有对碰撞点都不需要计算。另外,...  阅读全文

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

2007年7月3日

     摘要: 后续安排:基本物体之间的碰撞检测&处理(柔体除外)完善接触处理BallJoint、StickJoint等刚体破碎 EDIT:弄了个更复杂的测试三角形数约:4300动摩擦系数=静摩擦系数=0.5恢复系数=0.5结论:最终碰撞 & 剪枝 仍需完善。尤其是最终碰撞——球和三角形的求交比较麻烦,我没考虑和边碰撞。由此造成:三角形数越多越好(减少没检测出的可能性),但也别太多。AABB更高效...  阅读全文

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

2007年7月1日

     摘要: 非标准物理重心,以前每次都是简化了模型,让物理重心和几何重心一样,这次一般化,物理重心由以下式子可算出演示视频中的小球的重心靠下(下方密度较大),所以它像个不倒翁一样晃…… 如果一开始产生的水平分速度再大点,那么小球就应该会一快一慢的滚动起来 ,然后滚出地面,掉下去。。  阅读全文

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

2007年6月17日

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