Определите развернутые сетчатые УФ области

Предположим, у нас есть 3D-сетка с текстурными координатами для каждой вершины, поэтому, если я сделаю ее развернутой, я получу что-то вроде этого (игнорируя красный квадрат):

введите описание изображения здесь

Сейчас я пытаюсь найти подходящий алгоритм для уникальной идентификации этих областей с использованием вершинных UV и сохранения атрибута с этим уникальным значением id. Идея состоит в том, чтобы использовать это значение в качестве индекса для таблицы цветов и получить что-то вроде этого (сделано вручную):

введите описание изображения здесь

Я попытался перебрать каждую вершину и найти «несвязанные» треугольники, сравнивающие координаты текстуры, но порядок индексов сетки кажется не связанным с тем, как расположены UV, или я не применяю правильную формулу. У меня нет сомнений относительно того, как сохранить и передать это значение в шейдер или что-то еще, сомневаюсь, как узнать «область», к которой принадлежит вершина, или, в конечном счете, пиксель.

Благодарю.

ОБНОВИТЬ:
Данные, используемые для визуализации сетки, представляют собой список вершин (GL_VERTEX_BUFFER) плюс список индексов (GL_ELEMENT_ARRAY). Сетка отображается как GL_TRIANGLES, и каждая вершина является структурой, подобной этой:

struct Vertex
{
float x, y, z;
float nx, ny, nz;
float tcx, tcy;
float regionId; //the attribute I want to fill
};

struct MPUVRegionVertex
{
float x, y;
int faceId, regionId;
};

ОБНОВЛЕНИЕ 2: Я создал новый массив вершин MPUVRegionVertex, с элементом для каждого индекса (не для каждой уникальной вершины). После ответа @ CsabaBálint я получил следующий код:

MPUVRegionVertex* uvVertexData = new MPUVRegionVertex[indexCount];

for(int ic = 0; ic < indexCount / 3; ic++)
{
for(int vc = 0; vc < 3; vc++)
{
uvVertexData[3*ic+vc].x = vertexData[indexData[3*ic+vc]].tcx;
uvVertexData[3*ic+vc].y = vertexData[indexData[3*ic+vc]].tcy;
uvVertexData[3*ic+vc].faceId = ic;
}
}

std::vector<std::forward_list<int> > graph(indexCount);

for(int t1=0;t1 < indexCount; ++t1)
{
for(int t2 = t1 + 1; t2 < indexCount; ++t2)
{
if (uvVertexData[t1].faceId == uvVertexData[t2].faceId)
{
graph[t1].push_front(t2);
graph[t2].push_front(t1);
}
}
}

std::forward_list<int> stack;
std::vector<int> component(indexCount);
std::set<int> notvisited;

for(int nv = 0; nv < indexCount; nv++)
{
notvisited.insert(nv);
}int k = 0;
while(notvisited.size() > 0)
{
stack.push_front(*notvisited.begin());
notvisited.erase(notvisited.begin());

while(!stack.empty())
{
//SOMETHING WRONG HERE
int temp = stack.front();
notvisited.erase(temp);
stack.pop_front();
component[temp] = k;
stack.merge(graph[temp]);
graph[temp].clear();
}
k++;
}

Результатом является разное k каждые три индекса, это означает, что k ++ вызывается для каждого нового треугольника, поэтому я что-то упускаю в алгоритме: S.

component[0]=0
component[1]=0
component[2]=0
component[3]=1
component[4]=1
component[5]=1
component[6]=2
component[7]=2
component[8]=2
component[9]=3
...
component[1778]=592
component[1779]=593
component[1780]=593
component[1781]=593

Некоторая информация о сетке:

Size of shape[0].indices: 1782
shape[0].positions: 1242
shape[0].texcoords: 828
shape[0].normals: 1242

ОБНОВЛЕНИЕ 3

Для получения дополнительной информации, есть только одна UV-координата для каждой вершины.

Отчисления / Правила до сих пор:

  • вершина может быть более чем в одной грани (часть более чем одного треугольника).
  • вершина будет n раз в массиве vertexToFace, один раз для каждой грани она принадлежит.
  • первая вершина в массиве vertexToFace будет произвольно иметь regionId = 0.
  • вершина принадлежит области, если она имеет те же координаты x и y или ту же грань другой вершины в этой области.

