threejs instance multiple objects with different sizes and positions

感情迁移 提交于 2019-12-11 16:16:17

问题


just yesterday i started to tried the InstancedBufferGeometry method because i want to render thousand of objects with a good performance. In my case i want to instance cube geometries with different heights of each other. At moment i tried this code

AFRAME.registerComponent('instancing', {
  schema: {
    count: {type: 'int', default: 10000}
  },

  init: function () {
    this.count = this.data.count;
    this.model = null;
  },

  update: function () {
    if (this.model !== null) { return; }

    var data = this.data;
    var el = this.el;

    var count = this.count;

    var geometry = new THREE.InstancedBufferGeometry();
    geometry.copy(new THREE.BoxBufferGeometry(10, 5, 10));

    var translateArray = new Float32Array(count*3);
    var vectorArray = new Float32Array(count*3);
    var colorArray = new Float32Array(count*3);
    var vertices = new Float32Array(count * 24);

    for(var i = 0; i < count; i++){
      var y = Math.floor((Math.random() * 200) + 50);
      vertices[i*3+0] = 10;
      vertices[i*3+1] = y;
      vertices[i*3+2] = 10;

      vertices[i*24+3] = 10;
      vertices[i*24+4] = y;
      vertices[i*24+5] = -10;

      vertices[i*24+6] = 10;
      vertices[i*24+7] = -y;
      vertices[i*24+8] = 10;

      vertices[i*24+9] = 10;
      vertices[i*24+10] = -y;
      vertices[i*24+11] = -10;

      vertices[i*24+12] = -10;
      vertices[i*24+13] = y;
      vertices[i*24+14] = -10;

      vertices[i*24+15] = -10;
      vertices[i*24+16] = y;
      vertices[i*24+17] = 10;

      vertices[i*24+18] = -10;
      vertices[i*24+19] = -y;
      vertices[i*24+20] = -10;

      vertices[i*24+21] = -10;
      vertices[i*24+22] = -y;
      vertices[i*24+23] = 10;
   }

    for (var i = 0; i < count; i++) {
      translateArray[i*3+0] = (Math.random() - 0.5) * 100.0;
      translateArray[i*3+1] = (Math.random() - 0.5) * 100.0;
      translateArray[i*3+2] = (Math.random() - 0.5) * 100.0;
    }

    for (var i = 0; i < count; i++) {
      vectorArray[i*3+0] = (Math.random() - 0.5) * 100.0;
      vectorArray[i*3+1] = (Math.random() + 1.5) * 100.0;
      vectorArray[i*3+2] = (Math.random() - 0.5) * 100.0;
    }

    for (var i = 0; i < count; i++) {
      colorArray[i*3+0] = Math.random();
      colorArray[i*3+1] = Math.random();
      colorArray[i*3+2] = Math.random();
    }

    geometry.addAttribute('position', new THREE.InstancedBufferAttribute(vertices, 24, 1));
    geometry.addAttribute('translate', new THREE.InstancedBufferAttribute(translateArray, 3, 1));
    geometry.addAttribute('vector', new THREE.InstancedBufferAttribute(vectorArray, 3, 1));
    geometry.addAttribute('color', new THREE.InstancedBufferAttribute(colorArray, 3, 1));

    var material = new THREE.ShaderMaterial({
      uniforms: {
        time: {value: 0}
      },
      vertexShader: [
        'attribute vec3 translate;',
        'attribute vec3 vector;',
        'attribute vec3 color;',
        'uniform float time;',
        'varying vec3 vColor;',
        'const float g = 9.8 * 1.5;',
        'void main() {',
        '  vec3 offset;',
        '  offset.xz = vector.xz * time;',
        '  offset.y = vector.y * time - 0.5 * g * time * time;',
        '  gl_Position = projectionMatrix * modelViewMatrix * vec4( position + translate + offset, 1.0 );',
        '  vColor = color;',
  '}'
      ].join('\n'),
      fragmentShader: [
        'varying vec3 vColor;',
        'void main() {',
        '  gl_FragColor = vec4( vColor, 1.0 );',
        '}'
      ].join('\n')
    });

    var mesh = new THREE.Mesh(geometry, material);

    this.model = mesh;
    el.setObject3D('mesh', mesh);
    el.emit('model-loaded', {format:'mesh', model: mesh});
  },

  tick: function(time, delta) {
    if (this.model === null) { return; }

    var mesh = this.model;
    mesh.material.uniforms.time.value = (mesh.material.uniforms.time.value + delta / 1000) % 30.0;
  }
});

but i have this error

[.Offscreen-For-WebGL-0x17d96d74a000]GL ERROR :GL_INVALID_VALUE : glVertexAttribPointer: size GL_INVALID_VALUE (index):1 [.Offscreen-For-WebGL-0x17d96d74a000]GL ERROR :GL_INVALID_OPERATION : glDrawElementsInstancedANGLE: attached to enabled attrib 1 : no buffer

im a recruit to threejs low level functions. i started with aframe but unfortunately i see that in my case the normal use of this framework is not sufficent to have good performances.

my goal is to have multiple cubes with different sizes in different positions. any suggestion? thanks in advance

with TheJim solution.


回答1:


First, position should not be re-defined. ShaderMaterial injects common attributes, including position, and your definition conflicts with that. If you'd like to define everything yourself, look into RawShaderMaterial.

Second, remember, you're defining the vertices, indices, and normals for ONE cube, then using shaders/instancing to manipulate the rest. Only instance-specific data should be defined as InstancedBufferAttributes.

Take a look at this example:

var cubeGeo = new THREE.InstancedBufferGeometry().copy(new THREE.BoxBufferGeometry(10, 10, 10));
//cubeGeo.maxInstancedCount = 8;

cubeGeo.addAttribute("cubePos", new THREE.InstancedBufferAttribute(new Float32Array([
  25, 25, 25,
  25, 25, -25, -25, 25, 25, -25, 25, -25,
  25, -25, 25,
  25, -25, -25, -25, -25, 25, -25, -25, -25
]), 3, 1));

var vertexShader = [
  "precision highp float;",
  "",
  "uniform mat4 modelViewMatrix;",
  "uniform mat4 projectionMatrix;",
  "",
  "attribute vec3 position;",
  "attribute vec3 cubePos;",
  "",
  "void main() {",
  "",
  " gl_Position = projectionMatrix * modelViewMatrix * vec4( cubePos + position, 1.0 );",
  "",
  "}"
].join("\n");
var fragmentShader = [
  "precision highp float;",
  "",
  "void main() {",
  "",
  " gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);",
  "",
  "}"
].join("\n");

var mat = new THREE.RawShaderMaterial({
  uniforms: {},
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
  transparent: true
});

var mesh = new THREE.Mesh(cubeGeo, mat);

scene.add(mesh);

Like you, I've created a cube (just one cube). I then add an InstancedBufferAttribute named cubePos which will change the positions of the cubes (cubePos applies to ALL vertices in this example). This is similar to your translate attribute.

As far as stretching the cubes, it looks like you may be close with your position attribute. It may work as you expect if you simply give it a name that is not built-in, like stretch.

Personally, since it looks like you're stretching it equally in both directions, I'd probably just pass in a single value per instance, and use it to scale the cube in both Y directions.



来源:https://stackoverflow.com/questions/47035184/threejs-instance-multiple-objects-with-different-sizes-and-positions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!