0

I have a blender model that has some shape keys on it. I export this model and use it in threejs. Lets say this is a cube. Then I create Three.Points that draws smalls spheres on the vertices of this model (i.e Cube).

var cube = loadGLTFFromBlender(gltfFile); // This loads the model
var points = new THREE.Points(cube.geometry, pointMaterial); //got my points

Now using the morphTargets, I animate the cube. So far so good.

//Some code that uses the shapee keys 
cube.morphTargetInfluences[0] = 0.2; // <-- I animate this value

but I was expecting the points will also morph/animate since the points are using the same Geometry. But does not seem to work out of box.

Some Googling suggested that morphing happens at the GPU level and the geometry data in object, is not really modified. In one of the StackOverflow questions(Three.js: Get updated vertices with morph targets), @WestLangley suggested doing the same Geometry updates on the CPU and then using it.

My questions:

  1. Are there any other options in approaching this problem.
  2. The CPU morph calculation seems slower and I am trying to see if there is any other way to keep my Three.Points in sync with the morphed cube? The cube animated with morphtargets and I am expecting the Three.Points to do the same.

I guess #1 is not really a option if the shape key animations in blender are more involved?

Ideally, something like the following will help:

cube.morphTargetInfluences[0] = 0.2; // <-- I animate this value
var morphedGeometry = getMorphedGeometry(cube);
syncPointsGeometry(morphedGeometry); 
Ravi Gidwani
  • 198
  • 12

1 Answers1

2

Answering my own question, incase this is helpful to others in future.

I have able to find couple of solutions:

  1. CPU based (Slow) - using a function to copy the morphs into the positions

        function updatePointsGeometry(points,weight) {
            if (!points.geometry.attributes.morphTarget0) {
                return;
            }
            for (var i = 0; i < points.geometry.attributes.position.count; i++) {
                points.geometry.attributes.position.array[i+0] =  weight * points.geometry.attributes.morphTarget0.array[i+0];
                points.geometry.attributes.position.array[i+1] =  weight * points.geometry.attributes.morphTarget0.array[i+1];
                points.geometry.attributes.position.array[i+2] =  weight * points.geometry.attributes.morphTarget0.array[i+2];
            }
            points.geometry.attributes.position.needsUpdate = true;
        }
    
  2. GPU based (Recommended) - using a custom shader that uses the morphtargets and morphinfluence to change the points

            var shaderPoint = THREE.ShaderLib.points;
            var uniforms = THREE.UniformsUtils.clone(shaderPoint.uniforms);
            uniforms.map.value = imageTexture;
            uniforms.size.value = 10;
            //Custom Shader
            customPointMaterial = new THREE.ShaderMaterial({
                uniforms: uniforms,
                morphTargets: true,
                defines: {
                    USE_MAP: ""
                },
                transparent: false,
                opacity: 1,
                alphaTest: 0.5,
                fog: false,
                vertexShader: document.getElementById( 'vertexShader' ).textContent,
                fragmentShader: shaderPoint.fragmentShader
            });
            //copy over the morph data from original geometry to the points
            myPoints = new THREE.Points(mesh.geometry, customPointMaterial);
            myPoints.morphTargetInfluences = mesh.morphTargetInfluences;
            myPoints.morphTargetDictionary = mesh.morphTargetDictionary;
    

And here is the vertex Shader

    uniform float size;
    uniform float scale;
    uniform float morphTargetInfluences[ 4 ];

    #include <common>
    #include <color_pars_vertex>
    #include <fog_pars_vertex>
    #include <shadowmap_pars_vertex>
    #include <logdepthbuf_pars_vertex>
    #include <clipping_planes_pars_vertex>
    void main() {
        #include <color_vertex>
        #include <begin_vertex>
        #include <project_vertex>
        #ifdef USE_SIZEATTENUATION
            gl_PointSize = size * ( scale / - mvPosition.z );
        #else
            gl_PointSize = size;
        #endif
        vec3 morphed = vec3( 0.0 , 0.0 , 0.0 );
        morphed += ( morphTarget0 - position ) * morphTargetInfluences[0];
        morphed += position;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( morphed, 1.0 );
        #include <logdepthbuf_vertex>
        #include <clipping_planes_vertex>
        #include <worldpos_vertex>
        #include <shadowmap_vertex>
        #include <fog_vertex>
    }
Ravi Gidwani
  • 198
  • 12
  • 1
    `PointsMaterial` now [supports](https://threejs.org/examples/webgl_morphtargets_sphere.html) morph targets. – WestLangley May 31 '18 at 02:26
  • @WestLangley This only works for points? I have a base mesh and on the surface another small mesh. How can I move the smaller mesh so that it remains on the surface the base mesh when the base mesh is morphed? – Aniket Betkikar Feb 11 '19 at 18:28