如何向BufferGeometry面添加纹理。

问题描述 投票:6回答:1

我创建了一个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。

我所知道的是要添加纹理必须做以下事情。

  • 从画布生成纹理
  • 使用map:texture将材质更改为着色器材质
  • uvs添加到bufferGeometry。

但是uvs和纹理之间的关系是什么。

我有纹理创建功能

//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;
}

完整的DEMO Code

编辑

我认为性能是这项实验的重中之重。我们可以为每个文本添加每个网格,但这会增加屏幕中的网格数量并降低性能。我正在寻找像我的例子中的单个网格的任何想法。

我找到的最接近的是this,但我不明白他们使用的确切技术。

javascript three.js texture-mapping
1个回答
3
投票

你要从.faceVertexUvs复制THREE.Geometry属性的第一个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() );
        }
    } );
}

如果你想为一个几何定义多个纹理,那么你必须为一个THREE.Material使用多个THREE.Meshs。

.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>
© www.soinside.com 2019 - 2024. All rights reserved.