Показать сообщение отдельно
Старый 27.05.2012, 14:48   #12
Amatsu
Дэвелопер
 
Аватар для Amatsu
 
Регистрация: 24.07.2008
Сообщений: 1,544
Написано 1,095 полезных сообщений
(для 2,706 пользователей)
Ответ: Наложение текстуры на меш в любом месте, игнорируя UV-швы

Вкратце...
Я сейчас расскажу в случае связки 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-развертке), настраиваем камере ортографический рендер и ВУА ЛЯ
Полученную из рендера текстуру натягиваем на персонажа вместо той, что на нем была до этого.
Копию меша, камеру, квад, объект А и прочий мусор чистим.

Честно, я пытался рассказать доступно...
(Offline)
 
Ответить с цитированием
Эти 3 пользователя(ей) сказали Спасибо Amatsu за это полезное сообщение:
ARA (27.05.2012), Mr_F_ (27.05.2012), pax (27.05.2012)