Если я правильно понял, это правильная информация для реализации нерекурсивного обхода графа. Мне нужно перебрать и сохранить как связанную, так и несвязанную вершину, все связанные вершины будут частью текущей области, все не будет проверяться снова с уже подключенными, первая итерация сохраняет первые вершины треугольника, вторая — все вершины треугольники, которые «касаются» первого треугольника, продолжаются до тех пор, пока итерация не даст новой связанной вершины (оптимизация здесь, если мы проверяем только список вершин, добавленный в последней итерации), новая добавленная вершина означает, что пришло время увеличивать regionId и начните снова с первой несвязанной вершины.

Я постараюсь реализовать поиск, следуя этому проекту.

1

Решение

Сейчас я пытаюсь найти подходящий алгоритм для уникальной идентификации этих областей с использованием вершинных UV и сохранения атрибута с этим уникальным значением id.

Создание графика

Сделайте идентификаторы для вершин и граней (пронумеруйте их). Но убедитесь, что одни и те же вершины получают одинаковые идентификаторы, сравните их по УФ или положению.

Создать вектор: std::vector<int> vertexToFace;

vertexToFace[i]==j означает, что i-я вершина находится на лице j.

Тогда две вершины являются соседями, если они находятся на одной грани.

Затем создайте std::vector<std::forward_list<int> > graph; Сохраните вершины как векторный индекс и добавьте соседей. (O (n ^ 2) сложность)

Чтобы сделать это, вы должны взять i-ую вершину, и для каждого j вам нужно проверить погоду, они на одном лице. Немного оптимизированная версия:

for(int i=0; i<n; ++i) for(int j=i+1; j <n ++j)
if (vertexToFace[i] == vertexToFace[j])
{
graph[i].push_front(j);
graph[j].push_front(i);
}

Это O (n ^ 2), но его легко реализовать. Тяжелее, но быстрее требуется еще один вектор: std::vector<std::array<int,3>> faceToVertex;Таким образом, из i-й вершины вы можете получить доступ к ее соседям в постоянное время. В любом случае, мы построили график, в котором мы ищем подключенные компоненты, что легко с поиск в глубину.

Реализация алгоритма связанных компонентов

Чтобы реализовать это, вы должны сделать еще один вектор: std::vector<bool> visited(n,false);, и еще один std::vector<int> component(n), Решение вашей проблемы будет в этом последнем.

Алгоритм прост, начать с вершины 0 и установить visited[0] = true; а также component[0]=0;, Затем для каждого невидимого соседа сделайте точно так же, как и для соседа i (некоторый элемент forward_list) if (!visited[i]) component[i] = 0;затем сделайте то же самое. Он останавливается, когда все элементы компонента становятся посещенными. Итак, вам нужно найти не посещенный элемент и повторить вышеописанное, но знать, что вы делаете компонент 1 и так далее. Пример:

int l, k=0;
while(l!=n)
{
l=0;
while(visited[l]) l++;
fill_from(graph, visited, component, l, k);
++k;
}

Я думаю, вы поняли, так что: (псевдокод)

void fill_from(graph, visited, component, l, k)
{
if(visited[l]) return;
component[l] = k;
for(auto &i : graph[l])
fill_from(graph,visited,component,i,k);
}

Тогда мы закончили с задачей, но это еще не самое быстрое решение.

Более быстрый алгоритм

Чтобы получить еще быстрее, мы должны избавиться от рекурсии, и нам не нужен график впоследствии, используйте std::forward_list<int> для стека. Вставьте первую вершину в стек. Затем вытолкните одну вершину, установите ее компонент на k. От себя все его соседи в стек, затем удалите соседей. Другими словами, добавьте список соседей в стек (очень быстрая операция). Повторяйте до тех пор, пока стек не станет пустым.

Таким образом, мы не будем делать бесконечный цикл, потому что если мы вернемся к той же вершине, у нее не будет соседей, и мы уже посетили их. Поэтому нет необходимости в посещаемом векторе. Мы можем установить элемент вектора компонента несколько раз, но всегда на одно и то же значение, так зачем проверять его?

Однако, если у нас нет посещенного вектора, тогда будет сложнее искать другую вершину, которую мы не посетили. Хотя мы могли бы искать некоторую вершину в графе, у которой все еще есть соседи, есть лучшее решение.

