Hello, since the past 2 days I have been trying to replicate water that I have done in plain three js. Below is the code
import { Water } from "three/addons/objects/Water.js";
import { Sky } from "three/addons/objects/Sky.js";
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
let container;
let camera, scene, renderer;
let water, sun;
let mixer;
let model;
init();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.3; // Increase exposure to brighten the scene
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 1, 20000);
camera.position.set(34.197718304489214, 42.92727023247369, 98.40921054440358);
camera.rotation.set(0.23163731194605403, 0.5339245571653, -0.11946685651052753);
sun = new THREE.Vector3();
const waterGeometry = new THREE.PlaneGeometry(10000, 10000);
const loader = new THREE.TextureLoader();
const waterNormals = loader.load('https://threejs.org/examples/textures/waternormals.jpg');
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
water = new Water(
waterGeometry,
{
textureWidth: 1024,
textureHeight: 1024,
waterNormals: waterNormals,
sunDirection: new THREE.Vector3(),
sunColor: 0x2432a0,
waterColor: 0x070b2d,
distortionScale: 8.5,
size: 10000
}
);
water.rotation.x = -Math.PI / 2;
scene.add(water);
const skyGeometry = new THREE.SphereGeometry(5000, 32, 32);
const skyMaterial = new THREE.ShaderMaterial({
uniforms: {
topColor: { value: new THREE.Color(0x00011b) },
bottomColor: { value: new THREE.Color(0x07249b) },
offset: { value: 1100 },
exponent: { value: 0.6 }
},
vertexShader: `
varying vec3 vWorldPosition;
void main() {
vec4 worldPosition = modelViewMatrix * vec4(position, 1.0);
vWorldPosition = worldPosition.xyz;
gl_Position = projectionMatrix * worldPosition;
}
`,
fragmentShader: `
uniform vec3 topColor;
uniform vec3 bottomColor;
uniform float offset;
uniform float exponent;
varying vec3 vWorldPosition;
void main() {
float h = normalize(vWorldPosition + offset).y;
gl_FragColor = vec4(mix(bottomColor, topColor, max(pow(max(h, 0.0), exponent), 0.0)), 1.0);
}
`,
side: THREE.BackSide
});
const sky = new THREE.Mesh(skyGeometry, skyMaterial);
scene.add(sky);
// Add stars to the sky with twinkling effect using custom shader material
const starGeometry = new THREE.BufferGeometry();
const starVertices = [];
const starPhases = [];
const starSizes = [];
for (let i = 0; i < 5000; i++) {
const x = THREE.MathUtils.randFloatSpread(2000);
const y = THREE.MathUtils.randFloat(100, 2000); // Ensure stars are above the horizon
const z = THREE.MathUtils.randFloatSpread(2000);
// Ensure stars are far from the camera
if (Math.sqrt(x * x + y * y + z * z) > 500) {
starVertices.push(x, y, z);
starPhases.push(Math.random() * 2.0 * Math.PI); // Random phase for each star
starSizes.push(Math.random() * 5 + 2); // Random size between 2 and 7
}
}
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
starGeometry.setAttribute('phase', new THREE.Float32BufferAttribute(starPhases, 1));
starGeometry.setAttribute('size', new THREE.Float32BufferAttribute(starSizes, 1));
const starMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 1.0 }
},
vertexShader: `
attribute float phase;
attribute float size;
varying float vPhase;
void main() {
vPhase = phase;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size; // Use the size attribute for point size
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform float time;
varying float vPhase;
void main() {
float alpha = 0.8 + 0.5 * sin(time + vPhase);
vec2 coord = gl_PointCoord - vec2(0.5);
float distance = length(coord);
if (distance > 0.5) {
discard; // Discard fragments outside the circular area
}
float glow = smoothstep(0.1, 0.3, distance); // Adjust smoothstep for smaller white core
vec3 color = mix(vec3(1.0, 1.0, 1.0), vec3(0.0, 0.0, 1.0), glow);
gl_FragColor = vec4(color, alpha * (1.0 - distance * 0.5)); // Adjust alpha for more intensity
}
`,
transparent: true
});
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
// Add directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 5);
directionalLight.position.set(34.197718304489214, 42.92727023247369, 20 ).normalize();
const targetObject = new THREE.Object3D();
targetObject.position.set(34.197718304489214, 42.92727023247369, 20);
scene.add(targetObject);
directionalLight.target = targetObject;
directionalLight.castShadow = false;
scene.add(directionalLight);
window.addEventListener('resize', onWindowResize);
}
function loadModel() {
const loader = new GLTFLoader();
loader.load(
'./Moon JellyfishT.glb',
function (gltf) {
console.log('Model loaded successfully');
model = gltf.scene;
// Position in front of camera
model.position.set(34.197718304489214, 42.92727023247369, 20);
model.rotation.set(10,0,-3);
// Make model larger
model.scale.set(6, 6, 6);
// Traverse the model and adjust materials
model.traverse((child) => {
if (child.isMesh) {
child.material.emissive = new THREE.Color(0xffffff);
child.material.emissiveIntensity = 2;
}
});
scene.add(model);
// Log animations
console.log('Available animations:', gltf.animations.length);
// Setup animation mixer
mixer = new THREE.AnimationMixer(model);
if (gltf.animations.length > 0) {
const action = mixer.clipAction(gltf.animations[0]);
action.play();
}
},
// Loading progress
function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
// Error handler
function (error) {
console.error('Error loading model:', error);
}
);
}
loadModel();
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
render();
}
function render() {
const time = performance.now() * 0.001;
water.material.uniforms['time'].value += 0.2 / 60.0; // Default speed
if (mixer) {
mixer.update(0.016); // Update animation with approximate delta time
}
scene.children.forEach(child => {
if (child.material && child.material.uniforms && child.material.uniforms.time) {
child.material.uniforms.time.value = time;
}
});
renderer.render(scene, camera);
}
document.addEventListener('DOMContentLoaded', () => {
const firstSvg = document.querySelector('.svg-elem-1');
const secondSvg = document.querySelector('.svg-elem-2');
const dotSvg = document.querySelector('.hidden.circle');
const loadingScreen = document.querySelector('#loadingScreen');
if (firstSvg && secondSvg && dotSvg && loadingScreen) {
firstSvg.classList.remove('hidden');
firstSvg.classList.add('animate');
setTimeout(() => {
dotSvg.classList.remove('hidden');
dotSvg.classList.add('fade-in');
}, 1); // 4000
firstSvg.addEventListener('animationend', function() {
setTimeout(() => {
secondSvg.classList.add('animate');
document.querySelector('.hidden.spacing').classList.remove('hidden');
setTimeout(() => {
loadingScreen.classList.add('fade-out');
}, 1); //2500
}, 1); //800
});
}
});
I know that’s the worst kinda code you’ve ever seen but I am a beginner at three js, below is how it looked.
So I thought to move on to react three js which is much more user friendly and was trying to replicate the same scene that I did in plain three js, below is my code
import { extend, useThree, useLoader, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { Water } from "three/examples/jsm/objects/Water.js";
extend({ Water });
export function CustomWater() {
const ref = useRef();
const gl = useThree((state) => state.gl);
const waterNormals = useLoader(
THREE.TextureLoader,
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/textures/waternormals.jpg"
);
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
const geom = useMemo(() => new THREE.PlaneGeometry(1000, 1000), []);
const config = useMemo(
() => ({
textureWidth: 512, // Reduced resolution
textureHeight: 512,
waterNormals,
sunDirection: new THREE.Vector3(0, 1, 0),
sunColor: new THREE.Color(0x111111).convertSRGBToLinear(), // Darker sun
waterColor: new THREE.Color(0x001e0f).convertSRGBToLinear(), // Deep blue
distortionScale: 0.5, // Reduced distortion
size: 2,
alpha: 0.7, // More transparency
fog: true
}),
[waterNormals]
);
useFrame((state, delta) => {
if (ref.current?.material) {
const material = ref.current.material;
material.uniforms.time.value += delta * 0.5; // Slower movement
material.uniforms.size.value = config.size;
material.uniforms.distortionScale.value = config.distortionScale;
material.transparent = true;
material.opacity = config.alpha;
}
});
return (
<water
ref={ref}
args={[geom, config]}
rotation-x={-Math.PI / 2}
position={[0, -1, 0]}
transparent
/>
);
}``` for the customwater and then actual app.jsx
```import { Canvas } from "@react-three/fiber"
import { OrbitControls, Sphere } from "@react-three/drei"
import { CustomStars } from "./components/CustomStars"
import {CustomWater} from './components/CustomWater';
import { Jellyfish } from "./components/Jellyfish"
import { Suspense } from "react"
import { Gradient, LayerMaterial } from "lamina";
import * as THREE from "three";
function App() {
return (
<div id="canvas-container" style={{height: "100vh", width: "100vw"}}>
<Canvas camera={{ position: [3.36, 2.80, 21.02], rotation: [0.26, 0.16, -0.04] }} >
/* Remove orbit controls to get back to the position you left*/
<OrbitControls
makeDefault
enableDamping
dampingFactor={0.05}
target={[0, 0, 0]}
/>
<CustomStars />
<Sphere scale={[900, 900, 900]} rotation-y={-90}>
<LayerMaterial
lighting="physical"
transmission={1}
side={THREE.BackSide}
envMapIntensity={0}
roughness={1}
metalness={0}
>
<Gradient colorA={"#07249b"} colorB={"#09104d"} axes="y" start={-0.3} end={0.5}/>
</LayerMaterial>
</Sphere>
<Suspense fallback={null}>
<Jellyfish scale={0.5} position={[0, 0, 0]} />
</Suspense>
<CustomWater />
</Canvas>
</div>
)
}
export default App
And below is how it looked
I know that’s a lot of read, if you did all that Thank you so much! So I don’t get why there is such a difference between both (I know does not look like a big difference but the water in react is more white even after ignoring the stars’ reflection). The reflectiveness of water is just too much. I have tried changing water settings and color management settings but still no luck. Please help! I’ve been stuck here for the past two days and still can’t get it to work.