实时物体绘制

3
我正在尝试对对象纹理进行实时绘画。目前使用的是Irrlicht,但这并不重要。
到目前为止,我已经使用以下算法得到了正确的UV坐标:
1. 找出用户选择的物体三角形(射线投射,没有真正困难)。 2. 找出该三角形上交点的UV(重心)坐标。 3. 找出每个三角形顶点的UV(纹理)坐标。 4. 找出交点的UV(纹理)坐标。 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坐标(因此,在矮人斧子的一侧绘制某些东西将在该斧子的另一侧绘制相同的东西)。
我应该怎么做?

嗯...要想拥有独特的顶点UV坐标,你需要改变纹理,复制修改后的区域并将其应用于具有新UV的正确三角形,不是吗? - LBarret
我觉得不是。首先,我需要确定绘画区域。这需要独特的UV贴图;) - shybovycha
我找到了这个仓库,很高兴,但请添加一个许可证。我以为你已经转向Ruby,因为我发现了moo3d,但是以下URL中的程序看起来是正确的:https://github.com/shybovycha/irrPaint3D 如果您不添加许可证,则被视为“无权限”:https://choosealicense.com/no-permission/(除了您和回答者在此处粘贴的片段之外,它们获得了stackoverflow许可证)。该程序非常有用,因为Blender在裁剪笔画时与Irrlicht中的最近像素模式不准确,例如Minetest。许可证将允许其他人使用和改进该程序。 - Poikilos
1个回答

3

我能够使用你的代码库并使其对我起作用。

关于你的第二个问题“非唯一顶点UV坐标”: 好吧,你是完全正确的,你需要唯一的顶点UV才能使其正常工作,这意味着你必须展开你的模型,并且不要使用共享的UV空间来制作例如镜像元素等。 (例如左/右靴子-如果它们使用相同的UV空间,你将自动在两者上绘画,而你想要一个为红色,另一个为绿色)。你可以查看“uvlayout”(工具)或3ds max中的uv-unwrap修改器。

关于第一个更重要的问题:“错误的纹理坐标计算”: 你的重心坐标计算是正确的,但我认为你的输入数据是错误的。我假设你使用irrlicht的CollisionManager和TriangleSelector获取三角形和collisionPoint。问题是,三角形顶点的位置(你从collisionTest中获得的返回值)是在WorldCoordiates中。但是你需要它们在ModelCoordinates中进行计算,所以这里是你需要做的:

伪代码:

  1. 将包含被撞三角形网格的节点添加为参数以获取PointUV()
  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;
}

我采用了类似的方法,将向量表示为其他两个向量的和(都在单个三角形的上下文中),由于向量是相对的,因此无需逆矩阵(或任何其他矩阵)变换(耶!)。 在我的博客https://shybovycha.github.io/2021/05/04/irr-paint-3d.html#the-maths-behind中简要描述了这种方法。 - shybovycha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接