I was using react-three-fiber package and wanted to implement camera movement feature with mouse move instead of mouse click. For example, if you visit roundme.com then you will see a picture of a tour and when you move mouse camera, kinda, moves towards the position of pointer. So, to achieve it I decided to get current position of mouse and put that position to OrbitControls like this:
import React, { Suspense, useRef } from "react";
import { Canvas } from "react-three-fiber";
import { Html, OrbitControls } from "drei";
import "../../styles/index.css";
import { ReactReduxContext, Provider } from "react-redux";
import Portals from "../Portal/portal.component";
const Panorama = () => {
const mouse = useRef({ x: 0, y: 0 });
function getMousePos(e) {
console.log(e.clientX, e.clientY);
return { x: e.clientX, y: e.clientY };
}
return (
<>
<ReactReduxContext.Consumer>
{({ store }) => (
<Canvas
onMouseMove={(e) => (mouse.current = getMousePos(e))}
invalidateFrameloop
concurrent
camera={{ position: [50, 0, 0.1] }}
>
<OrbitControls
enableZoom={true}
enablePan={true}
dampingFactor={1}
minDistance={10}
maxDistance={500}
autoRotate
zoomSpeed={5}
autoRotateSpeed={0.5}
rotateSpeed={-1.4}
/>
<Suspense
fallback={
<Html center style={{ color: "white" }}>
loading...
</Html>
}
>
<Provider store={store}>
<Portals />
</Provider>
</Suspense>
</Canvas>
)}
</ReactReduxContext.Consumer>
</>
);
};
export default Panorama;
But camera does not move when I move mouse.
I'm not sure if you can use this same pattern with drei's OrbitControls, but here is how you can achieve it with the camera-controls package:
import CameraControls from 'camera-controls'
CameraControls.install({ THREE })
extend({ CameraControls })
function Controls() {
const ref = useRef()
const camera = useThree((state) => state.camera)
const gl = useThree((state) => state.gl)
useFrame((state, delta) => {
// update camera angles according to mouse position
ref.current.azimuthAngle = -state.mouse.x
ref.current.polarAngle = Math.PI / 2 + state.mouse.y
ref.current.update(delta)
})
return <cameraControls ref={ref} args={[camera, gl.domElement]} />
}
And then simply put <Controls /> inside of your <Canvas />.
Related
I'm trying to scale my model and change its position, but its not happening- I must be missing something. It's not webpack as the rest is updating fine and the model is loading. I used gltfjsx to create this file.
export default function Model({ ...props }) {
const group = useRef()
const { nodes } = useGLTF('./mymodel')
const material = useMemo(() => {
if (props.layer === DEFAULT_LAYER) return new THREE.MeshStandardMaterial({ color: new THREE.Color('rgba(62,53,105,1)'), roughness: 0.2, metalness: 0.9 })
else return new THREE.MeshBasicMaterial({ color: new THREE.Color('rgba(52,95,75,1)') })
}, [props.layer]);
return (
<group ref={group} dispose={null} position={[0,-500,0]} scale={[0.01, 0.01, 0.01]} {...props}>
<mesh geometry={nodes.Node.geometry} material={material}
layers={props.layer} receiveShadow castShadow />
</group>
)
}
export default function App() {
return (
<>
<Canvas camera={{ position: [0, 20, 12], fov: 35, near: 0.1, far: 2000 }} gl={{ antialias: true }} onCreated={state => state.gl.setClearColor( 0xffffff, 0)}>
<Suspense fallback={null}>
<Stage intensity={1}>
<Model layer = {DEFAULT_LAYER} />
</Stage>
</Suspense>
</Canvas>
<Loader />
</>
)
}
I have placed scale and position in the Model, mesh and group objects but none make a difference. How come? I can't change my camera position either.
My imports for Model.jsx:
import {useRef, useMemo} from 'react'
import { useGLTF } from '#react-three/drei'
import * as THREE from 'three'
My imports for App.jsx:
import React, { Suspense, useRef, useMemo } from 'react'
import { Canvas, useThree, useFrame } from '#react-three/fiber'
import { Loader, useFBO, Stage, Effects, Environment, OrbitControls } from '#react-three/drei'
import Model from './FortunaDraco'
import { FXAAShader } from 'three-stdlib'
import { AdditiveBlendingShader } from './shaders/AdditiveBlendingShader'
import { VolumetricLightShader } from './shaders/VolumetricLightShader'
import './App.css'
For anyone having this issue it was because of Stage
Basically, i am trying to do a 3d action at the background and lottie animations on the front. When i scroll, the camera moves, the position of everything changes. However, i need to keep the html layer at the same position during the 3d action.
Here is what i tried for html layer,
import { useEffect, useRef } from "react";
import { Html, useScroll } from "#react-three/drei";
import { useFrame, useThree } from "#react-three/fiber";
const Overlay = () => {
const { camera } = useThree();
const overlay = useRef();
useFrame(() => {
camera.updateMatrixWorld();
let vector = camera.position.clone();
vector.applyMatrix4(camera.matrixWorld);
vector.normalize().multiplyScalar(-10);
overlay.current.position.set(vector.x, vector.y, vector.z);
});
return (
<group ref={overlay}>
<h1 style={{ color: "white" }} >
hello world!
</h1>
<h1>something else</h1>
</group>
);
};
export default Overlay;
I'm drawing a 3d model with the following configuration.
react-three-fiber
#react-three/drei
The following is a CodeSandBox with a model drawn.
CodeSandBox-A
The drawn model is close to the top of the canvas, and I want to center it.
Could you please tell me how to center the model on the canvas?
This is the first time I've touched Three.js, so I'm sure I may have made some elementary mistakes.
The code is as follows.
I referred to the following document for the props of the Canvas component.
React Three Fiber Documentation
import { useMemo, Suspense } from "react";
import "./style.css";
import { Canvas } from "#react-three/fiber";
const { useGLTF, OrbitControls, Stage } = require("#react-three/drei");
const CanvasContainer = () => {
const { scene } = useGLTF("/scene.glb");
return (
<Canvas camera={{ fov: 70, position: [0, 1000, 1000] }}>
// No matter what I put in this "position" value, I could not see any change in the canvas.
<OrbitControls enablePan={true} enableZoom={true} enableRotate={true} />
<Stage>
<group dispose={null}>
<primitive scale={[1, 1, 1]} object={scene} />
</group>
</Stage>
</Canvas>
);
};
const App = () => {
const isWindow = typeof window === "undefined";
const memoCanvas = useMemo(() => <CanvasContainer />, []);
return (
<>
<h1>Canvas</h1>
<div className="canvas_container">
{!isWindow && <Suspense fallback={<div />}>{memoCanvas}</Suspense>}
</div>
</>
);
};
export default App;
setDefaultCamera removed in v6.x. · Issue #1147 · pmndrs/react-three-fiber
I also tried the code to use useThree as described in the above Issue.
The following is its CodeSandBox.
CodeSandBox-B
As you can see from the CodeSandBox, I was unable to move the camera.
give orbitcontrols the "makeDefault" prop, then it should in the center. otherwise check out dreis Bounds component. see: https://twitter.com/0xca0a/status/1453807293074661385
the memocanvas thing btw makes no sense, don't put jsx into usememo.
I'm trying to create a 360 image using react-three-fiber. The 360 viewer is working perfectly as it purposed but the generated image is darker than the original image.
Here's the screenshot from my 360 viewer
Here's the original image
Here's my code
import React, { Suspense } from "react";
import { useTexture, OrbitControls } from "#react-three/drei";
import { Canvas } from "#react-three/fiber";
import * as THREE from "three";
const Generate360Image = (props) => {
var myTexture = useTexture(props.imageLocation);
return (
<>
<mesh
position={[0, 0, 0]}
>
<sphereGeometry attach="geometry" args={[500, 60, 60]} />
<meshBasicMaterial
attach="material"
map={myTexture}
side={THREE.DoubleSide}
/>
</mesh>
</>
);
};
class GenerateImage extends React.Component {
render() {
return (
<Canvas style={{ width: "100%", height: "100%" }}>
<Suspense fallback={null}>
<Generate360Image
imageLocation={this.props.myImage}
className="image-canvas"
/>
</Suspense>
<OrbitControls
autoRotate="true"
autoRotateSpeed={0.4}
minDistance={5}
maxDistance={80}
/>
</Canvas>
);
}
}
export default GenerateImage;
Any help will be appreciated. Thanks!
try use meshPhysicalMaterial instead of meshBasicMaterial. meshBasicMaterial does not properly reflect lightings.
compare these two samples from three.js docs
meshBasicMaterial
meshPhysicalMaterial
I am learning to develop some mobile applications using react native and expo.
I have created some code to apply a texture on a sphere. I have tried both with http url image and an image stored on my machine. When I am running the project in the expo web browser, everything is OK, but when I am launching it on android and ios devices, the sphere object is not displayed.
Any idea why this would work in the browser but not in a native environment?
Here is my code:
import React, { useRef, useState,useMemo, Suspense } from 'react';
import { StyleSheet, View } from 'react-native';
import * as THREE from "three";
import { Canvas, useFrame,useLoader } from 'react-three-fiber';
// import myImage from './img/world.png'
function SphereElement(props) {
// This reference will give us direct access to the mesh
const mesh = useRef();
// Set up state for the hovered and active state
const [hovered, setHover] = useState(false);
const [active, setActive] = useState(false);
// Rotate mesh
useFrame(() => {
if (mesh && mesh.current) {
mesh.current.rotation.y = mesh.current.rotation.y += 0.002;
}
});
const image_pth='https://i.imgur.com/3yEEsnO.png'
const texture = useLoader(THREE.TextureLoader,image_pth)
return (
<mesh
{...props}
ref={mesh}
>
<sphereBufferGeometry attach="geometry" args={[2, 32, 32]} />
<meshBasicMaterial
attach="material"
transparent side={THREE.DoubleSide}
map={texture}
>
</meshBasicMaterial>
</mesh>
);
}
export default function App() {
return (
<View style={styles.container}>
<Canvas>
<Suspense fallback={null}>
<SphereElement position={[0, 0, 0]} />
</Suspense>
</Canvas>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "black",
},
});
You should use useTexture hook from drei, as of the newer version of three and react-three-fiber, that is the recommended way to load textures.
https://github.com/pmndrs/drei#usetexture