2
votes

My question is related to this answered Q from a couple years ago, which I got working: three.js texture across InstanceGeometry

I'm updating my application from r95 to the latest r124.

First I make the InstancedBufferGeometry (Geometry.createBuffered is my own bit that pops out any type of geometry from some JSON):

let bg = Geometry.createBuffered(id, params.instance.geo),
        ig : InstancedBufferGeometry = new InstancedBufferGeometry(),
        mesh;
    
    ig.copy( bg );

THen I set about generating InstancedBufferAttributes from JSON params and assigning them to the IBG:

for(var x = 0; x < xl; x++){
    for ( var y = 0; y < yl; y++) {
        for ( var z = 0; z < zl; z++) {

            ox = ( x * params.instance.arrayPlacement.spacing[0] - disX ) + Math.random() * ( params.instance.arrayPlacement.displacement[1] - params.instance.arrayPlacement.displacement[0] ) + params.instance.arrayPlacement.displacement[0];
            oy = ( y * params.instance.arrayPlacement.spacing[1] - disY ) + Math.random() * ( params.instance.arrayPlacement.displacement[1] - params.instance.arrayPlacement.displacement[0] ) + params.instance.arrayPlacement.displacement[0];
            oz = ( z * params.instance.arrayPlacement.spacing[2] - disZ ) + Math.random() * ( params.instance.arrayPlacement.displacement[1] - params.instance.arrayPlacement.displacement[0] ) + params.instance.arrayPlacement.displacement[0];

            offsets[ count * 3 ] = ox;
            offsets[ ( count * 3 ) + 1 ] = oy;
            offsets[ ( count * 3 ) + 2 ] = oz;
            
            scales[count] = Math.random() * (params.instance.arrayPlacement.scale[1] - params.instance.arrayPlacement.scale[0]) + params.instance.arrayPlacement.scale[0];
            
            orientations[ count * 4 ] = -0.5;
            orientations[ ( count * 4 ) + 1 ] = -0.5;
            orientations[ ( count * 4 ) + 2 ] = -0.5; 
            orientations[ ( count * 4 ) + 3 ] = 0.5;

            uvOffsets[ count * 2 ] = y;  
            uvOffsets[ ( count * 2 ) + 1 ] = x; 

            iCount[ count * 2 ] = 1/yl; 
            iCount[ ( count * 2 ) + 1 ] = 1/xl;

            
            
            count++;
        }
    }
}

let roti : InstancedBufferAttribute = new InstancedBufferAttribute( orientations, 4).setUsage( DynamicDrawUsage ),
    offs : InstancedBufferAttribute = new InstancedBufferAttribute( offsets, 3 ).setUsage( DynamicDrawUsage ),
    uvOffs : InstancedBufferAttribute = new InstancedBufferAttribute( uvOffsets, 2 ).setUsage( DynamicDrawUsage ),
    iC : InstancedBufferAttribute = new InstancedBufferAttribute( iCount, 2 ).setUsage ( DynamicDrawUsage ),
    scas : InstancedBufferAttribute = new InstancedBufferAttribute( scales, 1 ).setUsage( DynamicDrawUsage );


ig.setAttribute( 'offset', offs );
ig.setAttribute( 'uvOffset',  uvOffs);
ig.setAttribute('iCount', iC);
ig.setAttribute( 'iScale', scas );
ig.setAttribute( 'orientation',  roti );

Then, I make a material with a customized shader with uvoffsets and colors. However, when I create a mesh with even a basic 3js material nothing appears. If I inspect the Scene, the Mesh is there, no errorsscreenshot

UPDATE: @Mugen87 is correct, I was still running r115. sorry about that. I'm on r124 now, but I strangely still see maxInstancedCount, as well as instanceCount (as undefined) when I create an IBG. Also, when I use IBG.copy(BoxBufferGeometry), the geometry does not seem to be copied in. I assumed BoxBufferGeometry creates a BufferGeometry? Or is that coming in r125?

