【问题标题】:How to make smooth transition in Three.js with shaders?如何在 Three.js 中使用着色器进行平滑过渡?
【发布时间】:2020-11-28 17:40:54
【问题描述】:

我有一个基本的 Three.js 应用程序,它只是向平面添加了两个纹理。我将它们作为制服传递给我的着色器,当我的鼠标进入平面时,我将它们转换为另一个。

我的问题是,有没有办法在将一种纹理更改为另一种纹理的过程中添加过渡? 所以他们的变化比现在更顺利:

我的片段着色器:

uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;
uniform float u_progress;

uniform sampler2D image;
uniform sampler2D displacementTexture;

uniform int mouseIntersects;

varying vec2 vUv;

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec4 mainTexture = texture2D(image, vUv);
    vec4 displacementTexture = texture2D(displacementTexture, vUv.yx);
    vec2 disaplacedUv = vec2(
        vUv.x, 
        vUv.y
    );

    if (mouseIntersects == 1) {
        disaplacedUv.y = mix(vUv.y, displacementTexture.r + 0.2, 0.2);

        gl_FragColor = texture2D(image, disaplacedUv);
    } else {

        gl_FragColor = mainTexture;
    }
}

三个.js 设置

const setup = () => {
  ...scene, camera etc

  uniforms = {
    image: { type: 't', value: new THREE.TextureLoader().load(whale) },
    displacementTexture: { type: 't', value: new THREE.TextureLoader().load(texture) },
    mouseIntersects: { type: 'f', value: 0 },
  };

  material = new THREE.ShaderMaterial({
    side: THREE.DoubleSide,
    uniforms: uniforms,
    vertexShader: vertex,
    fragmentShader: fragment,
  });

  const geometry = new THREE.PlaneGeometry(1, 1, 32, 32);

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

  scene.add(mesh);

  raycaster = new THREE.Raycaster();

  document.addEventListener('mousemove', (event) => updateMousePositions(event), false);
};

const updateMousePositions = (event) => {
  event.preventDefault();
  uniforms.u_mouse.value.x = (event.clientX / window.innerWidth) * 2 - 1;
  uniforms.u_mouse.value.y = -(event.clientY / window.innerHeight) * 2 + 1;
};

const animate = () => {
  requestAnimationFrame(animate);
  render();
};

const render = () => {
  uniforms.u_time.value += 0.05;
  camera.lookAt(scene.position);

  camera.updateMatrixWorld();
  raycaster.setFromCamera(uniforms.u_mouse.value, camera);
  const intersects = raycaster.intersectObject(mesh);
  
  if (intersects.length === 1) {
    uniforms.mouseIntersects.value = 1;
  } else {
    uniforms.mouseIntersects.value = 0;
  }

  renderer.render(scene, camera);
};

setup();
animate();

【问题讨论】:

  • 使用 GSAP 或 TweenJS 等补间库逐渐更改 uniforms.u_time.value

标签: three.js glsl fragment-shader


【解决方案1】:

不要使用uniform int mouseIntersects;,而是将其设为float,这样您就可以将其从0.0 过渡到1.0,以平滑渐变而不是硬离散步骤。然后,您可以在着色器中使用mix() GLSL 命令从一种纹理平滑过渡到另一种纹理:

uniform sampler2D image;
uniform sampler2D displacementTexture;
uniform int mouseIntersects;
varying vec2 vUv;

void main() {
    vec4 mainTexture = texture2D(image, vUv);
    vec4 altTexture = texture2D(displacementTexture, vUv);

    // 0.0 outputs mainTexture, 1.0 outputs altTexture
    gl_FragColor = mix(mainTexture, altTexture, mouseIntersects);
}

最后,在 JavaScript 中,您可以在鼠标悬停时使用 MathUtils.lerp() 插入此值:

var targetValue = 0;

const render = () => {

  // ...

  if (intersects.length === 1) {
    targetValue = 1;
  } else {
    targetValue = 0;
  }

  // This will smoothly transition from its current value to the targetValue on each frame:
  uniforms.mouseIntersects.value = THREE.MathUtils.lerp(uniforms.mouseIntersects.value, targetValue, 0.1);

  renderer.render(scene, camera);
};

...或者您可以使用animation library like GSAP,这可能会给您更多控制权

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 2013-08-14
    • 1970-01-01
    • 2013-11-19
    • 2013-02-27
    相关资源
    最近更新 更多