Создайте std::set<int> notvisited(); Для тех точек, которые еще не посещены. Сначала он должен содержать все идентификаторы вершин, а затем каждый раз, когда мы устанавливаем идентификатор компонента, мы пытаемся удалить вершину из notvisited задавать. Мы повторяем получение вершины из набора и запускаем алгоритм fill_from (), пока набор не станет пустым, в то же время у нас будут все идентификаторы компонентов.

ОБНОВЛЕНИЕ: Использование обновленной информации о том, как хранится сетка.

Если у вас нет равных элементов в «списке вершин» (зачем вам), то индекс вершины — это ее позиция в массиве. Таким образом, идентификаторы для вершин сделаны.

Идентификаторы для треугольников или граней находятся в «списке индексов», позвольте мне назвать этот массив int listOfIndices[];для j-й грани связанные с ним вершины listOfIndices[3*j + 0], listOfIndices[3*j + 1] а также listOfIndices[3*j + 2], Чтобы сделать первый вектор, вам нужно сделать следующее:

std::vector<int> vertexToFace(num_of_verteces); //previously n
for(int j = 0; j < num_of_faces; ++j)
{
vertexToFace[listOfIndices[3*j + 0]]=j;
vertexToFace[listOfIndices[3*j + 1]]=j;
vertexToFace[listOfIndices[3*j + 2]]=j;
}

(Алгоритм построения обратной зависимости); Обратите внимание, что в этом случае вам даже не нужен другой faceToVertex массив, потому что у вас уже есть (listOfIndices), вы просто должны индексировать его по-разному (делить на 3 каждый раз).

1

Другие решения

Хорошо, следуя ранее упомянутым пунктам и благодаря Csaba Bálint за начальные подсказки, я нашел решение:

MPUVRegionVertex* uvVertexData = new MPUVRegionVertex[indexCount];

for(int ic = 0; ic < indexCount / 3; ic++)
{
for(int vc = 0; vc < 3; vc++)
{
uvVertexData[3*ic+vc].x = vertexData[indexData[3*ic+vc]].tcx;
uvVertexData[3*ic+vc].y = vertexData[indexData[3*ic+vc]].tcy;
uvVertexData[3*ic+vc].faceId = ic;
}
}

std::set<int> notAssigned;

for(int nv = 0; nv < indexCount; nv++)
{
notAssigned.insert(nv);
}

std::forward_list<int> addedInLastIterationElements;
int currentRegion = 0;

//while there are not assigned vertex
while (notAssigned.size() > 0)
{
//the first not assigned element simulate that was "added" to the current region in the last check
addedInLastIterationElements.push_front(*notAssigned.begin());

//this first has the new current region as it's regionId
uvVertexData[*notAssigned.begin()].regionId = currentRegion;

//and becomes assigned
notAssigned.erase(notAssigned.begin());

do
{
//will store the elements added in the next iteration
std::forward_list<int> newRegionElements;

//iterate not assigned elements
for(int currentElement : notAssigned)
{
//iterate elements added to the current region in the last iteration
for(int currentRegionElement : addedInLastIterationElements)
{
//compare if this vertex belongs to the same region of some of the recently added
if((uvVertexData[currentElement].x == uvVertexData[currentRegionElement].x &&
uvVertexData[currentElement].y == uvVertexData[currentRegionElement].y) ||
(uvVertexData[currentElement].faceId == uvVertexData[currentRegionElement].faceId))
{
//store as an element added this iteration
newRegionElements.push_front(currentElement);

//store the current region
uvVertexData[currentElement].regionId = currentRegion;
}
}
}

//remove new elements from the notAssigned list
for(int assigned : newRegionElements)
{
notAssigned.erase(assigned);
}

//replace the elements added the previous iteration for the last iteration
addedInLastIterationElements = newRegionElements;

//prepare for new elements
newRegionElements.clear();
}
//if there is no match in the last iteration, means all remaining vertex belongs to another region
while(!addedInLastIterationElements.empty());

//next region
currentRegion++;
}

Если рендеринг regionId сгенерирован с помощью таблицы цветов, я получаю желаемый результат (обратите внимание, что цвета варьируются только в 0.1 на канал, но здесь есть 10 разных цветов):

введите описание изображения здесь

Конечно, алгоритм может быть оптимизирован, но он работает за разумное время и использует память, так что пока все в порядке.

0

По вопросам рекламы [email protected]