another inspect of my InstancedBufferGeometry

1
maxInstancedCount is undefined on your pic. You need to set it with the amount of instances. Just ig.maxInstancedCount = _some_integer_;, after when you set attributes. - prisoner849

1 Answers

3
votes

Your screenshot is misleading since with r124 InstancedBufferGeometry does not have a maxInstanceCount property.

With r117 maxInstancedCount was renamed to just instanceCount. Since its default value is Infinity, there is no need to set it if you want to render all instances in your geometry.

I suggest you use the following code as a template for your own app:

let camera, scene, renderer;

init();
animate();

function init() {

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10);
    camera.position.z = 2;

    scene = new THREE.Scene();

    // geometry

    const vector = new THREE.Vector4();

    const instances = 50000;

    const positions = [];
    const offsets = [];
    const colors = [];
    const orientationsStart = [];
    const orientationsEnd = [];

    positions.push(0.025, -0.025, 0);
    positions.push(-0.025, 0.025, 0);
    positions.push(0, 0, 0.025);

    // instanced attributes

    for (let i = 0; i < instances; i++) {

        // offsets

        offsets.push(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);

        // colors

        colors.push(Math.random(), Math.random(), Math.random(), Math.random());

        // orientation start

        vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
        vector.normalize();

        orientationsStart.push(vector.x, vector.y, vector.z, vector.w);

        // orientation end

        vector.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
        vector.normalize();

        orientationsEnd.push(vector.x, vector.y, vector.z, vector.w);

    }

    const geometry = new THREE.InstancedBufferGeometry();

    geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
    geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3));
    geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 4));
    geometry.setAttribute('orientationStart', new THREE.InstancedBufferAttribute(new Float32Array(orientationsStart), 4));
    geometry.setAttribute('orientationEnd', new THREE.InstancedBufferAttribute(new Float32Array(orientationsEnd), 4));

    // material

    const material = new THREE.RawShaderMaterial({

        uniforms: {
            "time": {
                value: 1.0
            },
            "sineTime": {
                value: 1.0
            }
        },
        vertexShader: document.getElementById('vertexShader').textContent,
        fragmentShader: document.getElementById('fragmentShader').textContent,
        side: THREE.DoubleSide,
        transparent: true

    });

    //

    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    //

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    //

    window.addEventListener('resize', onWindowResize, false);

}

function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

}

//

function animate() {

    requestAnimationFrame(animate);

    render();

}

function render() {

    const time = performance.now();

    const object = scene.children[0];

    object.rotation.y = time * 0.0005;
    object.material.uniforms["time"].value = time * 0.005;
    object.material.uniforms["sineTime"].value = Math.sin(object.material.uniforms["time"].value * 0.05);

    renderer.render(scene, camera);

}
body {
  margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>

<script id="vertexShader" type="x-shader/x-vertex">
        precision highp float;

        uniform float sineTime;

        uniform mat4 modelViewMatrix;
        uniform mat4 projectionMatrix;

        attribute vec3 position;
        attribute vec3 offset;
        attribute vec4 color;
        attribute vec4 orientationStart;
        attribute vec4 orientationEnd;

        varying vec3 vPosition;
        varying vec4 vColor;

        void main(){

            vPosition = offset * max( abs( sineTime * 2.0 + 1.0 ), 0.5 ) + position;
            vec4 orientation = normalize( mix( orientationStart, orientationEnd, sineTime ) );
            vec3 vcV = cross( orientation.xyz, vPosition );
            vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );

            vColor = color;

            gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );

        }

    </script>

    <script id="fragmentShader" type="x-shader/x-fragment">

        precision highp float;

        uniform float time;

        varying vec3 vPosition;
        varying vec4 vColor;

        void main() {

            vec4 color = vec4( vColor );
            color.r += sin( vPosition.x * 10.0 + time ) * 0.5;

            gl_FragColor = color;

        }

    </script>