three.js - 如何使用着色器在 Three.js 中进行平滑过渡?
问题描述
我有一个基本的 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();
解决方案
而不是使用uniform int mouseIntersects;
make it afloat
这样您就可以将它从 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);
};
...或者您可以使用GSAP 之类的动画库,它可能会给您更多的控制权
推荐阅读
- jquery - 图像弹出在使用 jquery 的角度应用程序中不起作用
- reactjs - 隐藏 Material-UI 自动完成弹出窗口,直到输入文本
- apache-kafka-streams - 使用 Kafka Streams Binder 和函数式处理器处理 Spring Cloud Streams 中的处理异常示例
- sqlite - sqlite BEGIN EXCLUSIVE 失败事务是否在“busy_timeout”期间不断重试?
- sql - 连接 2 个表的数据库设计最佳实践
- amazon-web-services - 仅通过使用 SQS::QueuePolicy 'principal' 属性的 Lambda 函数来限制对 SQS 的访问
- html - 导航栏菜单响应式
- java - Firebase 实施后没有静态方法
- angular - 可点击的 algolia 搜索结果?
- javascript - 如何使用 PWA 服务工作者在后台发送请求