让物体从上到下消失的shader处理

49 阅读1分钟

让物体从上到下消失的shader处理。使用 threejs material中的onBeforeCompile方法,在编译前修改shader。

/**
 * 
 * @param params 
 *   target: Mesh。 目标Mesh物体
 *   isVertical: Boolean类型。 ture是y方向动画,false是x方向
 *   isStart: Boolean类型。true是从显示=>隐藏。false是 从隐藏=>显示
 *   callback: fn函数。动画执行完回调
 * @returns 
 */
export let MachineAnimateShader = function (params: any) {

    const { target, isVertical, isStart, callback } = params;
    if (!target.material) return false;

    if(isStart) {
        saveTarget[target.name] = {
            mesh: target,
            isVertical: isVertical,
            material: target.material.clone()
        };
    }

    // 物体的高度和宽度范围
    const { min, max } = target.geometry.boundingBox;
    
    const diff = 2.0;
    const MaxObject = { x: max.x + diff, y: max.y + diff };
    const MinObject = { x: min.x - diff, y: min.y - diff };

    const startValue = isStart ? ((isVertical ? max.y : max.x) + diff) : ((isVertical ? min.y : min.x) - diff);

    let newMaterial = target.material.clone();
    target.material = newMaterial;

    let targetShader: any = null;
    newMaterial.onBeforeCompile = (shader: any) => {        
        targetShader = shader;

        // 顶部颜色
        shader.uniforms.uTopColor = {
            value: new THREE.Color("#51ddef"),
        };
        // 时间
        shader.uniforms.iTime = {
            value: startValue,
        };

        // 修改顶点着色器
        shader.vertexShader = shader.vertexShader.replace(
            "#include <common>",
            `
            #include <common>
            varying vec3 vPosition;
            `
        );
        shader.vertexShader = shader.vertexShader.replace(
            "#include <fog_vertex>",
            `
            #include <fog_vertex>
            vPosition = position;
            `
        );

        // 修改片元着色器
        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <common>",
            `
            #include <common>
            uniform vec3 uTopColor;
            // uniform float uHeight;
            uniform float iTime;
            varying vec3 vPosition;
            `
        );

        let fragmentStr = '';
        if (isVertical) { // 垂直方向
            fragmentStr = `
                #include <dithering_fragment>
                if(abs(iTime - vPosition.y) < 0.05) {
                    gl_FragColor = vec4(uTopColor, 0.7);
                }
                if(iTime < vPosition.y) {
                    discard;
                }`
        } else { // 水平方向
            fragmentStr = `
                #include <dithering_fragment>
                if(abs(iTime - vPosition.x) < 0.05) {
                    gl_FragColor = vec4(uTopColor, 0.7);
                }
                if(iTime < vPosition.x) {
                    discard;
                }`
        }

        shader.fragmentShader = shader.fragmentShader.replace(
            "#include <dithering_fragment>", fragmentStr
        );
    }
    // 清除shader缓存
    newMaterial.customProgramCacheKey = function() {
        return '1' + target.name + newMaterial.uuid;
    }

    target.material.needsUpdate = true;

    // const tweenStartobject = { x: max.x + diff, y: max.y + diff };
    // const tweenEndObject = { x: min.x - diff, y: min.y - diff };

    const tweenStartobject = isStart ? MaxObject : MinObject;
    const tweenEndObject = isStart ? MinObject : MaxObject;
    
    const tween = new TWEEN.Tween(tweenStartobject);
    tween.to(tweenEndObject, 2000)
        .onUpdate(function (object) {
            if (targetShader) {
                targetShader.uniforms.iTime.value = isVertical ? object.y : object.x;
            }
        })
        .onComplete(function () {
            callback && callback();
        })
        .start();
}