'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