支付网站搭建建筑企业网站设计
web/
2025/10/5 0:14:21/
文章来源:
支付网站搭建,建筑企业网站设计,无锡网站制作中心,多页网站制作转自【翻译】NeHe OpenGL 教程 前言 声明#xff0c;此 NeHe OpenGL教程系列文章由51博客yarin翻译#xff08;2010-08-19#xff09;#xff0c;本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写#xff0c;以及yarn的翻译整理表示感谢。 NeHe OpenGL第三十课… 转自【翻译】NeHe OpenGL 教程 前言 声明此 NeHe OpenGL教程系列文章由51博客yarin翻译2010-08-19本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写以及yarn的翻译整理表示感谢。 NeHe OpenGL第三十课碰撞检测 碰撞检测: 这是一课激动的教程你也许等待它多时了。你将学会碰撞剪裁物理模拟太多的东西慢慢期待吧。 碰撞检测和物理模拟(作者:Dimitrios Christopoulos (christopfhw.gr)) 碰撞检测 这是一个我遇到的最困难的题目,因为它没有一个简单的解决办法.对于每一个程序都有一种检测碰撞的方法.当然这里有一种蛮力,它适用于各种不同的应用,当它非常的费时. 我们将讲述一种算法,它非常的快,简单并易于扩展.下面我们来看看这个算法包含的内容: 1) 碰撞检测 移动的球-平面 移动的球-圆柱 移动的球-移动的球 2) 基于物理的建模 碰撞表示 应用重力加速度 3) 特殊效果 爆炸的表示利用互交叉的公告板形式 声音使用Windows声音库 4) 关于代码 代码被分为以下5个部分 Lesson30.cpp : 主程序代码l Image.cpp, Image.h : 加载图像 Tmatrix.cpp, Tmatrix.h : 矩阵 Tray.cpp, Tray.h : 射线 Tvector.cpp, Tvector.h : 向量 1) 碰撞检测 我们使用射线来完成相关的算法它的定义为: 射线上的点 射线的原点 t * 射线的方向 t 用来描述它距离原点的位置它的范围是[0, 无限远). 现在我们可以使用射线来计算它和平面以及圆柱的交点了。 射线和平面的碰撞检测 平面被描述为 Xn dot X d Xn 是平面的法线. X 是平面上的一个点. d 是平面到原点的距离. 现在我们得到射线和平面的两个方程: PointOnRay Raystart t * Raydirection Xn dot X d 如果他们相交则上诉方程组有解如下所示 Xn dot PointOnRay d (Xn dot Raystart) t * (Xn dot Raydirection) d 解得 t: t (d - Xn dot Raystart) / (Xn dot Raydirection) t代表原点到与平面相交点的参数,把t带回原方程我们会得到与平面的碰撞点.如果Xn*Raydirection0。则说明它与平面平行则将不产生碰撞。如果t为负值则说明交点在射线的相反方向也不会产生碰撞。 //判断是否和平面相交是则返回1否则返回0int TestIntersionPlane(const Plane plane,const TVector position,const TVector direction, double lamda, TVector pNormal){ double DotProductdirection.dot(plane._Normal); double l2; //判断是否平行于平面 if ((DotProductZERO)(DotProduct-ZERO)) return 0; l2(plane._Normal.dot(plane._Position-position))/DotProduct; if (l2-ZERO) return 0; pNormalplane._Normal; lamdal2; return 1; } 射线-圆柱的碰撞检测 计算射线和圆柱方程组得解。 int TestIntersionCylinder(const Cylinder cylinder,const TVector position,const TVector direction, double lamda, TVector pNormal,TVector newposition) 球-球之间的碰撞检测 球被表示为中心和它的半径决定两个球是否相交就是求出它们之间的距离是否小于它们的直径。 在处理两个移动的球是否相交时有一个bug就是当它们的移动速度太快回出现它们相交但在相邻的两步检测不出它们是否相交的情况如下图所示 有一个替代的办法就是细分相邻的时间片断如果在这之间发生了碰撞则确定有效。我们把这个细分时间段设置为3代码如下 //判断球和球是否相交是则返回1否则返回0int FindBallCol(TVector point, double TimePoint, double Time2, int BallNr1, int BallNr2){ TVector RelativeV; TRay rays; double MyTime0.0, AddTime2/150.0, Timedummy10000, Timedummy2-1; TVector posi; //判断球和球是否相交 for (int i0;iNrOfBalls-1;i) { for (int ji1;jNrOfBalls;j) { RelativeVArrayVel[i]-ArrayVel[j]; raysTRay(OldPos[i],TVector::unit(RelativeV)); MyTime0.0; if ( (rays.dist(OldPos[j])) 40) continue; while (MyTimeTime2) { MyTimeAdd; posiOldPos[i]RelativeV*MyTime; if (posi.dist(OldPos[j])40) { pointposi; if (Timedummy(MyTime-Add)) TimedummyMyTime-Add; BallNr1i; BallNr2j; break; } } } } if (Timedummy!10000) { TimePointTimedummy; return 1; } return 0; } 怎样应用我们的知识 现在我们已经可以决定射线和平面/圆柱的交点了,如下图所示: 当我们找到了碰撞位置后,下一步我们需要知道它是否发生在当前这一步中.如果距离碰撞点的位置小于这一步球体运动的间隔,则碰撞发生.我们使用如下的方程计算运动到碰撞时所需的时间: Tc Dsc*T / Dst 接着我们知道碰撞点位置,如下面公式所示: Collision point Start Velocity*Tc 2) 基于物理的模拟 碰撞反应 为了计算对于一个静止物体的碰撞,我们需要知道以下信息:碰撞点,碰撞法线,碰撞时间. 它是基于以下物理规律的,碰撞的入射角等于反射角.如下图所示: R 为反射方向 I 为入射方向 N 为法线方向 反射方向有以下公式计算 : R 2*(-I dot N)*N I rt2ArrayVel[BallNr].mag(); // 返回速度向量的模 ArrayVel[BallNr].unit(); // 归一化速度向量 // 计算反射向量 ArrayVel[BallNr]TVector::unit( (normal*(2*normal.dot(-ArrayVel[BallNr]))) ArrayVel[BallNr] ); ArrayVel[BallNr]ArrayVel[BallNr]*rt2; 球体之间的碰撞 由于它很复杂我们用下图来说明这个原理. U1和U2为速度向量我们用X_Axis表示两个球中心连线的轴U1X和U2X为U1和U2在这个轴上的分量。U1y和U2y为垂直于X_Axis轴的分量。M1和M2为两个球体的分量。V1和V2为碰撞后的速度V1x,V1y,V2x,V2y为他们的分量。 在我们的例子里所有球的质量都相等解得方程为在垂直轴上的速度不变在X_Axis轴上互相交换速度。代码如下 TVector pb1,pb2,xaxis,U1x,U1y,U2x,U2y,V1x,V1y,V2x,V2y; double a,b; pb1OldPos[BallColNr1]ArrayVel[BallColNr1]*BallTime; // 球1的位置 pb2OldPos[BallColNr2]ArrayVel[BallColNr2]*BallTime; // 球2的位置 xaxis(pb2-pb1).unit(); // X-Axis轴 axaxis.dot(ArrayVel[BallColNr1]); // X_Axis投影系数 U1xxaxis*a; // 计算在X_Axis轴上的速度 U1yArrayVel[BallColNr1]-U1x; // 计算在垂直轴上的速度 xaxis(pb1-pb2).unit(); bxaxis.dot(ArrayVel[BallColNr2]); U2xxaxis*b; U2yArrayVel[BallColNr2]-U2x; V1x(U1xU2x-(U1x-U2x))*0.5; // 计算新的速度 V2x(U1xU2x-(U2x-U1x))*0.5; V1yU1y; V2yU2y; for (j0;jNrOfBalls;j) // 更新所有球的位置 ArrayPos[j]OldPos[j]ArrayVel[j]*BallTime; ArrayVel[BallColNr1]V1xV1y; // 设置新的速度 ArrayVel[BallColNr2]V2xV2y; 万有引力的模拟 我们使用欧拉方程来模拟万有引力如下所示 Velocity_New Velovity_Old Acceleration*TimeStep Position_New Position_Old Velocity_New*TimeStep 在每次模拟中我们用上面公式计算的速度取代旧的速度 3) 特殊效果 爆炸 最好的表示爆炸效果的就是使用两个互相垂直的平面并使用alpha混合在窗口中显示它们。接着让alpha变为0设定爆炸效果不可见。代码如下所示 // 渲染/混合爆炸效果 glEnable(GL_BLEND); // 使用混合 glDepthMask(GL_FALSE); // 禁用深度缓存 glBindTexture(GL_TEXTURE_2D, texture[1]); // 设置纹理 for(i0; i20; i) // 渲染20个爆炸效果 { if(ExplosionArray[i]._Alpha0) { glPushMatrix(); ExplosionArray[i]._Alpha-0.01f; // 设置alpha ExplosionArray[i]._Scale0.03f; // 设置缩放 // 设置颜色 glColor4f(1,1,0,ExplosionArray[i]._Alpha); glScalef(ExplosionArray[i]._Scale,ExplosionArray[i]._Scale,ExplosionArray[i]._Scale); // 设置位置 glTranslatef((float)ExplosionArray[i]._Position.X()/ExplosionArray[i]._Scale, (float)ExplosionArray[i]._Position.Y()/ExplosionArray[i]._Scale, (float)ExplosionArray[i]._Position.Z()/ExplosionArray[i]._Scale); glCallList(dlist); // 调用显示列表绘制爆炸效果 glPopMatrix(); } } 声音 在Windows下我们简单的调用PlaySound()函数播放声音。 4) 代码的流程 如果你成功的读完了理论部分在你开始运行程序并播放声音以前。我们将用伪代码向你介绍一些整个流程以便你能成功的看懂代码。 While (Timestep!0) { 对每一个球 { 计算最近的与平面碰撞的位置; 计算最近的与圆柱碰撞的位置; 如果碰撞发生则保存并替换最近的碰撞点; } 检测各个球之间的碰撞; 如果碰撞发生则保存并替换最近的碰撞点; If (碰撞发生) { 移动所有的球道碰撞点的时间; (We already have computed the point, normal and collision time.) 计算碰撞后的效果; Timestep-CollisonTime; } else 移动所有的球体一步 } 下面是对上面伪代码的实现 //模拟函数计算碰撞检测和物理模拟void idle(){ double rt,rt2,rt4,lamda10000; TVector norm,uveloc; TVector normal,point,time; double RestTime,BallTime; TVector Pos2; int BallNr0,dummy0,BallColNr1,BallColNr2; TVector Nc; //如果没有锁定到球上旋转摄像机 if (!hook_toball1) { camera_rotation0.1f; if (camera_rotation360) camera_rotation0; } RestTimeTime; lamda1000; //计算重力加速度 for (int j0;jNrOfBalls;j) ArrayVel[j]accel*RestTime; //如果在一步的模拟时间内(如果来不及计算则跳过几步) while (RestTimeZERO) { lamda10000; //对于每个球找到它们最近的碰撞点 for (int i0;iNrOfBalls;i) { //计算新的位置和移动的距离 OldPos[i]ArrayPos[i]; TVector::unit(ArrayVel[i],uveloc); ArrayPos[i]ArrayPos[i]ArrayVel[i]*RestTime; rt2OldPos[i].dist(ArrayPos[i]); //测试是否和墙面碰撞 if (TestIntersionPlane(pl1,OldPos[i],uveloc,rt,norm)) { //计算碰撞的时间 rt4rt*RestTime/rt2; //如果小于当前保存的碰撞时间则更新它 if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointOldPos[i]uveloc*rt; lamdart4; BallNri; } } } if (TestIntersionPlane(pl2,OldPos[i],uveloc,rt,norm)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointOldPos[i]uveloc*rt; lamdart4; BallNri; dummy1; } } } if (TestIntersionPlane(pl3,OldPos[i],uveloc,rt,norm)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointOldPos[i]uveloc*rt; lamdart4; BallNri; } } } if (TestIntersionPlane(pl4,OldPos[i],uveloc,rt,norm)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointOldPos[i]uveloc*rt; lamdart4; BallNri; } } } if (TestIntersionPlane(pl5,OldPos[i],uveloc,rt,norm)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointOldPos[i]uveloc*rt; lamdart4; BallNri; } } } //测试是否与三个圆柱相碰 if (TestIntersionCylinder(cyl1,OldPos[i],uveloc,rt,norm,Nc)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointNc; lamdart4; BallNri; } } } if (TestIntersionCylinder(cyl2,OldPos[i],uveloc,rt,norm,Nc)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointNc; lamdart4; BallNri; } } } if (TestIntersionCylinder(cyl3,OldPos[i],uveloc,rt,norm,Nc)) { rt4rt*RestTime/rt2; if (rt4lamda) { if (rt4RestTimeZERO) if (! ((rtZERO)(uveloc.dot(norm)ZERO)) ) { normalnorm; pointNc; lamdart4; BallNri; } } } } //计算每个球之间的碰撞如果碰撞时间小于与上面的碰撞则替换它们 if (FindBallCol(Pos2,BallTime,RestTime,BallColNr1,BallColNr2)) { if (sounds) PlaySound(Data/Explode.wav,NULL,SND_FILENAME|SND_ASYNC); if ( (lamda10000) || (lamdaBallTime) ) { RestTimeRestTime-BallTime; TVector pb1,pb2,xaxis,U1x,U1y,U2x,U2y,V1x,V1y,V2x,V2y; double a,b; pb1OldPos[BallColNr1]ArrayVel[BallColNr1]*BallTime; pb2OldPos[BallColNr2]ArrayVel[BallColNr2]*BallTime; xaxis(pb2-pb1).unit(); axaxis.dot(ArrayVel[BallColNr1]); U1xxaxis*a; U1yArrayVel[BallColNr1]-U1x; xaxis(pb1-pb2).unit(); bxaxis.dot(ArrayVel[BallColNr2]); U2xxaxis*b; U2yArrayVel[BallColNr2]-U2x; V1x(U1xU2x-(U1x-U2x))*0.5; V2x(U1xU2x-(U2x-U1x))*0.5; V1yU1y; V2yU2y; for (j0;jNrOfBalls;j) ArrayPos[j]OldPos[j]ArrayVel[j]*BallTime; ArrayVel[BallColNr1]V1xV1y; ArrayVel[BallColNr2]V2xV2y; //Update explosion array for(j0;j20;j) { if (ExplosionArray[j]._Alpha0) { ExplosionArray[j]._Alpha1; ExplosionArray[j]._PositionArrayPos[BallColNr1]; ExplosionArray[j]._Scale1; break; } } continue; } } //最后的测试替换下次碰撞的时间并更新爆炸效果的数组 if (lamda!10000) { RestTime-lamda; for (j0;jNrOfBalls;j) ArrayPos[j]OldPos[j]ArrayVel[j]*lamda; rt2ArrayVel[BallNr].mag(); ArrayVel[BallNr].unit(); ArrayVel[BallNr]TVector::unit( (normal*(2*normal.dot(-ArrayVel[BallNr]))) ArrayVel[BallNr] ); ArrayVel[BallNr]ArrayVel[BallNr]*rt2; for(j0;j20;j) { if (ExplosionArray[j]._Alpha0) { ExplosionArray[j]._Alpha1; ExplosionArray[j]._Positionpoint; ExplosionArray[j]._Scale1; break; } } } else RestTime0; } } 原文及其个版本源代码下载 http://nehe.gamedev.net/data/lessons/lesson.asp?lesson30 没有整理与归纳的知识一文不值高度概括与梳理的知识才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚让创造力自由成长吧 多花时间关心他她人正如别人所关心你的。理想的腾飞与实现没有别人的支持与帮助是万万不能的。 本文转自wenglabs博客园博客原文链接http://www.cnblogs.com/arxive/p/6239517.html如需转载请自行联系原作者
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87054.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!