Вкратце...
Я сейчас расскажу в случае связки C# + Unity3D
Ну, для начала на старте программы я обрабатываю развертку меша, на который буду накладывать текстуры вот этим методом
static Vector2[] CorrectUV(Mesh mesh, float scale)
{
if (mesh != null)
{
var meshTriangles = mesh.triangles;
var meshUV = mesh.uv;
var borderNormals = new Vector2[meshUV.Length];
var borderAngle = new float[meshUV.Length];
for (var i = 0; i < meshTriangles.Length; i += 3)
{
var foundRib = new bool[3];
for (var j = 0; j < meshTriangles.Length; j += 3)
{
if (i == j) continue;
var foundVert = new bool[3];
for (var k = 0; k < 3; k++)
{
if (meshTriangles[i + k] == meshTriangles[j] || meshTriangles[i + k] == meshTriangles[j + 1] || meshTriangles[i + k] == meshTriangles[j + 2]) foundVert[k] = true;
}
if (foundVert[0] && foundVert[1]) foundRib[0] = true;
if (foundVert[1] && foundVert[2]) foundRib[1] = true;
if (foundVert[2] && foundVert[0]) foundRib[2] = true;
}
if (!foundRib[0])
{
var normal = Perpendicular(meshUV[meshTriangles[i]], meshUV[meshTriangles[i + 1]], meshUV[meshTriangles[i + 2]]).normalized;
borderAngle[meshTriangles[i]] = Vector2.Angle(borderNormals[meshTriangles[i]], normal);
borderNormals[meshTriangles[i]] += normal;
borderAngle[meshTriangles[i + 1]] = Vector2.Angle(borderNormals[meshTriangles[i + 1]], normal);
borderNormals[meshTriangles[i + 1]] += normal;
}
if (!foundRib[1])
{
var normal = Perpendicular(meshUV[meshTriangles[i + 1]], meshUV[meshTriangles[i + 2]], meshUV[meshTriangles[i]]).normalized;
borderAngle[meshTriangles[i + 1]] = Vector2.Angle(borderNormals[meshTriangles[i + 1]], normal);
borderNormals[meshTriangles[i + 1]] += normal;
borderAngle[meshTriangles[i + 2]] = Vector2.Angle(borderNormals[meshTriangles[i + 2]], normal);
borderNormals[meshTriangles[i + 2]] += normal;
}
if (!foundRib[2])
{
var normal = Perpendicular(meshUV[meshTriangles[i + 2]], meshUV[meshTriangles[i]], meshUV[meshTriangles[i + 1]]).normalized;
borderAngle[meshTriangles[i + 2]] = Vector2.Angle(borderNormals[meshTriangles[i + 2]], normal);
borderNormals[meshTriangles[i + 2]] += normal;
borderAngle[meshTriangles[i]] = Vector2.Angle(borderNormals[meshTriangles[i]], normal);
borderNormals[meshTriangles[i]] += normal;
}
}
var updatedUV = new Vector2[meshUV.Length];
for (var i = 0; i < meshUV.Length; i++)
{
updatedUV[i] = meshUV[i] + borderNormals[i] * (0.5F + borderAngle[i] / 180F) * scale;
}
return updatedUV;
}
return null;
}
В результате я получаю UV-развертку меша, грани которой расширены как я рисовал выше. Но эта UV-развертка представлена лишь как массив 2d-векторов, к исходному мешу я ее не применяю. Эта операция выполняется один раз на старте
Далее, я кликаю по мешу, нахожу точку клика, а так же вектор (например от точки клика до камеры или же беру нормаль клика). На "вершине" этого найденного вектора вешаю GameObject, заставляю его "смотреть" на точку клика. Назовем этот объект А
Затем делаю копию оригинального меша (
внимание, не забудьте повесить на копию меша материал с шейдером, учитывающим вертексную альфу, шейдер представлен здесь ) и проецирую все координаты вертексов этой копии на плоскость, принадлежащую space'у А. То есть просто перевожу координаты вершин копии меша из мировых в координатную систему А, затем беру у этих вершин только X и Y составляющие, игнорируя Z, и записываю их в массив двухмерных векторов, который (массив) потом присваиваю UV-развертке копии исходного меша.
Затем я накладываю на копию меша текстуру с альфой, в clamp-режиме. Текстура накладывается на меш проекцией. Но чтобы она не размазывалась по всей поверхности, я делаю у копии меша повершинно три проверки. Сначала я проверяю дистанцию каждого вертекса копии меша относительно точки клика по исходному мешу. Если эта дистанция больше какого-то числа - я текущему вертексу вешаю цвет (1, 1, 1, 0) (то есть белый с нулевой альфой). Если же вертекс проходит проверку - я проверяю, что его угол между его нормалью и вектором клика (на конце которого мы повесили А) не больше, допустим, 90 градусов, если больше - так же ставлю вертексу нулевую альфу. Ну и если вертекс и эту проверку прошел - кидаю из А до него RayCast. Если луч не дошел до вертекса - правильно, вершину так же загоняем в нулевую альфу.
Тут может возникнуть вопрос, что при большом числе вертексом рейкасты могут замедлить проверку - не замедляют. Физиксовый рейкаст очень быстр
Все, наша текстура наложилась именно там, где вы кликнули и под тем углом, как вы захотели.
Теперь можно водя по поверхности исходного меша мышкой менять ему наложение (копировать заново меш не нужно, можно начать сразу с пункта установки в нужной точки объекта А и проэцирования в его координатную систему вершин копии меша)
Вторая фаза - запекание нашей текстуры из копии меша в текстуру оригинального меша.
Я передаю в соответствующий метод указатель на оригинальный меш, на нашу затекстуренную копию меша, а так же на тот массив 2d-векторов, представляющий собой "поправленную" UV-развертку оригинала.
Далее, я беру нашу поправленную UV-развертку (преимущество моего способа - эта развертка может быть практически любой). Затем я создаю новый меш, в который добавляю информацию о геометрии следующим образом:
каждый полигон из моей копии меша (захватив при этом координаты развертки этого полигона, которые мы нашли, когда накладывали текстуру на него) переносился на 2d-плоскость (плоскость естессно в 3д пространстве) в поправленные (!) UV-координаты соответствующего полигона из оригинального меша (мы ведь делали копию оригинального меша, так что число полигонов у нас совпадает и на меше, и на UV-развертке).
Этим хитрым способом мы переводим развертку копии меша (по сути разрезаем копию меша на полигоны и помещаем каждый из них в соответствующие координаты на UV) в UV-развертку оригинального меша.
Поле этого осталось лишь создать условия для рендера этой фигуры в текстуру для оригинального меша. Ставим перед полученной "сплюснутой" копией меша камеру, позади создаем из дух полигонов квад и натягиваем на него текстуру, которая у нас уже была на оригинальном меше (например текстуру тела, которую вы успели сделать герою в фотошопе по его UV-развертке), настраиваем камере ортографический рендер и ВУА ЛЯ
Полученную из рендера текстуру натягиваем на персонажа вместо той, что на нем была до этого.
Копию меша, камеру, квад, объект А и прочий мусор чистим.
Честно, я пытался рассказать доступно...