3

我正在尝试对对象纹理执行实时绘画。现在使用 Irrlicht,但这并不重要。

到目前为止,我已经使用这个算法得到了正确的 UV 坐标:

  1. 找出用户选择了哪个对象的三角形(光线投射,没什么难的)

  2. 找出该三角形上交点的 UV(重心)坐标

  3. 找出每个三角形顶点的 UV(纹理)坐标

  4. 找出交点的UV(纹理)坐标

  5. 计算交点的纹理图像坐标

但不知何故,当我在纹理图像的第 5 步中绘制点时,我得到了完全错误的结果。因此,当在光标点绘制一个矩形时,它的 X(或 Z)坐标是反转的:

在此处输入图像描述

在此处输入图像描述

这是我用来获取纹理坐标的代码:

core::vector2df getPointUV(core::triangle3df tri, core::vector3df p)
{
    core::vector3df 
    v0 = tri.pointC - tri.pointA,
    v1 = tri.pointB - tri.pointA,
    v2 = p - tri.pointA;

    float dot00 = v0.dotProduct(v0),
    dot01 = v0.dotProduct(v1),
    dot02 = v0.dotProduct(v2),
    dot11 = v1.dotProduct(v1),
    dot12 = v1.dotProduct(v2);

    float invDenom = 1.f / ((dot00 * dot11) - (dot01 * dot01)),
    u = (dot11 * dot02 - dot01 * dot12) * invDenom,
    v = (dot00 * dot12 - dot01 * dot02) * invDenom;

    scene::IMesh* m = Mesh->getMesh(((scene::IAnimatedMeshSceneNode*)Model)->getFrameNr());

    core::array<video::S3DVertex> VA, VB, VC;
    video::SMaterial Material;

    for (unsigned int i = 0; i < m->getMeshBufferCount(); i++)
    {
    scene::IMeshBuffer* mb = m->getMeshBuffer(i);
    video::S3DVertex* vertices = (video::S3DVertex*) mb->getVertices();

    for (unsigned long long v = 0; v < mb->getVertexCount(); v++)
    {
        if (vertices[v].Pos == tri.pointA)
        VA.push_back(vertices[v]); else
        if (vertices[v].Pos == tri.pointB)
        VB.push_back(vertices[v]); else
        if (vertices[v].Pos == tri.pointC)
        VC.push_back(vertices[v]);

        if (vertices[v].Pos == tri.pointA || vertices[v].Pos == tri.pointB || vertices[v].Pos == tri.pointC)
        Material = mb->getMaterial();

        if (VA.size() > 0 && VB.size() > 0 && VC.size() > 0)
        break;
    }

    if (VA.size() > 0 && VB.size() > 0 && VC.size() > 0)
        break;
    }

    core::vector2df 
    A = VA[0].TCoords,
    B = VB[0].TCoords,
    C = VC[0].TCoords;

    core::vector2df P(A + (u * (C - A)) + (v * (B - A)));
    core::dimension2du Size = Material.getTexture(0)->getSize();
    CursorOnModel = core::vector2di(Size.Width * P.X, Size.Height * P.Y);
    int X = Size.Width * P.X, Y = Size.Height * P.Y;

    // DRAWING SOME RECTANGLE    
    Material.getTexture(0)->lock(true);
    Device->getVideoDriver()->setRenderTarget(Material.getTexture(0), true, true, 0);
        Device->getVideoDriver()->draw2DRectangle(video::SColor(255, 0, 100, 75), core::rect<s32>((X - 10), (Y - 10), 
            (X + 10), (Y + 10)));
    Device->getVideoDriver()->setRenderTarget(0, true, true, 0);
    Material.getTexture(0)->unlock();

    return core::vector2df(X, Y);
}

我只想让我的对象可以实时绘制。我当前的问题是:错误的纹理坐标计算非唯一的顶点 UV 坐标(因此,在矮人斧头的一侧绘制一些东西会在斧头的另一侧绘制相同的东西)。

我该怎么做?

4

1 回答 1

3

我能够使用您的代码库并让它为我工作。

关于你的第二个问题“非唯一的顶点 UV 坐标”: 嗯,你是绝对正确的,你需要唯一的 vertexUVs 才能让它工作,这意味着你必须打开你的模型并且不要使用共享的 uv 空间,例如镜像元素和东西。(例如左/右启动 - 如果它们使用相同的 uv 空间,您将在两者上自动绘制,您希望一个为红色,另一个为绿色)。您可以查看“uvlayout”(工具)或 uv-unwrap 修改器 ind 3ds max。

关于第一个也是更重要的问题:“**错误的纹理坐标计算”:你的baycentric坐标的计算是正确的,但我认为你的输入数据是错误的。我假设您通过使用 irrlicht 的 CollisionManager 和 TriangleSelector 获得三角形和碰撞点。问题是,三角形顶点的位置(作为碰撞测试的返回值)在 WorldCoordiates 中。但是您需要在 ModelCoordinates 中使用它们进行计算,因此您需要执行以下操作:

伪代码

  1. 将包含命中三角形网格的节点作为参数添加到 getPointUV()
  2. 通过调用 node->getAbsoluteTransformation() [inverse] 获得逆 absoluteTransformation-Matrix
  3. 通过这个逆矩阵变换三角形的顶点,并将这些值用于方法的其余部分。

下面你会发现我的优化方法,它适用于一个非常简单的网格(一个网格,只有一个网格缓冲区)。

代码:

irr::core::vector2df getPointUV(irr::core::triangle3df tri, irr::core::vector3df p, irr::scene::IMeshSceneNode* pMeshNode, irr::video::IVideoDriver* pDriver)
{
    irr::core::matrix4 inverseTransform(
    pMeshNode->getAbsoluteTransformation(),
      irr::core::matrix4::EM4CONST_INVERSE);

    inverseTransform.transformVect(tri.pointA);
    inverseTransform.transformVect(tri.pointB);
    inverseTransform.transformVect(tri.pointC);

    irr::core::vector3df 
    v0 = tri.pointC - tri.pointA,
    v1 = tri.pointB - tri.pointA,
    v2 = p - tri.pointA;

    float dot00 = v0.dotProduct(v0),
    dot01 = v0.dotProduct(v1),
    dot02 = v0.dotProduct(v2),
    dot11 = v1.dotProduct(v1),
    dot12 = v1.dotProduct(v2);

    float invDenom = 1.f / ((dot00 * dot11) - (dot01 * dot01)),
    u = (dot11 * dot02 - dot01 * dot12) * invDenom,
    v = (dot00 * dot12 - dot01 * dot02) * invDenom;

    irr::video::S3DVertex A, B, C;
    irr::video::S3DVertex* vertices = static_cast<irr::video::S3DVertex*>(
      pMeshNode->getMesh()->getMeshBuffer(0)->getVertices());

    for(unsigned int i=0; i < pMeshNode->getMesh()->getMeshBuffer(0)->getVertexCount(); ++i)
    {
      if( vertices[i].Pos == tri.pointA)
      {
        A = vertices[i];
      }
      else if( vertices[i].Pos == tri.pointB)
      {
        B = vertices[i];
      }
      else if( vertices[i].Pos == tri.pointC)
      {
        C = vertices[i];
      }
    }

    irr::core::vector2df t2 = B.TCoords - A.TCoords;
    irr::core::vector2df t1 = C.TCoords - A.TCoords;

    irr::core::vector2df uvCoords = A.TCoords + t1*u + t2*v;

    return uvCoords;
}
于 2012-03-01T12:55:25.533 回答