threejs metaballs with dot shader

788 Views Asked by At

I am trying to test a simple dot shader in ThreeJS made by 2pha : https://2pha.com/demos/threejs/shaders/simple_dots.html

It seems to not work properly for metaballs using Marching Cubes : https://threejs.org/examples/webgl_marchingcubes.html. Is this a UV coordinates problem? The ThreeJS version has a enableUvs flag but does not seem to be enough.

Here the shader passed to ShaderMaterial

'polkadots' : {
    uniforms: {

        "amount":{type: "f",value: 5.},
        "radius1":{type: "f",value: 0.3},
        "radius2":{type: "f",value: 0.32},
        "color1":{type:"c",value: new THREE.Color(0xffffff)},
        "color2":{type:"c",value: new THREE.Color(0x000000)},

    },

    vertexShader: [
        "varying vec2 vUv;",


        "void main() {",
            "vUv = uv;",
            "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
            "gl_Position = projectionMatrix * mvPosition;",

        "}",
    ].join( "\n" ),

    fragmentShader: [

        "uniform vec3 color1;",
        "uniform vec3 color2;",
        "uniform float radius1;",
        "uniform float radius2;",
        "uniform float amount;",
        "varying vec2 vUv;",

        "void main(void){",

            "float p = smoothstep(radius1, radius2, length(fract(amount*vUv)-0.5));",
            "vec3 col = mix(color1,color2,vec3(p));",
            "gl_FragColor = vec4(col,1.0);",

        "}",
    ].join( "\n" )
}
1

There are 1 best solutions below

3
On

Yes. Your dot shader is using the UVs to determine where to draw the dots... If your marching cube algo isn't setting the UVs, you'll have to synthesize some yourself... You can do a box unwrapping.. It's not too super complicated but it was fun to figure out.. you loop through the faces of the geometry... figure out which plane the triangle lays on, and get the verts projected onto that uv plane. I took the liberty of writing it up for ya:

    function boxUnwrapUVs(geometry) {
        for (var i = 0; i < geometry.faces.length; i++) {
            var face = geometry.faces[i];
            var faceUVs = geometry.faceVertexUvs[0][i]
            var va = geometry.vertices[geometry.faces[i].a]
            var vb = geometry.vertices[geometry.faces[i].b]
            var vc = geometry.vertices[geometry.faces[i].c]
            var vab = new THREE.Vector3().copy(vb).sub(va)
            var vac = new THREE.Vector3().copy(vc).sub(va)

            //now we have 2 vectors to get the cross product of...
            var vcross = new THREE.Vector3().copy(vab).cross(vac);
            //Find the largest axis of the plane normal...
            vcross.set(Math.abs(vcross.x), Math.abs(vcross.y), Math.abs(vcross.z))
            var majorAxis = vcross.x > vcross.y ? (vcross.x > vcross.z ? 'x' : vcross.y > vcross.z ? 'y' : vcross.y > vcross.z) : vcross.y > vcross.z ? 'y' : 'z'
            //Take the other two axis from the largest axis
            var uAxis = majorAxis == 'x' ? 'y' : majorAxis == 'y' ? 'x' : 'x';
            var vAxis = majorAxis == 'x' ? 'z' : majorAxis == 'y' ? 'z' : 'y';
            faceUVs[0].set(va[uAxis], va[vAxis])
            faceUVs[1].set(vb[uAxis], vb[vAxis])
            faceUVs[2].set(vc[uAxis], vc[vAxis])
        }
        //Tell THREE that our modifications need to be uploaded to the GPU
        geometry.elementsNeedUpdate = geometry.verticesNeedUpdate = true;
    }