Несколько UV / текстур для одной сетки в THREE.js - PullRequest
0 голосов
/ 15 мая 2018

У меня есть OBJ, который использует четыре текстуры. UV, определенные в файле, находятся в диапазоне от (0, 0) до (2, 2), так что (0.5, 0.5) относится к координате в первой текстуре, (0.5, 1.5) - координата UV во второй текстуре, (1.5, 0.5) - это координата в третьей текстуре, а (1.5, 1.5) - это координата в последней текстуре.

У меня уже есть правильная геометрия или объект three.js. Однако теперь мне нужно иметь возможность применять правильные карты текстур к этим объектам.

В коде:

У меня есть THREE.Mesh с правильной геометрией (с координатами UV, такими, что U = [0, 2], V = [0, 2]) и фиктивный материал-заполнитель. В настоящее время я загружаю одну текстуру, например, так:

var texture = new THREE.TextureLoader().load('tex_u1_v1.png', function() {
    object.material.map = texture;
    object.material.map.needsUpdate = true;
});

Как и ожидалось, одна четверть меша текстурирована правильно. У меня есть еще три файла текстуры, tex_u1_v2.png, tex_u2_v1.png и tex_u2_v2.png. Я хочу иметь возможность применять эти текстуры также и к object (сетка THREE.js), чтобы в каждом сетке была текстура для каждого действительного UV.

Однако я не знаю, как добавить несколько материалов в object после его создания. Более того, я не знаю, как указать мешу, что, например, tex_u1_v2.png следует использовать для UV в диапазоне (U = [0, 2], V = [1, 2]).

Ответы [ 3 ]

0 голосов
/ 19 мая 2018

У меня есть ТРИ. Меш с правильной геометрией (с координатами UV, такими, что U = [0, 2], V = [0, 2])

Ваше понимание«правильный» в этом случае может быть неправильным.Давайте рассмотрим тетраэдр и отобразим его, получив 4 треугольника.Давайте поместим каждый треугольник в его собственный сектор в УФ-пространстве.Четыре квадранта будут:

02--12--22
 | B | C |
01--11--21
 | A | D |
00--10--20

Я не совсем уверен, как передать это лучше, но поиск текстуры всегда выполняется в квадрате А (0011), если это имеет смысл.Как только значение опускается ниже 0 или выше 1, оно просто оборачивается и ищет то же место.Итак, все 4 треугольника здесь (ABCD) фактически перекрываются.Там нет текстуры за пределами этого диапазона.Вы либо зажимаете крайний пиксель, либо оборачиваетесь (или, возможно, зеркалируете).

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

Этого также можно достичь с помощью другого ответа, используя массив материалов и группы настроек.Это все, что вам нужно.Когда он рендерит сетку, у которой все uvs находятся в 1,1,2,2, это будет точно так же, как если бы они были в 0,0,1,1.

0 голосов
/ 23 мая 2018

@ Ответ Джейва выглядит довольно хорошо для меня, но если вы хотите вместо этого пойти по маршруту ShaderMaterial, вот как вы это сделаете:

// Make the material
var material = new new THREE.ShaderMaterial({
      uniforms: {
        u_tex1: {value: null},
        u_tex2: {value: null},
        u_tex3: {value: null},
        u_tex4: {value: null},
      },
      vertexShader: `
        varying vec2 v_uv;
        varying float v_textureIndex;
        void main() {
          // This maps the uvs you mentioned to [0, 1, 2, 3]
          v_textureIndex = step(0.5, uv.x) + step(0.5, uv.y) * 2.0;
          v_uv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        varying vec2 v_uv;
        varying float v_textureIndex;
        uniform sampler2D u_tex1;
        uniform sampler2D u_tex2;
        uniform sampler2D u_tex3;
        uniform sampler2D u_tex4;
        void main() {
          vec4 color = texture2D(u_tex1, v_uv);
          // These lines make sure you get the right texture
          color = mix(color, texture2D(u_tex2, v_uv), step(0.5, v_textureIndex));
          color = mix(color, texture2D(u_tex3, v_uv), step(1.5, v_textureIndex));
          color = mix(color, texture2D(u_tex4, v_uv), step(2.5, v_textureIndex));
          gl_FragColor = color;
        }
      `,
    });

var texture1 = new THREE.TextureLoader().load('tex_u1_v1.png', function() { material.uniforms.u_tex1.value = texture1 });
var texture2 = new THREE.TextureLoader().load('tex_u2_v1.png', function() { material.uniforms.u_tex2.value = texture2 });
var texture3 = new THREE.TextureLoader().load('tex_u1_v2.png', function() { material.uniforms.u_tex3.value = texture3 });
var texture4 = new THREE.TextureLoader().load('tex_u2_v2.png', function() { material.uniforms.u_tex4.value = texture4 });

Это немного неэффективно, так как выделает 4 образца текстуры, но очень гибок.

0 голосов
/ 17 мая 2018

Стандартные материалы в Three будут принимать только один текстурный объект для различных map -параметров (а текстурные объекты будут содержать только одно изображение), поэтому для использовать несколько текстур на вашем объекте, вам придется использовать несколько материалов или создать свой собственный материал с несколькими текстурами . Если у вас есть опыт программирования шейдеров, вы, вероятно, получите лучшую производительность с последним подходом (при условии, что у вас достаточно видеопамяти для больших текстур), поскольку вы можете рисовать всю сетку за один вызов отрисовки без необходимости загружать новые шейдеры или текстуры.

Для создания собственного шейдера вы можете использовать ShaderMaterial или RawShaderMaterial, присвоить ему одну текстуру униформу для каждая текстура вам понадобится (четыре в вашем случае), а затем в коде шейдера выберите правильную для выборки в зависимости от координат.

Чтобы заставить объект использовать более одного материала , вы можете установить свойство material для массива материалов (либо во время создания с помощью параметра конструктора, либо просто замените его вручную на более позднем этапе).

const myMaterials = [tex1Material, tex2Material, tex3Material, tex4Material];
const myMesh = new THREE.Mesh(myGeometry, myMaterials);
//Or:
myMesh.materials = myMaterials;

Затем, чтобы различные части вашей сетки использовали соответствующие материалы, вы должны будете создать groups, если это BufferGeometry; или установите materialIndex граней, если вы используете Geometry. Индекс материала (как в группе, так и на поверхности) - это индекс материала в массиве mesh.material, показанном выше.

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

  • Вероятно, самый простой способ получить правильные координаты уф для текстуры должны были бы просто держать каждую часть в интервале [0,1]. поскольку каждая часть сетки использует уникальный материал, который вам не нужно беспокоиться о перекрывающихся координатах.

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

  • Установите текстурное обёртывание на THREE.RepeatWrapping:

    myTexture.wrapS = THREE.RepeatWrapping;
    myTexture.wrapT = THREE.RepeatWrapping;
    

    Это заставит текстуру повторяться за пределы стандартного [0-1] ультрафиолетового интервала.

  • Другой способ - использовать свойство текстуры offset для вставьте его обратно в интервал [0-1]. Для текстуры, которая будет помещена в интервал u [0,1], v [1,2] вы бы установили смещение v-координаты -1:

    myTexture.offset = new THREE.Vector2(0, -1);
    

Вот ссылка на jsfiddle, демонстрирующую эти методы: https://jsfiddle.net/xfehvmb4/

...