Как добавить текстуру в грани BufferGeometry.? - PullRequest
6 голосов
/ 02 апреля 2019

Я создал bufferGeometry , который состоит из 5 плоскостей (100x25) с двумя треугольниками в каждой.

enter image description here


function createGeometry() {
  var geometry = new THREE.PlaneGeometry(100, 25, 1);
  return geometry;
}

function createScene() {

    var bufferGeometry = new THREE.BufferGeometry();
    var radius = 125;
    var count = 5;

    var positions = [];
    var normals = [];
    var colors = [];

    var vector = new THREE.Vector3();
    var color = new THREE.Color( 0xffffff );
    var heartGeometry = createGeometry();
    var geometry = new THREE.Geometry();

    var step = 0;
    for ( var i = 1, l = count; i <= l; i ++ ) {

        geometry.copy( heartGeometry );

        const y = i * 30
        geometry.translate(-100, y, 0);

        // color.setHSL( ( i / l ), 1.0, 0.7 );

        geometry.faces.forEach( function ( face ) {
            positions.push( geometry.vertices[ face.a ].x );
            positions.push( geometry.vertices[ face.a ].y );
            positions.push( geometry.vertices[ face.a ].z );
            positions.push( geometry.vertices[ face.b ].x );
            positions.push( geometry.vertices[ face.b ].y );
            positions.push( geometry.vertices[ face.b ].z );
            positions.push( geometry.vertices[ face.c ].x );
            positions.push( geometry.vertices[ face.c ].y );
            positions.push( geometry.vertices[ face.c ].z );

            normals.push( face.normal.x );
            normals.push( face.normal.y );
            normals.push( face.normal.z );
            normals.push( face.normal.x );
            normals.push( face.normal.y );
            normals.push( face.normal.z );
            normals.push( face.normal.x );
            normals.push( face.normal.y );
            normals.push( face.normal.z );

            colors.push( color.r );
            colors.push( color.g );
            colors.push( color.b );
            colors.push( color.r );
            colors.push( color.g );
            colors.push( color.b );
            colors.push( color.r );
            colors.push( color.g );
            colors.push( color.b );

        });
    }
    bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
    bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
    bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );


    var material = new THREE.MeshBasicMaterial({
        vertexColors: THREE.VertexColors,
        side: THREE.FrontSide
    });

    var mesh = new THREE.Mesh( bufferGeometry, material );
    scene.add( mesh );

}

Теперь вместо окраски каждой плоскости как я могу добавить текст к каждой плоскости . Скажем, я просто хочу отобразить 1,2,3,4,5 в центре каждой плоскости.

Что я знаю, так это то, что нужно сделать, чтобы добавить текстуру.

  • Создание текстуры из холста
  • Измените материал на Shader Material с помощью map:texture
  • Добавить uv s в bufferGeometry.

Но какова связь между uv s и текстурой.

У меня есть функция создания текстур

//not sure for each character or one texture as a single texture
function createTexture(ch){

  var fontSize = 20;
  var c = document.createElement('canvas');
  c.width = 100;
  c.height = 25;
  var ctx = c.getContext('2d');
  ctx.font = fontSize+'px Monospace';
  ctx.fillText(ch, c.width/2, c.height/2);

  var texture = new THREE.Texture(c);
  texture.flipY = false;
  texture.needsUpdate = true;

  return texture;
}

Полный Код ДЕМО

EDIT

Я рассматриваю производительность как высокий приоритет для этого эксперимента. Мы можем добавить каждую сетку для каждого текста, но это увеличит количество мешей на экране и снизит производительность. Я ищу любую идею с одной сеткой, как в моем примере .

Самое близкое, что я нашел, это это , но я не понял точную технику, которую они используют.

1 Ответ

3 голосов
/ 05 апреля 2019

Вы должны скопировать первый ультрафиолетовый канал свойства .faceVertexUvs из THREE.Geometry, аналогично тому, как вы делаете это с координатами вершины и нормальными векторами:

for ( var i = 1, l = count; i <= l; i ++ ) {

    geometry.copy( heartGeometry );

    const y = i * 30
    geometry.translate(-100, y, 0);

    geometry.faces.forEach( function ( face ) {
        let f = [face.a, face.b, face.c];
        for (let i=0; i < 3; ++i) {
            positions.push( ...geometry.vertices[f[i]].toArray() );
            normals.push( ...face.normal.toArray() );
            colors.push( ...color.toArray() );
        }
    } );

    geometry.faceVertexUvs[0].forEach( function ( faceUvs ) {
        for (let i=0; i < 3; ++i) {
            uv.push( ...faceUvs[i].toArray() );
        }
    } );
}

