Есть много способов получить что-то похожее, поэтому вы можете выбрать предпочтительный:
Марширующие кубики
Этот алгоритм прост в использовании, но результат всегда наследует блочный «стиль» его. Если вы хотите именно такой вид, используйте его. Если вам нужно что-то более плавное и / или идеальное с точки зрения пикселей, ищите другие способы.
Маршевые лучи и подписанные функции расстояния
Это довольно интересная техника, которая может дать вам много контроля. Вы можете представлять свои базовые части с помощью простого куба / цилиндра / и т.д. уравнения и смешать их вместе с простой математикой.
Здесь вы можете увидеть несколько примеров:
http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
Самое приятное в этом то, что его очень просто настроить, вам даже не нужно объединять базовые части, вы просто отправляете свои данные в средство визуализации. Хуже того, это может усложнить вычисления при рендеринге детали.
Модификации старой школы
Здесь у вас больше всего вариантов, но они также самые сложные. Вы начинаете с базовых частей, которые сами по себе не имеют большого количества данных, поэтому вам, вероятно, следует объединить их в одну сетку с помощью операции CSG Union.
Имея эту сетку, вы можете вычислять соседние данные для ваших примитивов:
- для каждой вершины найдите треугольники, содержащие ее.
- для каждой вершины найти ребра, содержащие ее.
- для каждого ребра найдите треугольники, содержащие его.
и т.д.
С такими данными вы можете делать такие вещи:
- Найдите и вырежьте острую вершину.
- Найдите и порежьте какой-нибудь острый край.
- Переместите вершину, чтобы минимизировать угол между треугольниками / ребрами, которые она создает.
и так далее ...
Есть очень много деталей, которые могут сработать для вас или нет, вам просто нужно проверить некоторые из них, чтобы увидеть, какой из них дает предпочтительные результаты
.
Одна простая вещь, с которой я бы начал:
- Для каждой вершины найти все вершины, связанные с ней любым ребром.
- Вычислить среднее положение всех этих вершин.
- Используйте некоторый альфа-параметр в диапазоне [0,1], чтобы смешать начальное положение вершины с усредненным.
- Реализовать несколько итераций этого алгоритма и добавить для него параметр.
- Поэкспериментируйте с альфой и количеством итераций.
Используя этот способ, у вас также есть две разные фазы: вычисление и рендеринг, поэтому выполнение с анимацией может стать слишком медленным, но просто рендеринг меша будет быстрее, чем в подходе Ray Marching.
Надеюсь, это поможет.
EDIT:
К сожалению, у меня никогда не было такой необходимости, поэтому у меня нет примера кода, но здесь у вас есть псевдокод, который может вам помочь:
У вас есть сетка:
Mesh mesh;
Массив соседей вершин:
Для любого индекса вершины N
, triNeighbors[N]
будет хранить индексы других вершин, связанных ребром
List<HashSet<int>> triNeighbors = new List<HashSet<int>>();
int[] meshTriangles = mesh.triangles;
// iterate vert indices per triangle and store neighbors
for( int i = 0; i < meshTriangles.Length; i += 3 ) {
// three indices making a triangle
int v0 = meshTriangles[i];
int v1 = meshTriangles[i+1];
int v2 = meshTriangles[i+2];
int maxV = Mathf.Max( Mathf.Max( v0, v1 ), v2 );
while( triNeighbors.Count <= maxV )
triNeighbors.Add( new HashSet<int>() );
triNeighbors[v0].Add( v1 );
triNeighbors[v0].Add( v2 );
triNeighbors[v1].Add( v0 );
triNeighbors[v1].Add( v2 );
triNeighbors[v2].Add( v0 );
triNeighbors[v2].Add( v1 );
}
Теперь для любой отдельной вершины с индексом N
вы можете вычислить ее новую усредненную позицию, например:
int counter = 0;
int N = 0;
Vector3 sum = Vector3.zero;
if( triNeighbors.Count > N && triNeighbors[N] != null )
{
foreach( int V in triNeighbors[N] ) {
sum += mesh.vertices[ V ];
counter++;
}
sum /= counter;
}
В этом коде могут быть некоторые ошибки, я только что сделал это, но вы должны понять.