Instancing Point Cloud

Hi all,

I was trying to use instanced buffergeometry to instance a pointcloud, but hasn’t really been successful, if you guys could give me some help.

Basically I have an existing point cloud created using buffergeometry and Point:

var geometry = new THREE.BufferGeometry();
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
geometry.setAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
geometry.setAttribute( 'alpha', new THREE.BufferAttribute( alphas, 1 ) );
var System = new THREE.Points( geometry, shaderMaterial );
scene.add( System );

and I want to create some exact copies of it at different point in space, so basically just with different location offset, and not rotation/color change.

I tried to use instanced buffer geometry but it didn’t work:

var geometry = new THREE.InstancedBufferGeometry();
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
geometry.setAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
geometry.setAttribute( 'alpha', new THREE.BufferAttribute( alphas, 1 ) );
const sumDisp = new Float32Array(sumDisplacement);

geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(sumDisp, 3 ));
var System = new THREE.Mesh( geometry, shaderMaterial );
System.frustumCulled = false;
scene.add( System );

Please help me if you have any suggestions

the material/ shader I created is

export var uniforms = {
    color:     { value: new THREE.Color( 0xffffff ) },
    texture:   { value: new THREE.TextureLoader().load( "textures/sprites/disc.png" ) }
};

export var shaderMaterial = new THREE.RawShaderMaterial( {
    uniforms:       uniforms,
    vertexShader:   `
    precision highp float;
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    attribute vec3 position;
    attribute float size;
    attribute vec3 customColor;
    attribute vec3 offset;
    attribute float alpha;
    varying float vAlpha;
    varying vec3 vColor;

    void main() {
      vColor = customColor;
      vAlpha = alpha;
      vec3 newPosition = position + offset;
      vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );
      gl_PointSize = size * ( 300.0 / -mvPosition.z );
      gl_Position = projectionMatrix * mvPosition;
    }`,

    fragmentShader: `
    precision highp float;
    uniform vec3 color;
    uniform sampler2D texture;
    varying vec3 vColor;
    varying float vAlpha;
    void main() {
    gl_FragColor = vec4( color * vColor, vAlpha );
    gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
    }`,
    blending:       THREE.AdditiveBlending,
    depthTest:      false,
    transparent:    true
});

Any chance you could properly format the code in your post? It’s really hard to read.

Oops, sorry about that, should be good now

any ideas guys?

What exactly goes wrong? Do you see any error messages or warnings?

Sorry, this topic was overlooked. Do you mind sharing your code as a live example? Or maybe as a github repo? I can then have a look at app and debug if necessary.

Thanks a lot! I’ll prepare a JSFiddle example

@Mugen87 I have just created examples, could you take a look? thanks a lot!

without instancing: https://jsfiddle.net/08ysb1g4/
with instancing (not working): https://jsfiddle.net/vf29uc86/

I was basically trying to create the 2 exact copies of the point cloud with certain offset (-2000, 0, 0) and (2000, 0, 0). it doesn’t give me any error, but just not showing anything

1 Like

Update fiddle: https://jsfiddle.net/h9fnx3sy/

You’ve tried to render a mesh instead of a point cloud :wink:

2 Likes

@Mugen87 Thanks a lot! it works like a charm now :slight_smile:
A quick follow up question if you don’t mind: what it I would like to dynamically add new instance?
I tried to reset the offset attribute, but it didn’t seem to work. I created an example here: https://jsfiddle.net/mprj6bvg/3/
where it’s trying to add new instance on mouse down

Hi!

Maybe a better approach is to use a maximum value of instances you can have (approximately).
In couple with .maxInstancedCount.

Roughly, it will be something like that: https://jsfiddle.net/prisoner849/xfoLrnej/

var clickCounter = 0;
var maxInstances = 10;
...
    sumDisplacement = new Float32Array(maxInstances * 3);
    sumDisplacement.fill(0);
    sumDisplacement[0] = -2000; // x-coord for the first instance
...
    geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(sumDisplacement, 3 ));
    geometry.maxInstancedCount = 1;
...
function onMouseDown() {
	
  clickCounter++;
  if (clickCounter < maxInstances){
    newPosition = newPosition + 2000;
    sumDisplacement[clickCounter * 3] = newPosition; // x-coord for current instance
    geometry.maxInstancedCount = clickCounter + 1; // how many instances will be drawn
    geometry.attributes.offset.needsUpdate = true;
  }
  
}

1 Like

@prisoner849 Thanks a lot for your solution! it certainly works, but I’m a little concerned that would it be inefficient if the max count is large?
say the max possible number of instance is 1000, but usually we only need 1-50. Maybe In this way GPU would always have to process 1000 instances causing unnecessary performance lost? Is it possible to change the length of the offset attribute?
Thanks again!

I am going through the code sample. I see 2 point clouds rendered. I guess that was the ask. But what if I want to render just one?