import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { SpotLight, useDepthBuffer, OrbitControls } from '@react-three/drei'
import { Suspense, useRef } from 'react';
import { Vector3 } from 'three'
const MovieSpot = ({ vec = new Vector3(), ...props }) => {
const light = useRef();
const viewport = useThree((state) => state.viewport);
useFrame((state) => {
light.current.target.position.lerp(vec.set((state.mouse.x * viewport.width) / 2, (state.mouse.y * viewport.height) / 2, 0), 0.1)
light.current.target.updateMatrixWorld()
});
return <SpotLight castShadow ref={light} penumbra={1} distance={6} angle={0.35} attenuation={5} anglePower={4} intensity={2} {...props} />
}
useThree
이 후크를 사용하면 기본 렌더러, 장면, 카메라와 같이 내부에 보관된 모든 기본 객체에 액세스할 수 있다. 또한 화면 및 뷰포트 좌표에서 캔버스의 현재 크기를 제공한다. 예를 들어 브라우저 크기를 조정한 후 새 측정값을 얻을 경우 변경할 수 있는 모든 기본값에도 동일하게 적용됨.
lerp
첫 번째 인수로 보간할 다른 벡터와 알파라고 불리는 두 번째 인자를 사용한다. 알파는 위치가 새로운 벡터를 향해 보관되어야 하는 속도라고 생각할 수 있다.
updateMatrixWorld()
로컬 변환을 업데이트한다.
SpotLight
이 빛은 원뿔을 따라 한 방향으로 한 지점에서 방출되는데, 원뿔은 원뿔의 크기가 커질수록 빛으로부터 멀어질수록 더 커진다.
penumbra
반음파로 인해 감쇠되는 스포트라이트 원뿔의 백분율. 0과 1 사이의 값을 사용한다. 기본값은 0.
distance
조명의 최대 범위. 기본값은 0(제한 없음).
angle
상한이 Math.PI/2.인 방향에서 빛의 최대 분산 각도.
const Ball = () => {
const depthBuffer = useDepthBuffer({ frames: 1 });
return (
<>
<MovieSpot depthBuffer={depthBuffer} color="yellow" position={[1, 3, 0]} />
<MovieSpot depthBuffer={depthBuffer} color="#eee" position={[-1, 3, 0]} />
<mesh castShadow receiveShadow>
<sphereGeometry args={[1, 46, 46]} />
<meshStandardMaterial color={"orange"} metalness={0.1} roughness={0.3} />
</mesh>
<mesh receiveShadow position={[0, -1, 0]} rotation-x={-Math.PI / 2}>
<planeGeometry args={[50, 50]} />
<meshPhongMaterial color={"#202020"}/>
</mesh>
</>
)
}
const Example = () => {
return (
<Canvas
style={{ height: 600, width: 600 }}>
<color attach="background" args={['#202020']} />
<ambientLight intensity={0.1} />
<Suspense fallback={null}>
<Ball />
</Suspense>
<OrbitControls />
</Canvas>
)
}
export default Rounded;
파일 적용법. 이전글 참고
https://velog.io/@iepppop/react-three.js-%EC%A0%81%EC%9A%A9%EB%B2%95
사용한 파일 주소
https://sketchfab.com/3d-models/reinhardt-helmet-overwatch-fc6201d0eeed4fb5ab4b19c9935e537c
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { SpotLight, useDepthBuffer, Html, useGLTF } from '@react-three/drei'
import { OrbitControls } from "@react-three/drei";
import { Suspense, useRef } from 'react';
import { Vector3 } from 'three';
import * as THREE from 'three';
function Model({ ...props }) {
const group = useRef()
const { nodes, materials } = useGLTF('/scene.gltf')
//이 부분이 마우스를 움직일 때 같이 움직이게 해줌.
useFrame(({ pointer }) => (group.current.rotation.y = THREE.MathUtils.lerp(group.current.rotation.y, pointer.x * (Math.PI / 5), 0.01)))
return (
// 스케일 값을 따로 조정해줬다.
<group ref={group} {...props} dispose={null} scale={0.7} position={[0,-0.7,0]}>
<group rotation={[-Math.PI / 2, 0, 0]} scale={1.47}>
<group rotation={[Math.PI / 2, 0, 0]}>
<mesh geometry={nodes.pCube1_joues_0.geometry} material={materials.joues} />
<group position={[0.25, 0.21, -1.15]} rotation={[-0.04, 0, -Math.PI / 2]} scale={[0.31, 1, 0.31]}>
<mesh geometry={nodes.pCylinder1_Crane_0.geometry} material={materials.Crane} />
</group>
<mesh geometry={nodes.pCube2_Piques_0.geometry} material={materials.Piques} />
<mesh geometry={nodes.pCube3_blinn2_0.geometry} material={materials.blinn2} />
</group>
</group>
</group>
)
}
const Lights = () => {
const groupL = useRef()
const groupR = useRef()
const front = useRef()
useFrame(({ pointer }) => {
groupL.current.rotation.y = THREE.MathUtils.lerp(groupL.current.rotation.y, -pointer.x * (Math.PI / 2), 0.1)
groupR.current.rotation.y = THREE.MathUtils.lerp(groupR.current.rotation.y, pointer.x * (Math.PI / 2), 0.1)
front.current.position.x = THREE.MathUtils.lerp(front.current.position.x, pointer.x * 12, 0.05)
front.current.position.y = THREE.MathUtils.lerp(front.current.position.y, 2 + pointer.y * 4, 0.05)
})
return (
<>
<group ref={groupL}>
<pointLight position={[0, 7, -15]} distance={15} intensity={10} />
</group>
<group ref={groupR}>
<pointLight position={[0, 7, -15]} distance={15} intensity={10} />
</group>
<spotLight castShadow ref={front} penumbra={0.75} angle={Math.PI / 4} position={[10, 50, 8]} distance={10} intensity={10} shadow-mapSize={[2048, 2048]} />
</>
)
}
const Zoom = () => {
useFrame((state) => {
state.camera.position.lerp({ x: 0, y: 0, z: 12 }, 0.005)
state.camera.lookAt(0, 0, 0)
})
}
const Example = () => {
return (
<Canvas shadows camera={{ position: [0, 1.5, 14], fov: 30 }}>
<color attach="background" args={['#202020']} />
<fog attach="fog" args={['black', 0, 20]} />
<pointLight position={[0, 10, -10]} intensity={1} />
<Suspense fallback={ <Html center className="loader">
LOADING
</Html>}>
<Model />
<Zoom />
<Lights />
</Suspense>
<OrbitControls />
</Canvas>
)
}
export default Example;