UV-отображение куба в Three.js некорректно соединяет грани

Я успешно применил текстуру изображения к кубу с помощью UV-преобразования (для создания программы просмотра фото-сфер). Хотя текстура идеально совмещена с гранями куба, линии, на которых соединяются грани, видны как тонкие прямые.

Та же проблема не возникает, если плитки текстуры разделяются с помощью Canvas и применяются к кубу с помощью MultiMaterial.

На следующем изображении сравниваются результаты двух методов наложения текстуры (щелкните, чтобы увеличить изображение):

УФ-отображение против MultiTexture

Живой пример можно найти на CodePen.io

Изображение, используемое для текстуры, можно найти здесь

Вот код, который выполняет отображение UV (это довольно просто):

function mapCubeUV(geometry, cubeH) {

  // converting all vertices into polar coordinates
  geometry.faceVertexUvs[0] = []; // This clears out any UV mapping that may have already existed on the object

  // walking through all the faces defined by the object
  // ... we need to define a UV map for each of them
  geometry.faces.forEach(function(face) {

    var uvs = [];

    var ids = [ 'a', 'b', 'c'],
        faceSign = face.normal.x+'.'+face.normal.y+'.'+face.normal.z;

    for( var i = 0; i < ids.length; i++ ) {

      // using the point to access the vertice
      var vertexIndex = face[ ids[ i ] ],
          vertex = geometry.vertices[ vertexIndex ],
          tileIx,
          uvY, uvX;

      // face order in the image: West, East, Up, Down, South, North
      switch(faceSign) {
        case '1.0.0': // West
          uvY = vertex.y;
          uvX = -vertex.z;
          tileIx = 0;
          break;
        case '-1.0.0': // East
          uvY = vertex.y;
          uvX = vertex.z;
          tileIx = 1;
          break;
        case '0.1.0': // Up
          uvY = -vertex.z;
          uvX = vertex.x;
          tileIx = 2;
          break;
        case '0.-1.0': // Down
          uvY = vertex.z;
          uvX = vertex.x;
          tileIx = 3;
          break;
        case '0.0.1': // South
          uvY = vertex.y;
          uvX = vertex.x;
          tileIx = 4;
          break;
        case '0.0.-1': // North
          uvY = vertex.y;
          uvX = -vertex.x;
          tileIx = 5;
          break;
      }

      // coordinate values range from [-cubeH/2, +cubeH/2]
      // here we're fixing moving the range to [0, +cubeH]
      uvY = uvY+cubeH/2;
      uvX = uvX+cubeH/2;

      // each UV coordinate represents decimal range [0, +1]
      uvY = uvY/cubeH;
      uvX = uvX/cubeH;

      // since the image contains multiple texture tiles (8 of them = 6 with
      // images + 2 dummy, which were added so that the width is a multiple of 2),
      // [uvX] must be adjusted to point to the part of the image
      // containing current tile
      uvX = (uvX+tileIx)/8;

      uvs.push( new THREE.Vector2( uvX, uvY ) );
    }

    geometry.faceVertexUvs[ 0 ].push( uvs );
  });

  geometry.uvsNeedUpdate = true;

  return(geometry);
}

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

Face VerticeA   VerticeB   VerticeC 
 0: (    0,1), (    0,0), (0.125,1)
 1: (    0,0), (0.125,0), (0.125,1)
 2: (0.125,1), (0.125,0), ( 0.25,1)
 3: (0.125,0), ( 0.25,0), ( 0.25,1)
 4: ( 0.25,1), ( 0.25,0), (0.375,1)
 5: ( 0.25,0), (0.375,0), (0.375,1)
 6: (0.375,1), (0.375,0), (  0.5,1)
 7: (0.375,0), (  0.5,0), (  0.5,1)
 8: (  0.5,1), (  0.5,0), (0.625,1)
 9: (  0.5,0), (0.625,0), (0.625,1)
10: (0.625,1), (0.625,0), ( 0.75,1)
11: (0.625,0), ( 0.75,0), ( 0.75,1)

Я что-то делаю не так или есть какие-то проблемы с Three.js?

P.S. тест был основан на примере, найденном на веб-сайте Three.js

P.P.S

очень похожий вопрос можно найти ЗДЕСЬ (хотя это не касается ручного расчета UV-карты)


person knee-cola    schedule 13.04.2017    source источник


Ответы (1)