Если вы хотите определить несколько текстур для одной геометрии, то вы должны использовать несколько THREE.Material s для одного THREE.Mesh.

Определите группы (см. .addGroup) для THREE.BufferGeometry. Каждая группа связывает диапазон вершин с материалом.

var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
bufferGeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv, 2 ) );

let materials = []
let v_per_group= positions.length / 3 / count;
for ( var i = 0; i < count; i ++ ) {
    bufferGeometry.addGroup(i * v_per_group, v_per_group, i);

    let material = new THREE.MeshBasicMaterial({ 
        vertexColors: THREE.VertexColors,
        side: THREE.FrontSide,
        map : createTexture("button" + (i+1))
    });
    materials.push(material);
}

var mesh = new THREE.Mesh( bufferGeometry, materials );
scene.add( mesh );  

var camera, scene, renderer, controls, stats;
init();
animate();


function init() {
  renderer = new THREE.WebGLRenderer( { antialias: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera( 45.0, window.innerWidth / window.innerHeight, 100, 1500.0 );
  camera.position.z = 480.0;
  scene.add( camera );
  controls = new THREE.TrackballControls( camera, renderer.domElement );
  controls.minDistance = 100.0;
  controls.maxDistance = 800.0;
  controls.dynamicDampingFactor = 0.1;
  
  scene.add( new THREE.AmbientLight( 0xffffff, 1 ) );
  
  createScene();
  //stats = new Stats();
  //document.body.appendChild( stats.dom );
  window.addEventListener( 'resize', onWindowResize, false );
}

function createGeometry() {
  var geometry = new THREE.PlaneGeometry(100, 25, 1);
  return geometry;
}

function createTexture(ch){

  var fontSize = 20;
  var c = document.createElement('canvas');
  c.width = 128;
  c.height = 32;
  var ctx = c.getContext('2d');
  
  ctx.beginPath();
  ctx.rect(0, 0, 128, 32);
  ctx.fillStyle = "white";
  ctx.fill();

  ctx.fillStyle = "black";
  ctx.font = fontSize+'px Monospace';
  ctx.fillText(ch, 20, 24);

  var texture = new THREE.Texture(c);
  texture.flipY = true;
  texture.needsUpdate = true;

  return texture;
}

function createScene() {
  
  var radius = 125;
  var count = 5;
  
  var vector = new THREE.Vector3();
  var color = new THREE.Color( 0xffffff );
  var heartGeometry = createGeometry();
  var geometry = new THREE.Geometry();
  
  var positions = [];
  var normals = [];
  var colors = [];
  var uv = [];

  for ( var i = 1, l = count; i <= l; i ++ ) {

      geometry.copy( heartGeometry );

      const y = i * 30
      geometry.translate(-100, y, 0);

      geometry.faces.forEach( function ( face ) {
          let f = [face.a, face.b, face.c];
          for (let i=0; i < 3; ++i) {
              positions.push( ...geometry.vertices[f[i]].toArray() );
              normals.push( ...face.normal.toArray() );
              colors.push( ...color.toArray() );
          }
      } );

      geometry.faceVertexUvs[0].forEach( function ( faceUvs ) {
          for (let i=0; i < 3; ++i) {
              uv.push( ...faceUvs[i].toArray() );
          }
      } );
  }

  var bufferGeometry = new THREE.BufferGeometry();
  bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
  bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
  bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
  bufferGeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv, 2 ) );

  let materials = []
  let v_per_group = positions.length / 3 / count;
  for ( var i = 0; i < count; i ++ ) {
      bufferGeometry.addGroup(i * v_per_group, v_per_group, i);
      
      let material = new THREE.MeshBasicMaterial({ 
          vertexColors: THREE.VertexColors,
          side: THREE.FrontSide,
          map : createTexture("button" + (i+1))
      });
      materials.push(material);
  }
      
  var mesh = new THREE.Mesh( bufferGeometry, materials );
  scene.add( mesh );   
}
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
  requestAnimationFrame( animate );
  controls.update();
  //stats.update();
  renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
...