我的大脑已经融化了我一直在努力的线段与圆柱相交的例程。
/// Line segment VS <cylinder>
// - cylinder (A, B, r) (start point, end point, radius)
// - line has starting point (x0, y0, z0) and ending point (x0+ux, y0+uy, z0+uz) ((ux, uy, uz) is "direction")
// => start = (x0, y0, z0)
// dir = (ux, uy, uz)
// A
// B
// r
// optimize? (= don't care for t > 1)
// <= t = "time" of intersection
// norm = surface normal of intersection point
void CollisionExecuter::cylinderVSline(const Ogre::Vector3& start, const Ogre::Vector3& dir, const Ogre::Vector3& A, const Ogre::Vector3& B, const double r,
const bool optimize, double& t, Ogre::Vector3& normal) {
t = NaN;
// Solution : http://www.gamedev.net/community/forums/topic.asp?topic_id=467789
double cxmin, cymin, czmin, cxmax, cymax, czmax;
if (A.z < B.z) { czmin = A.z - r; czmax = B.z + r; } else { czmin = B.z - r; czmax = A.z + r; }
if (A.y < B.y) { cymin = A.y - r; cymax = B.y + r; } else { cymin = B.y - r; cymax = A.y + r; }
if (A.x < B.x) { cxmin = A.x - r; cxmax = B.x + r; } else { cxmin = B.x - r; cxmax = A.x + r; }
if (optimize) {
if (start.z >= czmax && (start.z + dir.z) > czmax) return;
if (start.z <= czmin && (start.z + dir.z) < czmin) return;
if (start.y >= cymax && (start.y + dir.y) > cymax) return;
if (start.y <= cymin && (start.y + dir.y) < cymin) return;
if (start.x >= cxmax && (start.x + dir.x) > cxmax) return;
if (start.x <= cxmin && (start.x + dir.x) < cxmin) return;
}
Ogre::Vector3 AB = B - A;
Ogre::Vector3 AO = start - A;
Ogre::Vector3 AOxAB = AO.crossProduct(AB);
Ogre::Vector3 VxAB = dir.crossProduct(AB);
double ab2 = AB.dotProduct(AB);
double a = VxAB.dotProduct(VxAB);
double b = 2 * VxAB.dotProduct(AOxAB);
double c = AOxAB.dotProduct(AOxAB) - (r*r * ab2);
double d = b * b - 4 * a * c;
if (d < 0) return;
double time = (-b - sqrt(d)) / (2 * a);
if (time < 0) return;
Ogre::Vector3 intersection = start + dir * time; /// intersection point
Ogre::Vector3 projection = A + (AB.dotProduct(intersection - A) / ab2) * AB; /// intersection projected onto cylinder axis
if ((projection - A).length() + (B - projection).length() > AB.length()) return; /// THIS IS THE SLOW SAFE WAY
//if (projection.z > czmax - r || projection.z < czmin + r ||
// projection.y > cymax - r || projection.y < cymin + r ||
// projection.x > cxmax - r || projection.x < cxmin + r ) return; /// THIS IS THE FASTER BUGGY WAY
normal = (intersection - projection);
normal.normalise();
t = time; /// at last
}
我想到了这种方法可以加快发现交点的投影是否位于圆柱长度内。但是,它不起作用,我不能真正得到它,因为它看起来如此合乎逻辑: 如果投影点的x,y或z坐标不在圆柱体的限制范围内,则应将其视为外部。虽然这在实践中似乎不起作用。
非常感谢任何帮助!
干杯,
比尔科茨亚斯编辑:似乎问题随着边界情况而上升,即当圆柱体与其中一个轴平行时。舍入误差进入等式,“优化”停止正常工作。
也许,如果逻辑 正确,那么通过插入一些容差就会消除问题:
if (projection.z > czmax - r + 0.001 || projection.z < czmin + r - 0.001 || ... etc...
答案 0 :(得分:4)
这是我使用的,它可能会有所帮助:
bool d3RayCylinderIntersection(const DCylinder &cylinder,const DVector3 &org,const DVector3 &dir,float &lambda,DVector3 &normal,DVector3 &newPosition)
// Ray and cylinder intersection
// If hit, returns true and the intersection point in 'newPosition' with a normal and distance along
// the ray ('lambda')
{
DVector3 RC;
float d;
float t,s;
DVector3 n,D,O;
float ln;
float in,out;
RC=org; RC.Subtract(&cylinder.position);
n.Cross(&dir,&cylinder.axis);
ln=n.Length();
// Parallel? (?)
if((ln<D3_EPSILON)&&(ln>-D3_EPSILON))
return false;
n.Normalize();
d=fabs(RC.Dot(n));
if (d<=cylinder.radius)
{
O.Cross(&RC,&cylinder.axis);
//TVector::cross(RC,cylinder._Axis,O);
t=-O.Dot(n)/ln;
//TVector::cross(n,cylinder._Axis,O);
O.Cross(&n,&cylinder.axis);
O.Normalize();
s=fabs( sqrtf(cylinder.radius*cylinder.radius-d*d) / dir.Dot(O) );
in=t-s;
out=t+s;
if (in<-D3_EPSILON)
{
if(out<-D3_EPSILON)
return false;
else lambda=out;
} else if(out<-D3_EPSILON)
{
lambda=in;
} else if(in<out)
{
lambda=in;
} else
{
lambda=out;
}
// Calculate intersection point
newPosition=org;
newPosition.x+=dir.x*lambda;
newPosition.y+=dir.y*lambda;
newPosition.z+=dir.z*lambda;
DVector3 HB;
HB=newPosition;
HB.Subtract(&cylinder.position);
float scale=HB.Dot(&cylinder.axis);
normal.x=HB.x-cylinder.axis.x*scale;
normal.y=HB.y-cylinder.axis.y*scale;
normal.z=HB.z-cylinder.axis.z*scale;
normal.Normalize();
return true;
}
return false;
}
答案 1 :(得分:3)
圆筒是圆形的,对吗?您可以变换坐标,使圆柱的中心线作为Z轴。然后你有一个2D线与圆相交的问题。交点将是沿着线的长度从0到1的参数,因此您可以计算它们在该坐标系中的位置,并与圆柱的顶部和底部进行比较。
你应该能够以封闭的形式完成所有这些工作。没有宽容。当然,你会得到奇点和想象的解决方案。你似乎已经想到了这一切,所以我想我不确定问题是什么。
答案 2 :(得分:2)
但是,如果你想要优化(听起来像你这样),你可以做一些装载。
例如,您可以计算两条线之间的最短距离 - 可能使用点积规则 - 想象一下线程连接两条线。然后如果这个线程是所有可能线程中最短的,那么它将垂直于两条线,所以Thread.LineA = Thread.LineB = 0
如果最短距离大于圆柱体的半径,则为未命中。
您可以使用x,y,z来定义圆柱的轨迹,并将整个事物作为一些可怕的二次方程式进行抖动,并通过首先计算判别式来优化,如果是负的则返回无击中。
要定义轨迹,请取任意点P =(x,y,z)。将它作为垂直于圆柱体的中心线,然后观察它的平方。如果那等于R ^ 2那个点在。
然后你将你的行{s = U + lamda * V}扔进那个混乱中,你最终会得到lamda中一些丑陋的二次方。但这可能比摆弄矩阵更快,除非你能得到硬件(我猜测OpenGL有一些功能让硬件做到这一点超高速)。
这完全取决于你想要多少优化;除非有一个非常好的理由不这样做,否则我个人会接受迈克的回答。
PS如果你解释你使用的技术而不仅仅是转储代码,你可以得到更多的帮助,让读者知道你正在做什么。
答案 3 :(得分:1)
你有没有这样想过?
圆柱体本质上是一个“胖”线段,因此一种方法是找到线段(圆柱体的中心线)上的最近点到线段(您正在测试交叉线的线段)。 / p>
从那里,检查这个最近点和另一个线段之间的距离,并将其与半径进行比较。
此时,你有一个“Pill vs Line Segment”测试,但是你可以做一些平面测试来“切掉”药丸上的盖子来制作一个圆柱体。
从臀部拍摄一点但是希望它有所帮助(: