3

我想沿着由点列表定义的路径绘制一个管子。

我的想法是,我基本上可以围绕我的每个点画圈,然后使用 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 数学的初学者。

4

3 回答 3

2

如果您不重新发明轮子,总有GLE 管材和挤压库。

于 2012-06-25T23:27:45.883 回答
1

我已经实现了类似的东西,可以为您提供一些建议:

让 P_0、P1 等成为路径上的点。Tube 由分段 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 = DX (ZXD)

X 是叉积,Z 是向上方向。

根据我的经验,这很难做到。不幸的是,我无法共享代码。祝你好运。

于 2012-06-26T06:46:24.093 回答
0

找到切线后,可以得到旋转quat,如下所示。(参考: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;
    }
于 2012-06-26T06:10:57.187 回答