记得在97年我用FoxPro编程完成了中国象棋这个游戏后(现在看来非常幼稚、呆板),指导导师对我大加赞赏,他对我说你可不可以用c改写这个程序,c 对我来说简直一无所知,因为我一直用FP在编程,我几乎用了三个月的时间才完成了界面的设置,不过我很快发现用c可以很方便实现很多新的东西,例如棋子可以成为圆形的了,不象以前是方形的,而且速度可以更快一些,从此我爱上了c,并用它做了很多有趣的东西。古墓丽影这个游戏改变我的编程生涯,我开始接触 3D编程,不过3D编程技术在国内非常落后,从D3D使用开始我查阅了大量的资料,后来我发现运用GL比D3D更轻松,我建议初学者都应从GL开始入手,先了解构建客观世界的基本方法,然后用D3D改写,当然并不是说GL实现不够好,只是在图形的特殊处理上GL比D3D要略差一些。我很高兴GL这个论坛为国内的3D爱好者无私的交流提供了平台,我也想认实一些忠实于3D的朋友,并与你们分享快乐,我也很乐意把我的心得与你们分享,如果你们愿意。我也会无私地提供给你们所耍要的帮助。我也正在写一本书名叫《世界》,它有你们所耍要的东西,它能够帮助你如何去实现一个真实的生动的世界。当然不会出版它,因为是公司要我编的内部学习教材,但我会把它在适当的时候与你分享。如果你有什么困惑联系我吧:QQ89322712 <<世界>>摘录部分(动力学篇之运动学1): 运用计算机摸拟客观世界中物体的运动可以看成是物体空间坐标与时间的对应关系。物体的空间位置变化根据运动学公式:Vt=V0+at和S=Vtt+ at2/2来进行计算,其中Vt为末速度,V0为初速度,a为加速度,t为运动时间,在时间很短的情况下位移公式可以写成:S=Vtt。特别的当a=0 时,物体做匀速直线运动,运动学公式变为:Vt=V0和S=Vtt。在计算机摸拟通过计算物体下一时刻的空间位置,然后不断更新物体的位置坐标来达到动画摸拟的目的。如下图物体位置从初位置A点沿直线匀速运动到下一位置B点: 设两点坐标为A(x0,y0,z0),B(x1,y1,z1)从A点到B点的计算公式为: PositionB=PositionA+DirectionA_B*Speed*Time; (1) 公式中DirectionA_B为运动方向单位向量,Speed为运动速度大小,Time为运动时间。 如果物体以一定的加速度从A点沿直线运动到B点则计算公式为:
PositiomB=PositionA+DirectionA_B*Speed*Time+DirectionA_B*Acceleration*Time*Time*0.5 (2) 公式中Acceleration为物体沿A-B方向的加速大小; 当然对于一般曲线运动如平抛运动等,只需要定义加速度的方向单位向量和加速度的数值大小。对公式2进行修正如下: PositiomB=PositionA+Direction_Speed*Speed*Time+Direction_ Acceleration* Acceleration*Time*Time*0.5 (3) 公式中Direction_Speed为速度方向单位向量,Speed为速度大小,Direction_ Acceleration为加速度方向单位向量,Acceleration为加速度大小。
C语言调用Opengl API例程:摸拟平抛运动 GLUquadricObj *ball;//定义二次几何体 float time1;//时间变量,在初始化数据中赋值为0; //定义坐标结构 struct vector { float x;//向量方向坐标 float y; float z; float value;//向量大小值 } //定义单位向量 vector unitVector(vector vectorDirection) { float unit_length; unit_length=sqrt(vectorDirection.x*vectorDirection.x+vectorDirection.y* vectorDirection.y+ vectorDirection.z* vectorDirection.z); vectorDirection.x/= unit_length; vectorDirection.y/= unit_length; vectorDirection.z/= unit_length; return vectorDirection; } vector positionB;//下一位置坐标 void DrawBall(); { vector positionA={-4,4,0}; vector Direction_Speed={1,0,0};//速度方向沿X轴正方向 vector Direction_Acceleration={0,-1,0};//加速度方向沿Y轴负方向
float Speed=2.0f;//初速度大小 float Acceleration=0.68f;//加速度大小 Direction_Speed= unitVector (Direction_Speed);//速度矢量单位化 Direction_Acceleration= unitVector (Direction_Acceleration);//加速度矢量单位化 positionB.x=positionA.x+Direction_Speed.x*Speed*time1+0.5*Direction_Acceleration.x*Acceleration*time1*time1; positionB.y=positionA.y+Direction_Speed.y*Speed*time1+0.5*Direction_Acceleration.y*Acceleration*time1*time1; positionB.z=positionA.z+Direction_Speed.z*Speed*time1+0.5*Direction_Acceleration.z*Acceleration*time1*time1; glTranslatef(positionB.x,positionB.y,positionB.z);//更新到下一个位置 ball=gluNewQuadric();//得到二次物体 gluQuadricDrawStyle(ball,GLU_LINE);//采用线框方式画物体 gluSphere(ball,0.3f,20,20);//画球体 time1+=0.02f;//时间累记 } 在上面的例程中可以更改速度和加速度的方向坐标,得到各种直线或曲线运动。当然在实际的生活中物体的受力情况相当复杂,现在对这一情况作一个简单的分析: 假设物体受到n个恒定不变的力F1、F2、….Fn,当然还有变化的力存在如:空气阻力等。我们先不讨论变力的存在,按照牛顿第二定律:F=ma先计算出合力的大小和方向矢量就可以得到加速度的大小和方向矢量。实现方法如下: int n;//力的个数 vector Force_F[n];//定义n个力 vector ResultantForce;//定义合力 for(int i=0;i<n;i++) { ResultantForce= ResultantForce+Force_F[n]; } 如果存在变力,可按照运动的独立性原理把力按照时间片段进行处理。 二、物体碰撞摸拟 客观世界中物体的碰撞是相当复杂的,首先第一步就是要判断物体间是否会发生碰撞,如果发生,则进一步分析碰撞点,再根据能量损失的情况作出碰撞后物体的运动处理。现实生活中光线的反射给与了我们很好的启示:(如下图) 1表示入射光线,2表示反射光线, N表示平面法线。 按照反射定律:入射光与反射光关于 平面法线对称。 现在我们判断入射光与平面是否存在交点。 假定入射光的方向向量为N0,光路上一点坐标为P0,平面法线向量为N,平面上一点坐标为P。因此: 入射光线方程为:P(t)=P0+N0*t (1) 平面方程为:p*N=D (2) 光线与平面不相交无处乎存在两种情况: 是入射向量与平面相平行,即:N0*N=0; 是入射向量与平面法线向量方向相反,即:N0*N<0; 如果上述两种情况均不存在,则入射光线必与平面有交点,现在来求交点坐标: 把(1)式代入(2)式可得到:(P0+N0*t)*N=D (3) 解出时间t=(D-P0*N)/N0*N (4) 把(4)式代入(1)式可得到交点坐标: P(t)=P0+(P*N-P0*N)*N0/N0*N (5) C语言实现算法如下: vector RayVectorN0;//入射光线方向向量 vector RayPositionP0;//入射光初位置坐标 vector PlanNormalN;//平面法线向量 vector PlanPointP;//平面上一点坐标 //计算交点坐标并反回交点坐标 vector GetIntersectionPoint(vector P0,vector N0,vector P,vector N) { vector Pt; float PdotN_P0dotN; float N0_dot_N; PdotN_P0dotN =(P.x*N.x+P.y*N.y+P.z*N.z)-(P0.x*N.x+P0.y*N.y+P0.z*N.z); N0_dot_N=N0.x*N.x+N0.y*N.y+N0.z*N.z; Pt.x=P0.x+ PdotN_P0dotN / N0_Dot_N*N0.x; Pt.y=P0.y+ PdotN_P0dotN / N0_Dot_N*N0.y; Pt.z=P0.z+ PdotN_P0dotN / N0_Dot_N*N0.z; return Pt; } 现在已计算出入射光线与平面的交点,根据反射定律我们知道反射光线与入射光线关于平面交点法线对称,现在我们来计算反射光线向量方向。由下图我们可以看到把入射光线向量分解到平面方向为N1,法线反方向为N2。 现在反射光线向量就等于N1与N2的反向向 量的矢量和:P2=N1+(-N2); N2= (N0*N)*N N1=N0-N2; 所以:P2= N0-2(N0*N)*N; 根据上面的经验,如果一个球体以一定的速度碰到平面并发生完全弹性碰撞,则该球体反弹 速度方向满足光线反射定律,如果碰撞中有能的损失,则在N1、N2方向的速度分量乘上衰 减系数。 现在我们来讨论刚性球体与平面发生完全弹性碰撞的情况。 球体沿N0方向运动,当球体运动到P1位置时,球体与平面刚好发生接触,设接触点为Q1, 球体从P0到P1的时间与球体上的点Q0到Q1位置的时间是相等的。现在我们来计算点Q0到Q1位置的时间: 沿速度方向的运动方程:Q(t)=Q0+N0*t; 平面方程:Q*N=D 由上面的两个方程可知: (Q0+N0*t)*N=D 解得时间t=(D-Q0*N)/N0*N; 因为P0坐标是已知的,可以解出Q0坐标:Q0=P0-r*N;代入上式得到 t=(D-(P0-r*N)*N)/N0*N=(D-P0*N+r)/N0*N; 现在就已得到了球体与平面在Q1点发生碰撞的时间,根据计算的时间我们可以预计球体与平面是否发生碰撞。当然要注意如果速度方向与平面平行,碰撞是不会发生的,同时如果解出的时间为负值表明球体远离平面,也不会发生碰撞。下面我们来分析实现这种检测方法的步骤:(略) 碰撞实例3:
|