Hello everyone. I need to make an animation for the background of the site where there will be a scene with water on which a ball falls and round waves diverge from this ball (then I want to change the ball to a drop). I can’t do it and I don’t understand why. The waves are simply not created by the falling ball.
<template>
<div ref="container" class="scene-container"></div>
</template>
<script setup>
import * as THREE from 'three';
import { Water } from 'three/examples/jsm/objects/Water.js';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
import { onMounted, ref } from 'vue';
const container = ref(null);
onMounted(() => {
// Scene initialization
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 15);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping; // Improves contrast
renderer.toneMappingExposure = 1.0;
container.value.appendChild(renderer.domElement);
// HDRI Environment
new RGBELoader().load('/autumn_field_puresky_2k.hdr', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
scene.background = texture;
});
// Lighting setup
const sunLight = new THREE.DirectionalLight(0xffffff, 3);
sunLight.position.set(10, 20, 10);
scene.add(sunLight);
scene.add(new THREE.AmbientLight(0x404040)); // Soft ambient light
// Water normal texture
const waterNormals = new THREE.TextureLoader().load('/waternormals.jpg');
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
// Add realistic water
const waterGeometry = new THREE.PlaneGeometry(100, 100);
const water = new Water(waterGeometry, {
textureWidth: 512,
textureHeight: 512,
waterNormals: waterNormals,
sunDirection: new THREE.Vector3(),
sunColor: 0xffffff,
waterColor: 0x0077be,
distortionScale: 3.7,
fog: false,
transparent: true,
opacity: 0, // Initial transparency
});
// Check if uniforms exist
if (!water.material.uniforms) {
water.material.uniforms = {};
}
// Add ripple effects to water shader
water.material.onBeforeCompile = (shader) => {
shader.uniforms.rippleCenter = { value: new THREE.Vector2(0, 0) };
shader.uniforms.rippleTime = { value: -1 };
shader.uniforms.rippleStrength = { value: 1.5 }; // Increased ripple strength
shader.uniforms.rippleColor = { value: new THREE.Color(0x333) }; // Ripple color
// Add ripple logic to shader
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
uniform vec2 rippleCenter;
uniform float rippleTime;
uniform float rippleStrength;
uniform vec3 rippleColor;
`
);
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
if (rippleTime >= 0.0) {
float rippleDistance = distance(position.xz, rippleCenter);
float rippleIntensity = max(0.0, 1.0 - rippleDistance / 15.0); // Increased ripple radius
float rippleEffect = sin(rippleTime * 10.0 - rippleDistance * 2.0) * rippleIntensity * rippleStrength;
transformed.y += rippleEffect;
}
`
);
shader.fragmentShader = shader.fragmentShader.replace(
'#include <output_fragment>',
`
#include <output_fragment>
if (rippleTime >= 0.0) {
float rippleDistance = distance(vWorldPosition.xz, rippleCenter);
float rippleIntensity = max(0.0, 1.0 - rippleDistance / 15.0); // Increased ripple radius
gl_FragColor.rgb = mix(gl_FragColor.rgb, rippleColor, rippleIntensity * 0.5); // Color mixing
}
`
);
water.material.uniforms.rippleCenter = shader.uniforms.rippleCenter;
water.material.uniforms.rippleTime = shader.uniforms.rippleTime;
water.material.uniforms.rippleStrength = shader.uniforms.rippleStrength;
water.material.uniforms.rippleColor = shader.uniforms.rippleColor;
};
water.rotation.x = -Math.PI / 2;
scene.add(water);
// Add sphere
const ballGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const ballMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const ball = new THREE.Mesh(ballGeometry, ballMaterial);
ball.visible = false;
scene.add(ball);
// Create wave effect function
function createWave(position) {
// Set ripple center
water.material.uniforms.rippleCenter.value.set(position.x, position.z);
// Activate ripple effect
water.material.uniforms.rippleTime.value = 0;
}
// Ball drop animation
function dropBall() {
ball.position.set(Math.random() * 10 - 5, 15, Math.random() * 10 - 5);
ball.visible = true;
function animateBall() {
ball.position.y -= 0.5;
if (ball.position.y <= 0.5) {
ball.visible = false;
createWave(ball.position); // Create ripple effect
} else {
requestAnimationFrame(animateBall);
}
}
animateBall();
}
// Ball drops every 3 seconds
setInterval(dropBall, 3000);
// Water transparency control
let isWaterTransparent = false;
// Toggle water transparency
function toggleWaterTransparency() {
isWaterTransparent = !isWaterTransparent;
water.material.transparent = isWaterTransparent;
water.material.opacity = isWaterTransparent ? 0.2 : 0.9; // Change opacity
}
// Add 'T' key event listener
window.addEventListener('keydown', (event) => {
if (event.key === 't' || event.key === 'T') {
toggleWaterTransparency();
}
});
// Main animation loop
function animate() {
requestAnimationFrame(animate);
// Update water time uniform
if (water.material.uniforms && water.material.uniforms.time) {
water.material.uniforms.time.value += 0.02;
}
// Update ripple effects
if (water.material.uniforms && water.material.uniforms.rippleTime) {
water.material.uniforms.rippleTime.value += 0.05;
// Disable finished ripples
if (water.material.uniforms.rippleTime.value > 2) {
water.material.uniforms.rippleTime.value = -1; // Disable ripple
}
}
renderer.render(scene, camera);
}
animate();
// Window resize handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
});
</script>
<style>
.scene-container {
width: 100%;
height: 100vh;
}
</style>