Search code examples
three.jswebglautodesk-forge

Autodesk Forge Viewer - smoke particle effect in Forge Viewer


I've been trying to make a smoke particle effect in Forge Viewer for several days. I want to achieve something like this in the Forge Viewer. I find some threejs particle engine samples. But all of them can't use in version r71(which used by Forge Viewer). So I decided to write my own particle engine. But there's a problem I can't figured it out why.

At first ,I've tried it in threejs (not in Forge Viewer)(with version r71 of course) and I can do something like this. It seems good for me, I think I can start writing my particle engine. But when I test it in Forge Viewer, things aren't going well.

Back to Forge Viewer, I have tested point cloud with custom shader and it worked well in the Forge Viewer. I can customize every attributes such as color, size, position to every single point. But when I try to add an image texture using texture2D in my fragmentShader. The browser shows me some warnings and nothing show on the viewer.

Here are the warnings showed by browser:

WebGL: INVALID_OPERATION: getUniformLocation: program not linked
WebGL: INVALID_OPERATION: getAttribLocation: program not linked
WebGL: INVALID_OPERATION: useProgram: program not valid

vertexShader:

attribute float customSize;

void main() {
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

    gl_Position = projectionMatrix * mvPosition;
    gl_PointSize = customSize;
}

fragmentShader:

uniform sampler2D texture;

void main() {
    vec4 color = texture2D(texture,gl_PointCoord);  //when I comment out this line, everything works well
    gl_FragColor = color;
}

function for create points:

    createPoints() {
        const width = 100;
        const height = 100;
        const pointCount = width * height;

        const positions = new Float32Array(pointCount * 3);
        //const colors = new Float32Array(pointCount * 4);
        const sizes = new Float32Array(pointCount);

        const geometry = new THREE.BufferGeometry();

        const material = new THREE.ShaderMaterial({
            uniforms: {
                texture: { type: "t", value: this.particleTexture }
            },
            vertexShader: this.vertexShader,
            fragmentShader: this.fragmentShader,
            transparent: true,
            depthTest: true,
            blending: THREE.NormalBlending
        });
        material.supportsMrtNormals = true; 


        let i = 0;
        for (var x = 0; x < width; x++) {
            for (var y = 0; y < height; y++) {
                const u = x / width, v = y / height;

                positions[i * 3] = u * 20;
                positions[i * 3 + 1] = v * 20;
                positions[i * 3 + 2] = Math.sin(u * 20) + Math.cos(v * 20);

                sizes[i] = 1 + THREE.Math.randFloat(1, 5);

                colors[i * 4] = THREE.Math.random();
                colors[i * 4 + 1] = THREE.Math.random();
                colors[i * 4 + 2] = THREE.Math.random();
                colors[i * 4 + 3] = 1;

                i++;
            }
        }
        //const colorsAttribute = new THREE.BufferAttribute(colors, 4);
        //colorsAttribute.normalized = true;

        geometry.addAttribute("position", new THREE.BufferAttribute(positions, 3));
        geometry.addAttribute("customSize", new THREE.BufferAttribute(sizes, 1));
        //geometry.addAttribute("customColor", colorsAttribute);

        geometry.computeBoundingBox();
        geometry.isPoints = true;


        points = new THREE.PointCloud(geometry, material);

        viewer.impl.createOverlayScene('pointclouds');
        viewer.impl.addOverlay('pointclouds', points);
    }

in the createPoints() function, this.particleTexture comes from :

THREE.ImageUtils.loadTexture("../img/smokeparticle.png")

vertexShader ,fragmentShader and the createPoints() function are all the same between threejs testing app on browser(not in Forge Viewer) and in Forge Viewer app. But it works well only when it's not running in Forge Viewer.

I have searched a lot of tutorials and blogs, but just can't find a solution that fits me. Can anyone help? Or maybe there's a better way to make smoke effect in Forge Viewer? Thx for help!

(If I missed some information just tell me. I would update them!)


Solution

  • I tried your code and I managed to make it working changing the uniform name : texture by tex.

    I think texture is a reserved word in WebGL2. If you switch to WebGL1 (as describe in this article : Custom shader materials in Forge) it works.

    Fragment shader :

    uniform sampler2D tex;
    void main() {
        vec4 color = texture2D(tex,gl_PointCoord);
        gl_FragColor = color;
    }
    

    Replace uniform name in material constructor :

    const material = new THREE.ShaderMaterial({
        uniforms: {
            tex: { type: "t", value: this.particleTexture }
        },
        vertexShader: this.vertexShader,
        fragmentShader: this.fragmentShader,
        transparent: true,
        depthTest: true,
        blending: THREE.NormalBlending
    
    });
    

    Can you try it ?