我想沿着由一系列点定义的路径绘制一条管。
我的想法是,我基本上可以围绕每个点绘制圆圈,然后使用GL_QUAD_STRIP将圆圈(或者更确切地说,圆圈上的点)连接在一起。
所以,我在圈子上创建点的代码类似于:
point_of_circle[0] = pathVertices[i][0] + TUBE_RADIUS * cos(j/TUBE_SEGMENTS * 2*M_PI));
point_of_circle[1] = pathVertices[i][1] + TUBE_RADIUS * sin(j/TUBE_SEGMENTS * 2*M_PI));
point_of_circle[2] = pathVertices[i][2];
其中i
是路径顶点的索引(即我希望绘制管的原始点之一),j
是圆的当前点的索引正在创建(从0到TUBE_SEGMENTS
)。
现在,问题在于,如果我以这种方式创建圆圈,它们总是以相同的方式定向(圆圈彼此“平行”,看起来像我一直point_of_circle[2] = pathVertices[i][2]
),这不是我想要的,因为那时结构看起来不像一个合适的管。
我认为我能做的是计算向量,它与我正在绘制的路径相切。然后,我需要以某种方式使圆圈面向正确的方向,具体取决于计算的切线。
例如,如果计算的正切值为0,0,1
,则代码应保持原样。但是,如果切线计算为其他任何值,则应旋转圆圈。
例如,如果计算的切线为0,1,0
,则整个圆的y坐标应相同,如果沿z轴看,则应该只看到圆的水平线段。
如果切线为1,0,0
,如果沿z轴看,则应该看到圆的垂直线段。
如何根据计算的切线编写能够正确创建圆的代码?我希望我的解释足够清楚,我是OpenGL的初学者和大部分3D数学。
答案 0 :(得分:2)
如果你没有重新发明轮子的话,总会有GLE Tubing and Extrusion Library。
答案 1 :(得分:1)
我已经实现了类似的功能,可以为您提供一些建议:
设P_0,P1等为路径上的点。管由段S_i =(P_i,Pi + 1)
组成您的方法是正确的 - 您可以在P_i周围生成圆圈,然后将它们连接成每个线段的四条带。对于段S_0到S_N-2,每个将有1个圆,而对于最后一个段S_N-1,有2个圆要生成。
为了生成段S_i =(P_i,P_i + 1)的圆,计算段方向D = P_i + 1-P_i。现在,您需要在Normal = D的平面上生成一个圆。这可以通过多种方式完成。也许你在XY平面上有一个预计算圆的点,你可以变换点。
这里的主要问题是,当你从一个段到另一个段时,管可以开始扭曲,因为当矢量变化时,圆上的“原点”点将不匹配。如果您对管进行纹理处理,这一点尤为重要。解决此问题的一种方法是基于“顶侧”标准选择生成的圆中的第一个点。想象一下3D管 - 在视觉上,很容易在管上绘制3D线,使其始终保持在管的顶部。该“顶侧”矢量取决于管段方向,并且对于管的垂直截面是不确定的。对于非垂直剖面,由下式给出:
T = D X(Z X D)
X是交叉乘积,Z是向上的方向。
根据我的经验,这很难实现。不幸的是我无法分享代码。祝你好运。
答案 2 :(得分:0)
找到切线后,您可以按如下方式获得旋转四分之一。 (参考:Ogre3D Vector3.cpp)
public static Quaternion getRotationTo(Vector3 src, Vector3 dest)
{
Quaternion q = new Quaternion();
src.Normalize();
dest.Normalize();
double d = Vector3.Dot(src, dest);
if (d >= 1.0f)
{
return Quaternion.Identity;
}
if (d < (1e-6f - 1.0f))
{
// // Generate an axis
// Vector3 axis = Vector3::UNIT_X.crossProduct(*this);
// if (axis.isZeroLength()) // pick another if colinear
// axis = Vector3::UNIT_Y.crossProduct(*this);
// axis.normalise();
// q.FromAngleAxis(Radian(Math::PI), axis);
// Generate an axis
Vector3 axis = Vector3.Cross(Vector3.UnitX, src);
if (axis.Length() == 0.0f)
{
axis = Vector3.Cross(Vector3.UnitY, src);
}
axis.Normalize();
q = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(180));
}
else
{
// Real s = Math::Sqrt( (1+d)*2 );
// Real invs = 1 / s;
// Vector3 c = v0.crossProduct(v1);
// q.x = c.x * invs;
// q.y = c.y * invs;
// q.z = c.z * invs;
// q.w = s * 0.5;
// q.normalise();
Double s = Math.Sqrt((1 + d) * 2);
Double invs = 1 / s;
Vector3 c = Vector3.Cross(src, dest);
q.X = (float)(c.X * invs);
q.Y = (float)(c.Y * invs);
q.Z = (float)(c.Z * invs);
q.W = (float)(s * 0.5);
q.Normalize();
}
return q;
}