我已经通过vtkOBJReader
加载了纹理映射的OBJ并将其加载到vtkModifiedBSPTree
:
auto readerOther(vtkSmartPointer<vtkOBJReader>::New());
auto rawOtherPath(modelPathOther.toLatin1());
readerOther->SetFileName(rawOtherPath.data());
readerOther->Update();
auto meshDataOther(readerOther->GetOutput());
auto bspTreeOther(vtkSmartPointer<vtkModifiedBSPTree>::New());
bspTreeOther->SetDataSet(meshDataOther);
bspTreeOther->BuildLocator();
然后我计算我的线段开始和结束并将其输入
if (bspTreeOther->IntersectWithLine(p1, p2, tolerance, distanceAlongLine, intersectionCoords, pcoords, subId, cellId, cell))
当然还有所有相关的预定义变量。
我需要的是交叉点处纹理的UV坐标。
我对VTK非常陌生,我还没有抓住它如何组合在一起的逻辑;当我在挖掘源头时,抽象层仍在失去我。
我已经在SO和VTK用户档案中寻找这个答案,并且发现那些了解VTK的人给那些几乎在那里的人们提供了模糊的暗示,因此对我来说几乎没有帮助。
(附件11/9/2018) 为了澄清,我正在使用由单个3D扫描仪拍摄创建的非退化三角网格,因此我的代码将不会看到四边形和其他更高的多边形。一般的解决方案应该考虑到这些问题,但这可以通过良好的手法应用程序首先对网格进行三角测量来实现。
答案 0 :(得分:1)
[重写2019/5/7以反映更新的理解。]
在发现参数坐标是一个函数的输入后,在三角形单元的情况下可以得到重心坐标,然后了解重心坐标是什么,我能够解决以下问题。
const auto readerOther(vtkSmartPointer<vtkOBJReader>::New());
const auto rawOtherPath(modelPathOther.toLatin1());
readerOther->SetFileName(rawOtherPath.data());
readerOther->Update();
const auto meshDataOther(readerOther->GetOutput());
const auto bspTreeOther(vtkSmartPointer<vtkModifiedBSPTree>::New());
bspTreeOther->SetDataSet(meshDataOther);
bspTreeOther->BuildLocator();
double point1[3]{0.0, 0.0, 0.0}; // start of line segment used to intersect the model.
double point2[3]{0.0, 0.0, 10.0}; // end of line segment
double distanceAlongLine;
double intersectionCoords[3]; // The coordinate of the intersection.
double parametricCoords[3]; // Parametric Coordinates of the intersection - see https://lorensen.github.io/VTKExamples/site/VTKBook/08Chapter8/#82-interpolation-functions
int subId; // ?
vtkIdType cellId;
double intersectedTextureCoords[2];
if (bspTreeOther->IntersectWithLine(point1, point2, TOLERANCE, distanceAlongLine, intersectionCoords, parametricCoords, subId, cellId))
{
const auto textureCoordsOther(meshDataOther->GetPointData()->GetTCoords());
const auto pointIds{meshDataOther->GetCell(cellId)->GetPointIds()};
const auto vertexIndex0{pointIds->GetId(0)};
const auto vertexIndex1{pointIds->GetId(1)};
const auto vertexIndex2{pointIds->GetId(2)};
double texCoord0[2];
double texCoord1[2];
double texCoord2[2];
textureCoordsOther->GetTuple(vertexIndex0, texCoord0);
textureCoordsOther->GetTuple(vertexIndex1, texCoord1);
textureCoordsOther->GetTuple(vertexIndex2, texCoord2);
const auto parametricR{parametricCoords[0]};
const auto parametricS{parametricCoords[1]};
const auto barycentricW0{1 - parametricR - parametricS};
const auto barycentricW1{parametricR};
const auto barycentricW2{parametricS};
intersectedTextureCoords[0] =
barycentricW0 * texCoord0[0] +
barycentricW1 * texCoord1[0] +
barycentricW2 * texCoord2[0];
intersectedTextureCoords[1] =
barycentricW0 * texCoord0[1] +
barycentricW1 * texCoord1[1] +
barycentricW2 * texCoord2[1];
}
请注意,此代码是对我使用的实际代码的解释;我使用Qt及其QVector2D和QVector3D类以及一些解释器粘合函数来往返双打数组。
有关各种细胞类型的参数坐标系的详细信息,请参阅https://lorensen.github.io/VTKExamples/site/VTKBook/08Chapter8。
答案 1 :(得分:1)
注意,如果一个顶点属于多个多边形并且具有不同的纹理坐标,则VTK将创建该顶点的副本。 我不使用vtkCleanPolyData,因为据我所知,VTK将合并此类“重复项”,并且我们将丢失所需的信息。
我使用vtkCellLocator而不是vtkModifiedBSPTree, 因为就我而言,它更快。
主文件main.cpp
。
您可以在start
和end
数组中找到幻数-它们是您的p1
和p2
。
我已经设置了这些值,例如
#include <vtkSmartPointer.h>
#include <vtkPointData.h>
#include <vtkCellLocator.h>
#include <vtkGenericCell.h>
#include <vtkOBJReader.h>
#include <vtkTriangleFilter.h>
#include <vtkMath.h>
#include <iostream>
int main(int argc, char * argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " OBJ_file_name" << std::endl;
return EXIT_FAILURE;
}
auto reader{vtkSmartPointer<vtkOBJReader>::New()};
reader->SetFileName(argv[1]);
reader->Update();
// Triangulate the mesh if needed
auto triangleFilter{vtkSmartPointer<vtkTriangleFilter>::New()};
triangleFilter->SetInputConnection(reader->GetOutputPort());
triangleFilter->Update();
auto mesh{triangleFilter->GetOutput()};
// Use `auto mesh(reader->GetOutput());` instead if no triangulation needed
// Build a locator to find intersections
auto locator{vtkSmartPointer<vtkCellLocator>::New()};
locator->SetDataSet(mesh);
locator->BuildLocator();
// Initialize variables needed for intersection calculation
double start[3]{-1, 0, 0.5};
double end[3]{ 1, 0, 0.5};
double tolerance{1E-6};
double relativeDistanceAlongLine;
double intersectionCoordinates[3];
double parametricCoordinates[3];
int subId;
vtkIdType cellId;
auto cell{vtkSmartPointer<vtkGenericCell>::New()};
// Find intersection
int intersected = locator->IntersectWithLine(
start,
end,
tolerance,
relativeDistanceAlongLine,
intersectionCoordinates,
parametricCoordinates,
subId,
cellId,
cell.Get()
);
// Get points of intersection cell
auto pointsIds{vtkSmartPointer<vtkIdList>::New()};
mesh->GetCellPoints(cellId, pointsIds);
// Store coordinates and texture coordinates of vertices of the cell
double meshTrianglePoints[3][3];
double textureTrianglePoints[3][2];
auto textureCoordinates{mesh->GetPointData()->GetTCoords()};
for (unsigned pointNumber = 0; pointNumber < cell->GetNumberOfPoints(); ++pointNumber)
{
mesh->GetPoint(pointsIds->GetId(pointNumber), meshTrianglePoints[pointNumber]);
textureCoordinates->GetTuple(pointsIds->GetId(pointNumber), textureTrianglePoints[pointNumber]);
}
// Normalize the coordinates
double movedMeshTrianglePoints[3][3];
for (unsigned i = 0; i < 3; ++i)
{
movedMeshTrianglePoints[0][i] = 0;
movedMeshTrianglePoints[1][i] =
meshTrianglePoints[1][i] -
meshTrianglePoints[0][i];
movedMeshTrianglePoints[2][i] =
meshTrianglePoints[2][i] -
meshTrianglePoints[0][i];
}
// Normalize the texture coordinates
double movedTextureTrianglePoints[3][2];
for (unsigned i = 0; i < 2; ++i)
{
movedTextureTrianglePoints[0][i] = 0;
movedTextureTrianglePoints[1][i] =
textureTrianglePoints[1][i] -
textureTrianglePoints[0][i];
movedTextureTrianglePoints[2][i] =
textureTrianglePoints[2][i] -
textureTrianglePoints[0][i];
}
// Calculate SVD of a matrix consisting of normalized vertices
double U[3][3];
double w[3];
double VT[3][3];
vtkMath::SingularValueDecomposition3x3(movedMeshTrianglePoints, U, w, VT);
// Calculate pseudo inverse of a matrix consisting of normalized vertices
double pseudoInverse[3][3]{0};
for (unsigned i = 0; i < 3; ++i)
{
for (unsigned j = 0; j < 3; ++j)
{
for (unsigned k = 0; k < 3; ++k)
{
if (w[k] != 0)
{
pseudoInverse[i][j] += VT[k][i] * U[j][k] / w[k];
}
}
}
}
// Calculate interpolation matrix
double interpolationMatrix[3][2]{0};
for (unsigned i = 0; i < 3; ++i)
{
for (unsigned j = 0; j < 2; ++j)
{
for (unsigned k = 0; k < 3; ++k)
{
interpolationMatrix[i][j] += pseudoInverse[i][k] * movedTextureTrianglePoints[k][j];
}
}
}
// Calculate interpolated texture coordinates of the intersection point
double interpolatedTexturePoint[2]{textureTrianglePoints[0][0], textureTrianglePoints[0][1]};
for (unsigned i = 0; i < 2; ++i)
{
for (unsigned j = 0; j < 3; ++j)
{
interpolatedTexturePoint[i] += (intersectionCoordinates[j] - meshTrianglePoints[0][j]) * interpolationMatrix[j][i];
}
}
// Print the result
std::cout << "Interpolated texture coordinates";
for (unsigned i = 0; i < 2; ++i)
{
std::cout << " " << interpolatedTexturePoint[i];
}
std::cout << std::endl;
return EXIT_SUCCESS;
}
CMake项目文件CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
PROJECT(IntersectInterpolate)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
add_executable(IntersectInterpolate MACOSX_BUNDLE main.cpp)
if(VTK_LIBRARIES)
target_link_libraries(IntersectInterpolate ${VTK_LIBRARIES})
else()
target_link_libraries(IntersectInterpolate vtkHybrid vtkWidgets)
endif()
假设您有一个由三角形组成的网格,并且您的顶点具有纹理坐标。
给定三角形A
,B
和C
的顶点,相应的纹理坐标A'
,B'
和C'
,您想要查找从三角形的另一个内部和边界点到纹理的映射(以进行插值)。
让我们做出一些合理的假设:
A
,B
,C
应对应于其纹理坐标A'
,B'
,C'
; X
,例如AB
,应通过以下方式与A'B'
线的点相对应:|AX| / |AB| = |A'X'| / |A'B'|
—原始三角形的一半在纹理贴图上处于中间位置;
(A + B + C) / 3
的质心应与纹理三角形(A' + B' + C') / 3
的质心相对应。好像我们要进行仿射映射:原始三角形的顶点坐标应乘以一些系数,并添加到某些常数中。 让我们构建方程组
Ax * Mxx + Ay * Myx + Az * Mzx + M0x = A'x
Ax * Mxy + Ay * Myy + Az * Mzy + M0y = A'y
Ax * Mxz + Ay * Myz + Az * Mzz + M0z = 0
,与B
和C
相同。
您可以看到我们有9
个方程式和12
个未知数。
不过,包含Miz
(在i
中用于{x, y, z}
的方程式具有解0
,并且在进一步的计算中不起作用,因此我们可以将它们设置为等于0
。
因此,我们的系统具有6
个方程式和8
个未知数
Ax * Mxx + Ay * Myx + Az * Mzx + M0x = A'x
Ax * Mxy + Ay * Myy + Az * Mzy + M0y = A'y
让我们在矩阵视图中编写整个系统
-- -- -- -- -- --
| 1 Ax Ay Az | | M0x M0y | | A'x A'y |
| 1 Bx By Bz | x | Mxx Mxy | = | B'x B'y |
| 1 Cx Cy Cz | | Myx Myy | | C'x C'y |
-- -- | Mzx Mzy | -- --
-- --
我从A
和B
中减去C
顶点的坐标
和A'
和B'
中的纹理坐标C'
,
现在我们有了第一个顶点的三角形
在坐标系的开始以及相应的纹理坐标。
这意味着现在三角形不相对于另一个进行平移(移动)
而且我们不需要插值矩阵的M0
部分
-- -- -- -- -- --
| Bx By Bz | | Mxx Mxy | | B'x B'y |
| Cx Cy Cz | x | Myx Myy | = | C'x C'y |
-- -- | Mzx Mzy | -- --
-- --
让我们叫第一个矩阵P
,第二个M
和最后一个T
P M = T
矩阵P
不是正方形。
如果我们向其添加零行,矩阵将变为奇异。
因此,我们必须计算它的伪逆来求解方程。
VTK中没有用于计算伪逆矩阵的函数。
我们转到Wikipedia上的Moore–Penrose inverse文章,看到可以使用SVD进行计算。
VTKMath::SingularValueDecomposition3x3函数允许我们执行此操作。
该函数为我们提供U
,S
和VT
矩阵。
我将矩阵P
的伪逆写为P"
,
将U
转换为UT
,将VT
转换为V
。
对角矩阵S
的伪逆是具有1 / Sii
个元素的矩阵
其中Sii
不是零,0
是零元素
P = U S VT
P" = V S" UT
M = P" T
要应用插值矩阵,
我们不必忘记我们需要转换输入和输出向量。
A'
是三角形中第一个顶点的纹理坐标的2D向量,
A
是顶点坐标的3D向量,
M
是找到的插值矩阵,
p
是我们要获取纹理坐标的3D交点,
t'
是带有插值纹理坐标的2D向量
t' = A' + (p - A) M