'How to create water in react-three-fiber
I want to create 3D scene with water like in this example three.js shader ocean or water but I have to create this in react-three-fiber library. I already searched the internet to find some good working example for this case but without result.
Can I ask for help to figure it out how to implement above examples into react-three-fiber approach?
here is what I have so far for metioned Water component:
import React from "react";
import waterNormal from "./waternormals.jpg";
//import { useLoader } from "react-three-fiber";
import * as THREE from "three";
import { Water } from "three/examples/jsm/objects/Water.js";
const WaterObject = () => {
//const mapNormalWater = useLoader(TextureLoader, waterNormal);
return (
<mesh>
<planeBufferGeometry attach="geometry" args={[100, 100]} />
<Water
options={{
textureWidth: 512,
textureHeight: 512,
waterNormals: new THREE.TextureLoader().load(
waterNormal,
function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
}
),
sunDirection: new THREE.Vector3(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 3.7,
// fog: scene.fog !== undefined
}}
></Water>
</mesh>
);
};
export default WaterObject ;
Solution 1:[1]
Solved thanks to the hpalu.
Below code to the component which can be imported and used in canvas component.
import React, { useRef, useMemo } from "react";
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 });
function Ocean() {
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(30000, 30000), []);
const config = useMemo(
() => ({
textureWidth: 512,
textureHeight: 512,
waterNormals,
sunDirection: new THREE.Vector3(),
sunColor: 0xeb8934,
waterColor: 0x0064b5,
distortionScale: 40,
fog: false,
format: gl.encoding,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[waterNormals]
);
useFrame(
(state, delta) => (ref.current.material.uniforms.time.value += delta)
);
return (
<water
ref={ref}
args={[geom, config]}
rotation-x={-Math.PI / 2}
position={[0, 0, 0]}
/>
);
}
export default Ocean;
Solution 2:[2]
Thanks both! This helped a great deal...
Still had quite some struggles getting this to work with other versions of what have ya, so adding the changes I made in case they could help others:
useFrame((state, delta) => {
const material = ref?.current?.material as THREE.ShaderMaterial;
material.uniforms.time.value += delta;
})
to prevent object is possibly 'null' error.
Declared a namespace interface, as the lower case 'water' really doesn't work otherwise:
extend({ Water });
declare global {
namespace JSX {
interface IntrinsicElements {
water: Object3DNode<Water, typeof Water>
}
}
}
... and finally... if your up-vector is the Z-axis, as opposed to the default Y-axis, you get funky results, so beware the rotation of the object.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | prkrekel |