Поработав над проблемой некоторое время и хорошо выспавшись, я обнаружил, что проблема вызвана пикселями из соседней плитки, которые просачиваются по краям лица. Этого не происходит, изображение разбивается на более мелкие независимые части (т.е. через холст), поскольку пиксели из соседних плиток не копируются.

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

На изображении, используемом в примере из вопроса выше, плитки расположены следующим образом: Запад-Восток-Вверх-Внизу-Юг-Север. Правильный порядок: восток-юг-запад-север + верх + низ.

По-прежнему существует проблема с плитками по краям этой последовательности, чьи края все еще примыкают к неправильной плитке: * Север - его правый край должен быть соединен с Востоком * Верх - его левый край должен быть соединен с Востоком, а правый край с Западом * Нижний - левый край с Западом, правый край с Востоком

Чтобы исправить это, нам нужно будет оставить пространство между Севером, Верхом и Низом. Затем мы можем скопировать и вставить узкую вертикальную полосу с краев плиток Восток и Запад и вставить их рядом с краями Север, Верх и Низ. Это предотвратит просвечивание неправильных пикселей.

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

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

Еще две вещи, о которых необходимо позаботиться:

  • размеры изображения, которые должны быть степенью двойки (ширина и высота) ... иначе WebGLRenderer будет жаловаться
  • верхняя и нижняя плитки помещаются в координату X, которая в UV-координатах образует рациональное число с несколькими десятичными знаками, для этого начального пикселя, рассчитанного WebGL, будет номер отверстия (т.е. 4,5 * 1024 для верхнего и 6 * 1024 для нижнего)

Живой пример этого решения, в котором сравниваются два подхода, можно найти по адресу CodePen.io

Поскольку плитки были переставлены, мне нужно было исправить функцию отображения UV:

function mapCubeUV_v2(geometry, cubeH) {

  // converting all vertices into polar coordinates
  geometry.faceVertexUvs[0] = []; // This clears out any UV mapping that may have already existed on the object

  // walking through all the faces defined by the object
  // ... we need to define a UV map for each of them
  geometry.faces.forEach(function(face) {

    var uvs = [];

    var ids = [ 'a', 'b', 'c'],
        faceSign = face.normal.x+'.'+face.normal.y+'.'+face.normal.z;

    for( var i = 0; i < ids.length; i++ ) {

      // using the point to access the vertice
      var vertexIndex = face[ ids[ i ] ],
          vertex = geometry.vertices[ vertexIndex ],
          tileIx,
          uvY, uvX;

      // face order in the image: East, South, West, North, Up, Down
      switch(faceSign) {
        case '-1.0.0': // East
          uvY = vertex.y;
          uvX = vertex.z;
          tileIx = 0;
          break;
        case '0.0.1': // South
          uvY = vertex.y;
          uvX = vertex.x;
          tileIx = 1;
          break;
        case '1.0.0': // West
          uvY = vertex.y;
          uvX = -vertex.z;
          tileIx = 2;
          break;
        case '0.0.-1': // North
          uvY = vertex.y;
          uvX = -vertex.x;
          tileIx = 3;
          break;
        case '0.1.0': // Up
          uvY = -vertex.z;
          uvX = vertex.x;
          tileIx = 4.5; // "up" is 1.5 tile width distance from "north"
          break;
        case '0.-1.0': // Down
          uvY = vertex.z;
          uvX = vertex.x;
          tileIx = 6; // "down" if further 1.5 widths distance from "up"
          break;
                     }

      // coordinate values range from [-cubeH/2, +cubeH/2]
      // here we're fixing moving the range to [0, +cubeH]
      uvY = uvY+cubeH/2;
      uvX = uvX+cubeH/2;

      // each UV coordinate represents decimal range [0, +1]
      uvY = uvY/cubeH;
      uvX = uvX/cubeH;

      // since the image contains multiple texture tiles (8 of them),
      // [uvX] must be adjusted to point to the part of the image
      // containing current tile
      uvX = (uvX+tileIx)/8;
      console.log(uvX);

      // if(faceSign!=='1.0.0') {
      //    uvY = uvX = 0;
      // }

      uvs.push( new THREE.Vector2( uvX, uvY ) );
    }

    geometry.faceVertexUvs[ 0 ].push( uvs );
  });

  geometry.uvsNeedUpdate = true;

  return(geometry);
}
person knee-cola    schedule 14.04.2017
comment
Да, я сделал - взгляните здесь: codepen.io/knee-cola/pen/oWgQdN Работает, как ожидалось! - person knee-cola; 14.04.2017