React three fiber and Textureloader not display - reactjs

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

Related

My r3f model cannot be scaled nor its position changed

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

How to move the camera using react-three-fiber and #react-three/drei?

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.

react-three-fiber 360 image darker than the original image

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

How to move camera with mouse move instead of mouse click

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 />.

How to implement Three.js inside Gatsby?

I've installed react-three-fiber and three packages. I'm following this tutorial, but I have doubts of where to put this line:
const rootElement = document.getElementById("root");
Also, I'm starting to receive this error:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
My index.js:
import React, { Children, useRef, useState } from "react"
import { Link } from "gatsby"
import { Canvas} from 'react-three-fiber'
import Layout from "../components/layout"
const IndexPage = () => {
return(
<Layout>
<div>
<h1>Hi</h1>
</div>
<canvas>
<Children></Children>
</canvas>
</Layout>
)
}
const rootElement = document.getElementById("root");
export default IndexPage
Any ideas?
Make sure you have installed both three and react-three-fiber.
npm install three react-three-fiber
Then in your gatsby page component just import Canvas from react-three-fiber before using it in your JSX.
import React from "react"
import { Canvas} from 'react-three-fiber'
import Layout from "../components/layout"
const IndexPage = () => (
<Layout>
<Canvas />
</Layout>
)
export default IndexPage
Regarding const rootElement = document.getElementById("root"); :
Although Gatsby is built with React, it doesn't require you to select a root element in order to render your app. If this sounds confusing, you should spend a little bit of time to take a read of the Gatsby docs.
If you were to build the example from the react-three-fiber docs in Gatsby, it would look something like this.
import React, { useRef, useState } from "react"
import { Canvas, useFrame } from "react-three-fiber"
const Box = props => {
// This reference will give us direct access to the mesh so we can animate it
const mesh = useRef()
// Set up state for the hovered and active state
const [hovered, setHover] = useState(false)
const [active, setActive] = useState(false)
// Rotate mesh every frame, this is outside of React without overhead
useFrame(() => (mesh.current.rotation.x = mesh.current.rotation.y += 0.01))
return (
<mesh
{...props}
ref={mesh}
scale={active ? [1.5, 1.5, 1.5] : [1, 1, 1]}
onClick={e => setActive(!active)}
onPointerOver={e => setHover(true)}
onPointerOut={e => setHover(false)}
>
<boxBufferGeometry attach="geometry" args={[1, 1, 1]} />
<meshStandardMaterial
attach="material"
color={hovered ? "hotpink" : "orange"}
/>
</mesh>
)
}
const IndexPage = () => (
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
)
export default IndexPage

Resources