R3F(React Three Fiber)基础篇

之前一直在做ThreeJS方向,整理了两篇R3F(React Three Fiber)的文档,这是基础篇,如果您的业务场景需要使用R3F,您又对R3F不太了解,或者不想使用R3F全英文文档,您可以参考一下这篇,作为学习R3F的基础教程使用。

经验篇:⬇️

R3F(React Three Fiber)经验篇

一、R3F基础

文章目录

  • 一、R3F基础
    • [一、React Three Fiber基础](#一、React Three Fiber基础)
      • [1. 基础场景搭建](#1. 基础场景搭建)
      • [2. 自定义Geometry](#2. 自定义Geometry)
      • [3. useFrame](#3. useFrame)
      • [4. toneMapping (色调映射)](#4. toneMapping (色调映射))
    • 二、@react-three/drei
      • [1. OrbitControls](#1. OrbitControls)
      • [2. TransformControls](#2. TransformControls)
      • [3. PivotControls](#3. PivotControls)
      • [4. Html](#4. Html)
      • [5. Text](#5. Text)
      • [6. Float](#6. Float)
      • [7. 镜面反射材质](#7. 镜面反射材质)
    • 三、Debugger
      • [1. leva](#1. leva)
      • [2. r3f-perf](#2. r3f-perf)
      • [3. useHelper](#3. useHelper)
    • 三、Environment
      • [1. 设置背景颜色](#1. 设置背景颜色)
      • [2. 配置阴影](#2. 配置阴影)
      • [3. 天空盒](#3. 天空盒)
      • [4. 场景HDR文件](#4. 场景HDR文件)
    • [四、Load Models](#四、Load Models)
      • [1. useLoader](#1. useLoader)
      • [2. Suspense](#2. Suspense)
      • [3. useGLTF](#3. useGLTF)
      • [4. GLTF Clone(模型克隆)](#4. GLTF Clone(模型克隆))
      • [5. GLTF Animation](#5. GLTF Animation)
      • [6. Text3D](#6. Text3D)
      • [7. useMatcapTexture](#7. useMatcapTexture)
      • [8. Multiple model processing](#8. Multiple model processing)
    • [五、Mouse Event](#五、Mouse Event)
      • [1. EventHandler](#1. EventHandler)
      • [2. Event Kind](#2. Event Kind)
    • [六、Post Processing](#六、Post Processing)
      • [1. Install](#1. Install)
      • [2. multisampling 多重采样](#2. multisampling 多重采样)
      • [3. vignette 晕映](#3. vignette 晕映)
      • [4. Glitch 失灵](#4. Glitch 失灵)
      • [5. Noise 噪点](#5. Noise 噪点)
      • [6. Bloom](#6. Bloom)
      • [7. DepthOfField 景深](#7. DepthOfField 景深)
    • 七、Physics
      • [1. Installation](#1. Installation)
      • [**2. RigidBody:刚体**](#2. RigidBody:刚体)
      • [3. Controll rigidbody movement](#3. Controll rigidbody movement)
      • [4. grvity 重力](#4. grvity 重力)
      • [5. gravityScale,restitution,friction](#5. gravityScale,restitution,friction)
      • [6. RigidBody mass 刚体重力](#6. RigidBody mass 刚体重力)

一、React Three Fiber基础

1. 基础场景搭建

typescript 复制代码
function App() {

  const cameraSettings = {
    fov: 1,
    zoom: 100,
    near: 0.1,
    far: 200,
    position: new Vector3(3, 2, 6)
  }

  return (
    <>
      <Canvas
        orthographic={true}
        camera={{ ...cameraSettings }}
      >
        <Experience />
      </Canvas>
    </>
  )
}

相机场景配置

typescript 复制代码
const cameraSettings = {
  // fov: 1,
  // zoom: 100,
  near: 0.1,
  far: 200,
  position: new Vector3(3, 2, 6)
}

<Canvas
  gl={ {
    antialias: true,
    toneMapping: ACESFilmicToneMapping,
    outputEncoding: LinearEncoding
  } }
  // orthographic={true}  // orthographic 会影响Environment组件
  camera={{ ...cameraSettings }}
  shadows={true}
  onCreated={created}
>
  <Experience />
</Canvas>

Experience.tsx

typescript 复制代码
import "@react-three/fiber";
import { useRef } from "react";
import { Group } from "three";
import {extend, useFrame, useThree} from "@react-three/fiber";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import CustomObject from "./CustomObject.tsx";

extend({ OrbitControls })

export default function Experience() {

	const { camera, gl } = useThree();

	const cubeRef = useRef<any>(null!);
	const groupRef = useRef<Group>(null!);

	useFrame(() => {
		cubeRef.current.rotation.y += 0.01;
	})
    
	return <>

		{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
		{/* @ts-ignore */}
		<orbitControls args={ [camera, gl.domElement] } />
		<directionalLight position={ [ 1, 2, 3 ] } intensity={ 1.5 } />
		<ambientLight intensity={ 0.5 } />

		<group ref={groupRef}>
			<mesh>
				<sphereGeometry/>
				<meshStandardMaterial color={'orange'}/>
			</mesh>
			<mesh ref={cubeRef} rotation-y={Math.PI * 0.25} position-x={2} scale={1.5}>
				<boxGeometry/>
				<meshStandardMaterial color={"mediumpurple"} wireframe={false}/>
			</mesh>
			<mesh position-y={-1} rotation-x={-Math.PI * 0.5} scale={10}>
				<planeGeometry/>
				<meshStandardMaterial color={"greenyellow"}/>
			</mesh>
		</group>
	</>
}

2. 自定义Geometry

typescript 复制代码
import { DoubleSide, BufferGeometry } from "three";
import {useEffect, useMemo, useRef} from "react";

export default function CustomObject() {

	const geometryRef = useRef<BufferGeometry>(null!);

	const verticesCount = 10 * 3;
	
	const positions = useMemo(() => {
		const positions = new Float32Array(verticesCount * 3);
		for (let i = 0; i < verticesCount; i++) {
			positions[i] = (Math.random() - 0.5) * 3;
		}
		
		return positions;
	}, [verticesCount]);

	useEffect(() => {
		geometryRef.current.computeVertexNormals();
	}, []);

	return <mesh>
		<bufferGeometry ref={geometryRef}>
			<bufferAttribute
				attach={"attributes-position"}
				count={verticesCount}
				itemSize={3}
				array={positions}
			/>
		</bufferGeometry>
		<meshBasicMaterial color={"red"} side={ DoubleSide } />
	</mesh>
}

3. useFrame

typescript 复制代码
useFrame((state, delta) => {

  // delta 一直是 1.33
  console.log(delta);
  // 开始渲染时间
  console.log(state.clock.getElapsedTime());
  console.log(state.clock.elapsedTime);

  cubeRef.current.rotation.y += delta;
})

摄像机周期运动

typescript 复制代码
useFrame((state, delta) => {
    const angle = state.clock.elapsedTime;
    state.camera.position.x = Math.sin(angle);
    state.camera.position.z = Math.cos(angle);
    state.camera.lookAt(0, 0, 0);
})

让Canvas绘制得更好的一些效果

typescript 复制代码
<Canvas
    gl={ {
    	antialias: true // 抗锯齿
    } }
    orthographic={true} // 效果不详
    camera={{ ...cameraSettings }}
>

4. toneMapping (色调映射)

https://threejs.org/docs/#api/en/constants/Renderer

CineonToneMapping,ACESFilmicToneMapping(HDR)

typescript 复制代码
<Canvas
    gl={ {
    	antialias: true,
    	toneMapping: ACESFilmicToneMapping
    } }
	orthographic={true}
	camera={{ ...cameraSettings }}
>

二、@react-three/drei

1. OrbitControls

自由旋转镜头组件

typescript 复制代码
import {extend, useFrame, useThree} from "@react-three/fiber";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

extend({ OrbitControls })

const { camera, gl } = useThree();
<orbitControls args={ [camera, gl.domElement] } />

typescript 复制代码
import {OrbitControls} from "@react-three/drei";
<OrbitControls />

2. TransformControls

物体Transform组件(会在物体中心多出一个坐标系)

typescript 复制代码
import { TransformControls } from "@react-three/drei";
<TransformControls>
    <mesh ref={cubeRef} rotation-y={Math.PI * 0.25} position-x={2} scale={1.5}>
    	<boxGeometry/>
    	<meshStandardMaterial color={"mediumpurple"} wireframe={false}/>
    </mesh>
</TransformControls>

另一种写法:

typescript 复制代码
<TransformControls object={ cubeRef } />

注意

同时使用OrbitControls和TransformControls,OrbitControls要给makeDefault属性。

typescript 复制代码
<OrbitControls makeDefault={true}/>

Props

typescript 复制代码
TransformControlsProps:
	mode?: 'translate' | 'rotate' | 'scale';

3. PivotControls

效果类似TransformControls,但是好像比它好用。

typescript 复制代码
<PivotControls anchor={[2, 0, 0]} depthTest={false}>

Props

typescript 复制代码
PivotControlsProps
	scale?: number;
	lineWidth?: number;
	rotation?: [number, number, number];
	axisColors?: [string | number, string | number, string | number];
	anchor?: [number, number, number];
	depthTest?: boolean;

4. Html

创建3D字体(HTML标签)

typescript 复制代码
<mesh position-x={-2}>
    <sphereGeometry/>
    <meshStandardMaterial color={'orange'}/>
    <Html
        wrapperClass={ 'label' } // label > div { color: white }
        position={ [1, 1, 0] }
        center
        distanceFactor={ 0.01 } // 越大,字体越大
        occlude={ [ cubeRef ] } // 文字遮挡效果
    >
    	This is sphere.
    </Html>
</mesh>

Generating a 3D text geometry has its limits

  1. We can notice the polygons
  2. Takes a lot of CPU resources
  3. Some fonts won't look very good
  4. Doesn't support line breaks

5. Text

一个更好的,性能开销更少的文字组件,但是不支持occlude。

typescript 复制代码
<Text
	font={ '' }
    fontSize={ 1 }
    color={'salmon'}
    position-y={ 2 }
    maxWidth={ 3 }
    textAlign={ 'center' }
>
	I Love R3F
</Text>

6. Float

让一个物体 飘来飘去

typescript 复制代码
<Float speed={4}>
    <Text
        font={''}
        fontSize={ 1 }
        color={'salmon'}
        position-y={ 2 }
        maxWidth={ 3 }
        textAlign={ 'center' }
    >
    I Love R3F
    </Text>
</Float>

7. 镜面反射材质

注意:仅可用于平面

typescript 复制代码
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[-10, 0, 25]}>
    <planeGeometry args={[250, 250]} />
    <MeshReflectorMaterial
        blur={[300, 100]}
        resolution={2048}
        mixBlur={1}
        mixStrength={80}
        roughness={1}
        depthScale={1.2}
        minDepthThreshold={0.4}
        maxDepthThreshold={1.4}
        color="#050505"
        metalness={0.5}
        mirror={0}
    />
</mesh>

三、Debugger

1. leva

useControls

typescript 复制代码
import { useControls, button } from 'leva';
const {
	position: ct_position,
	color: ct_color,
	visible: ct_visible,
} = useControls('sphere', {
	position: {
		value: { x: -2, y: 0 },
		step: 0.01,
		joystick: 'invertY'
	},
	color: '#ff0000',
	visible: true,
	myInterval: {
		min: 0,
		max: 10,
		value: [ 4, 5 ]
	},
	choice: { options: ['a', 'b', 'c'] },
	clickMe: button(() => console.log('ok'))
})

2. r3f-perf

typescript 复制代码
import { Perf } from "r3f-perf";
<Perf position={'top-left'}/>

3. useHelper

可以展示光线的路径

typescript 复制代码
import { useHelper, } from "@react-three/drei";
import { DirectionalLightHelper, DirectionalLight } from "three";

const directionalLight = useRef<DirectionalLight>(null!);
useHelper(directionalLight, DirectionalLightHelper);

三、Environment

1. 设置背景颜色

  1. 通过 color 标签
typescript 复制代码
<Canvas
  gl={ {
    antialias: true,
    toneMapping: ACESFilmicToneMapping,
    outputEncoding: LinearEncoding
  } }
  orthographic={true}
  camera={{ ...cameraSettings }}
  shadows={true}
>
  <color args={ ['#ff0000'] } attach={"background"} />
  <Experience />
</Canvas>
  1. 通过 onCreated 钩子函数
typescript 复制代码
const created = (state: RootState) => {
  console.log('canvas created! ');
  const { gl, scene } = state;
  gl.setClearColor('#ff0000', 1);
  scene.background = new Color('red');
}

<Canvas
  gl={ {
    antialias: true,
    toneMapping: ACESFilmicToneMapping,
    outputEncoding: LinearEncoding
  } }
  orthographic={true}
  camera={{ ...cameraSettings }}
  shadows={true}
  onCreated={created}
>
  <Experience />
</Canvas>
  1. 通过CSS样式
css 复制代码
*, html, body {
  padding: 0;
  margin: 0;
}

html,
body,
#root {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

2. 配置阴影

  1. 阴影参数
typescript 复制代码
{/*shadow-mapSize:阴影精度,越大精度越高*/}
{/*shadow-camera-top,right,bottom,left 阴影是否柔和,越大阴影越柔和*/}
<directionalLight
  ref={ directionalLight }
  position={ [ 1, 2, 3 ] }
  intensity={ 1.5 }
  castShadow={ true }
  shadow-mapSize={ [1024 * 4, 1024 * 4] }
  shadow-camera-near={ 1 }
  shadow-camera-far={ 10 }
  shadow-camera-top={ 200 }
  shadow-camera-right={ 200 }
  shadow-camera-bottom={ - 200 }
  shadow-camera-left={ - 200 }
/>
  1. 阴影烘焙,在适当的场景下 添加BakeShadows组件
typescript 复制代码
<BakeShadows />
  1. 柔和阴影SoftShadows
typescript 复制代码
<SoftShadows
  size={100}
  focus={0}
  samples={10}
/>
  1. 累积阴影AccumulativeShadows
typescript 复制代码
<AccumulativeShadows
  position={ [ 0, - 0.99, 0 ] }
  scale={ 10 }
  color={ '#316d39' }
  opacity={ 0.8 }
  // frames={ Infinity }
  // temporal={ true }
  // blend={ 100 }
>
  <RandomizedLight
    amount={ 8 }
    radius={ 1 }
    ambient={ 0.5 }
    intensity={ 1 }
    position={ [ 1, 2, 3 ] }
    bias={ 0.001 }
  />
</AccumulativeShadows>
  1. 接触阴影ContactShadows
typescript 复制代码
const {
  position: cs_position,
  color: cs_color,
  opacity: cs_opacity,
  blur: cs_blur,
} = useControls('ContactShadows', {
  position: { value: { x: 0, y: - 0.99 }, step: 0.01, joystick: 'invertY' },
  color: '#000000',
  opacity: { value: 0.5, min: 0, max: 1 },
  blur: { value: 1, min: 0, max: 10 },
  clickMe: button(() => console.log('ok'))
})

<ContactShadows
  position={ [cs_position.x, cs_position.y, 0] }
  scale={ 10 }
  resolution={ 128 }
  far={ 5 }
  color={ cs_color }
  blur={ cs_blur }
  opacity={ cs_opacity }
/>

3. 天空盒

typescript 复制代码
<Sky distance={20} />

4. 场景HDR文件

HDR文件下载:https://polyhaven.com/

typescript 复制代码
<Environment
	background
	files={ '/industrial.hdr' }
    // ground={{
	//   radius: 1,
	//   scale: 100,
	//   height: 0
	// }}
    // preset="apartment" 预设场景
/>

通过suspend-react,可将Environment转为异步组件,支持Suspense的方式调用

typescript 复制代码
import { suspend } from 'suspend-react'
const city = import('@pmndrs/assets/hdri/city.exr').then((module) => module.default)

<Environment files={suspend(city)} />

四、Load Models

1. useLoader

typescript 复制代码
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js";

const modelCyberpunk = useLoader(GLTFLoader, './cyberpunk.glb');

自定义加载器

https://threejs.org/docs/#examples/en/loaders/DRACOLoader

Draco是一个用于压缩和解压缩 3D 网格和点云的开源库。压缩后的几何图形可以明显更小,但代价是客户端设备上需要额外的解码时间。

typescript 复制代码
const modelCyberpunk = useLoader(GLTFLoader, './cyberpunk.glb', loader => {
	const dracoLoader = new DRACOLoader()
	dracoLoader.setDecoderPath('./draco/')
	loader.setDRACOLoader(dracoLoader);
});

2. Suspense

typescript 复制代码
<Suspense fallback={<PlaceHolder scale={5}/>}>
	<ModelCyberpunk />
</Suspense>

PlaceHolder.tsx

typescript 复制代码
const PlaceHolder = (props: MeshProps) => {
	return <mesh {...props}>
		<boxGeometry />
		<meshStandardMaterial wireframe={true} color={ 'red' } />
	</mesh>
}

export default PlaceHolder;

3. useGLTF

Secondary Encapsulation of useLoadler(useLoadler的二次封装)

typescript 复制代码
export declare function useGLTF<T extends string | string[]>(path: T, useDraco?: boolean | string, useMeshOpt?: boolean, extendLoader?: (loader: GLTFLoader) => void): T extends any[] ? import("three-stdlib").GLTF[] : import("three-stdlib").GLTF;

可选配置:useDraco,useMeshOpt,extendLoader

Extend the useGLTF return value type(对useGLTF 的返回值类型进行拓展)

typescript 复制代码
declare type GLTFEnhance = import('three-stdlib').GLTF & {
	nodes: Record<string, import("three").Mesh>;
	materials: Record<string, import("three").MeshStandardMaterial>;
};

const { nodes, materials } = useGLTF('/cyberpunk.glb', true) as C_GLTF;

GLTF 预加载(preload)

typescript 复制代码
// Remember to write outside the component.
useGLTF.preload('/cyberpunk.glb')

4. GLTF Clone(模型克隆)

Object3D.clone()

typescript 复制代码
/**
  * Returns a clone of `this` object and optionally all descendants.
  * @param recursive If true, descendants of the object are also cloned. Default `true`
  *
  * clone(recursive?: boolean): this;
  */
<primitive object={scene.clone()}></primitive>

Clone Component

https://github.com/pmndrs/drei#clone

Declarative abstraction around THREE.Object3D.clone. This is useful when you want to create a shallow copy of an existing fragment (and Object3D, Groups, etc) into your scene, for instance a group from a loaded GLTF. This clone is now re-usable, but it will still refer to the original geometries and materials.

typescript 复制代码
import {Clone} from "@react-three/drei";

<group {...props} dispose={null}>
    <Clone object={scene.clone()} position-x={ -10 }></Clone>
    <Clone object={scene.clone()} position-x={ 0 }></Clone>
    <Clone object={scene.clone()} position-x={ 10 }></Clone>
</group>

5. GLTF Animation

typescript 复制代码
const { animations: gAnimations, scene} = useGLTF('./dog.glb', true) as GLTFEnhance;
const { actions } = useAnimations(gAnimations, scene);

useEffect(() => {
  const play_dead =  actions["0|play_dead_0"]!
  const rollover =  actions["0|rollover_0"]!
  const shake =  actions["0|shake_0"]!
  const sitting =  actions["0|sitting_0"]!
  const standing =  actions["0|standing_0"]!


  shake.play();

  window.setTimeout(() => {
    rollover.play();
    rollover.crossFadeFrom(
      shake, 1, false
    )
  }, 10000)

}, []);

useController

typescript 复制代码
const { 
  animations: gAnimations, 
  scene
} = useGLTF('./dog.glb', true) as GLTFEnhance;

const { actions, names } = useAnimations(gAnimations, scene);
const { animationName } = useControls({ animationName: { options: names } })

useEffect(() => {
  const action = actions[animationName]!
  action.fadeIn(0.5).play()
  return () => { 
    action.fadeOut(0.5) 
  }
}, [animationName]);

6. Text3D

Documentation:https://github.com/pmndrs/drei#text3d

Example:

typescript 复制代码
const [ matcapTexture ] = useMatcapTexture('3E2335_D36A1B_8E4A2E_2842A5', 256);

<Text3D
  font={'./Regular.json'}
  size={ 0.75 }
  height={ 0.2 }
  curveSegments={ 12 }
  bevelEnabled={ true }
  bevelThickness={ 0.02 }
  bevelSize={ 0.02 }
  bevelOffset={ 0 }
  bevelSegments={ 5 }
>

The purpose of bevel-ralated properties is to make the font smoother.

bevel 的作用是让字体变得更加圆滑。

font属性需要填写一个被称作 typeface.json的字体文件,可在这个网站 https://gero3.github.io/facetype.js/ 将原始的ttf文件经过转化后得到。

The font props requires filling in a font file called typeface.json,which can be obtained by converting the orignal ttf file on https://gero3.github.io/facetype.js website.

7. useMatcapTexture

https://github.com/pmndrs/drei#usematcaptexture

The built in Texture can use in testing, not in the production environment.

内置的Texture,可用于测试,别使用在生产环境。

typescript 复制代码
/**
  * The name of seconds parameters is format, we can choose between 64, 128, 256, 512, 1024
  * In our case, 256 is more than enough and you should try to use the smallest possible size   for performance reasons.
  */
const [ matcapTexture ] = useMatcapTexture('3E2335_D36A1B_8E4A2E_2842A5', 256);

8. Multiple model processing

You shoud write the geometry, materal in mesh property when repeatedly rendering a model of the same geometry with the same materal. It performance better this way.

typescript 复制代码
{
	[...Array(100)].map((_, index) =>
		<mesh
			key={new Date().toString() + index}
			position={[
				(Math.random() - 0.5) * 10,
				(Math.random() - 0.5) * 10,
				(Math.random() - 0.5) * 10,
			]}
			scale={0.2 + Math.random() * 0.2}
			rotation={[
				Math.random() + Math.PI,
				Math.random() + Math.PI,
				Math.random() + Math.PI,
			]}
			geometry={torusGeometry}
			material={material}
		>
			<torusGeometry args={[1, 0.6, 16, 32]} />
			<meshMatcapMaterial matcap={matcapTexture}/>
		</mesh>
	)
}

↓↓↓

typescript 复制代码
const Text3DHello: FC = memo(() => {
	
	const [matcapTexture] = useMatcapTexture('3E2335_D36A1B_8E4A2E_2842A5', 256);

	const [torusGeometry, setTorusGeometry] = useState<TorusGeometry>();
	const [material, setMaterial] = useState<MeshMatcapMaterial>();

	return <>
		<torusGeometry ref={(torusGeometry) => setTorusGeometry(torusGeometry!)} args={[1, 0.6, 16, 32]} />
		<meshMatcapMaterial ref={(material) => setMaterial(material!) } matcap={matcapTexture}/>

		<Center>
			<Text3D
				font={'./Regular.json'}
				size={0.75}
				height={0.2}
				curveSegments={12}
				bevelEnabled={true}
				bevelThickness={0.02}
				bevelSize={0.02}
				bevelOffset={0}
				bevelSegments={5}
			>
				你好,React Three Fiber !
				<meshMatcapMaterial matcap={matcapTexture}/>
			</Text3D>
		</Center>
		{
			[...Array(100)].map((_, index) =>
				<mesh
					key={new Date().toString() + index}
					position={[
						(Math.random() - 0.5) * 10,
						(Math.random() - 0.5) * 10,
						(Math.random() - 0.5) * 10,
					]}
					scale={0.2 + Math.random() * 0.2}
					rotation={[
						Math.random() + Math.PI,
						Math.random() + Math.PI,
						Math.random() + Math.PI,
					]}
					geometry={torusGeometry}
					material={material}
				/>
			)
		}
	</>
});

The better approach is to use OOP.

typescript 复制代码
import {FC, memo, useEffect} from "react";
import {Center, Text3D, useMatcapTexture} from "@react-three/drei";
import {MeshMatcapMaterial, TorusGeometry} from "three";

const torusGeometry = new TorusGeometry(1, 0.6, 16, 32);
const material = new MeshMatcapMaterial();

const Text3DHello: FC = memo(() => {
	
	const [matcapTexture] = useMatcapTexture('3E2335_D36A1B_8E4A2E_2842A5', 256);

	useEffect(() => {
        matcapTexture.needsUpdate = true;
		material.matcap = matcapTexture;
		material.needsUpdate = true;
	}, [matcapTexture]);

	return <>

		<Center>
			<Text3D
				font={'./Regular.json'}
				size={0.75}
				height={0.2}
				curveSegments={12}
				bevelEnabled={true}
				bevelThickness={0.02}
				bevelSize={0.02}
				bevelOffset={0}
				bevelSegments={5}
			>
				你好,React Three Fiber !
				<meshMatcapMaterial matcap={matcapTexture}/>
			</Text3D>
		</Center>
		{
			[...Array(100)].map((_, index) =>
				<mesh
					key={new Date().toString() + index}
					position={[
						(Math.random() - 0.5) * 10,
						(Math.random() - 0.5) * 10,
						(Math.random() - 0.5) * 10,
					]}
					scale={0.2 + Math.random() * 0.2}
					rotation={[
						Math.random() + Math.PI,
						Math.random() + Math.PI,
						Math.random() + Math.PI,
					]}
					geometry={torusGeometry}
					material={material}
				/>
			)
		}
	</>
});

export default Text3DHello;

Use useFrame and useRef to add animation.

typescript 复制代码
const donuts = useRef<Mesh[]>([]);

useFrame((_, delta) => {
	for (const donut of donuts.current) donut.rotation.y += delta * 0.5
})

// ...
<mesh ref={(mesh) => { donuts.current[index] = mesh! }}
// ...

or use group tag**(not recommanded)**

typescript 复制代码
const donutsGroup = useRef<Group>(null!);

useFrame((_, delta) => {
	for (const donut of donutsGroup.current.children) donut.rotation.y += delta * 0.1
}

<group ref={ donutsGroup }>
	{ [...Array(100)].map((_, index) => <mesh 
	// ... }

五、Mouse Event

1. EventHandler

typescript 复制代码
const eventHandler = (event: ThreeEvent<MouseEvent>) => {
	console.log('event.uv', event.distance) // distance between camera and hit point.
	console.log('event.uv', event.uv)
	console.log('event.point', event.point) // Hit point coordinates (坐标).
	console.log('event.object', event.object)
	console.log('event.eventObject', event.eventObject) // Usually, eventObject is the same as object
	console.log('event.x', event.x) // 2D Screen coordinates of the pointer
	console.log('event.y', event.y)
	console.log('event.shiftKey', event.shiftKey)
	console.log('event.ctrlKey', event.ctrlKey)
	console.log('event.metaKey', event.metaKey) // Click while holding down command / win key.
}

2. Event Kind

  • onClick

    • CLICK or CLICK with CTRL、SHIFT、COMMAND(WIN)、ALT
    • shiftKey,ctrlKey,metaKey,altKey
  • onContextMenu

    • RIGHT CLICK or CTRL + LEFT CLICK.
    • On a mobile, by pressing down for some time.
  • onDoubleClick

    • It works bisically the same as onClick.
    • The delay between the first and second click/tap is defined by the OS
  • onPointerUp

  • onPointerDown

  • onPointerOver and onPointerEnter

    • When the cursor or finger just went above the object
  • onPointerMove

  • onPointerMissed

    • When the user clicks outside of the object. ( Cannot get the event.object parameter ).

六、Post Processing

1. Install

We need tow dependencies,@react-three/postprocessing,postprocesssing, But for now, the only we neeed to install is @react-three/postprocessing since the dependency will also install postprocesssing.

json 复制代码
"@react-three/drei": "^9.85.1",
"@react-three/fiber": "^8.14.2",
"@react-three/postprocessing": "2.6",
"postprocessing": "~6.31.2",
"r3f-perf": "^7.1.2",
"three": "~0.151.0",
"three-stdlib": "^2.27.0"

2. multisampling 多重采样

The default value is 8.

typescript 复制代码
<EffectComposer multisampling={16} />

3. vignette 晕映

The default effect is add a black mask around the sceen.

typescript 复制代码
<Vignette offset={0.3} darkness={0.9} />

You can specify the blending(混合、交融) method.

typescript 复制代码
import { BlendFunction } from "postprocessing";
<Vignette
    offset={0.3}
    darkness={0.9}
    blendFunction={ BlendFunction.ALPHA }
/>

4. Glitch 失灵

Create snowflake(雪花) glitch effect like an old-fashioned TV.

typescript 复制代码
<Glitch delay={ new Vector2(1, 1) } mode={ GlitchMode.SPORADIC } />

Delay attribute reviews a value of type Vector2.It represents the delay time for the horizontal and vertical axes.

The same effect to other attributes.

typescript 复制代码
delay?: import("three").Vector2;
duration?: import("three").Vector2;
strength?: import("three").Vector2;

Effect Mode

typescript 复制代码
mode: typeof GlitchMode[keyof typeof GlitchMode];

export enum GlitchMode {
	DISABLED,
    SPORADIC,
    CONSTANT_MILD,
    CONSTANT_WILD,
}

5. Noise 噪点

typescript 复制代码
<Noise 
    blendFunction={ BlendFunction.SOFT_LIGHT } 
    premultiply  // effect overlay
/>

BlendFunction

typescript 复制代码
BlendFunction.OVERLAY // 叠加
BlendFunction.SCREEN  // It doesn't work well in bright scenes
BlendFunction.SOFT_LIGHT
BlendFunction.AVERAGE

6. Bloom

Bloom can be used to build an object glow(发光,同luminescence)effect

1、Set material attriblue.

Set larger value for color attriblue.

typescript 复制代码
<meshStandardMaterial 
	color={ [ 1.5 * 30, 1 * 30, 4 * 30 ] } 
	toneMapped={ false } 
/>

Or set standard color, and set emissive attriblue and emissiveIntensity attibute.

typescript 复制代码
<meshStandardMaterial 
	color={ 'white' } 
	emissive={ 'yellow' } 
	emissiveIntensity={ 10 } 
	toneMapped={ false } 
/>

2、Set Bloom effect component attriblue.

typescript 复制代码
<Bloom
	mipmapBlur={ true } // always true
	intensity={ 1 }
	luminanceSmoothing={ 2 } // 滤波
	luminanceThreshold={ 10 } // 阈值
/>

7. DepthOfField 景深

typescript 复制代码
<DepthOfField
    focusDistance={ 0.025 }
    focalLength={ 0.025 }
    bokehScale={ 6 }
/>

七、Physics

1. Installation

shell 复制代码
pnpm install @react-three/rapier

2. RigidBody:刚体

  • colliders:对撞机,设置刚体碰撞形状,ball 球,cuboid 矩形,hull Mesh的船体形状,trimesh Mesh网线形状

    typescript 复制代码
    export declare type RigidBodyAutoCollider = "ball" | "cuboid" | "hull" | "trimesh" | false;

Scene Example:

tsx 复制代码
<Physics debug={true}>

	<RigidBody colliders={'ball'} type={"dynamic"}>
		<mesh castShadow={true} position={[0, 10, 0]}>
			<sphereGeometry />
			<meshStandardMaterial color={'orange'} />
		</mesh>
	</RigidBody>

	<RigidBody colliders={'trimesh'}>
		<mesh castShadow={true} position={[0, 1, 0]} rotation={[Math.PI * 0.5, 0, 0]}>
			<torusGeometry args={[1, 0.5, 16, 32]} />
			<meshStandardMaterial color={'mediumpurple'} />
		</mesh>
	</RigidBody>

	<RigidBody type={"fixed"}>
		<mesh receiveShadow={true} position={[0, 0, 0]} scale={1}>
			<boxGeometry args={[10, 0.5, 10]}/>
			<meshStandardMaterial color={"greenyellow"}/>
		</mesh>
	</RigidBody>

</Physics>

You can use CuboidCollider Component to add rigid body shape manually.

tsx 复制代码
<RigidBody colliders={false} position={[0, 1, 0]} rotation={[Math.PI / 2, 0, 0]}>
	<CuboidCollider args={[1.5, 1.5, 0.5]} />
	<CuboidCollider args={[1, 1, 1]} />
	<mesh castShadow={true}>
		<torusGeometry args={[1, 0.5, 16, 32]} />
		<meshStandardMaterial color={'mediumpurple'} />
	</mesh>
</RigidBody>

BallCollider, the ball shape of rigid bidy.

typescript 复制代码
<RigidBody colliders={false} position={[0, 1, 0]} rotation={[Math.PI / 2, 0, 0]}>
	<BallCollider args={[1.5]} />
	<mesh castShadow={true}>n
		<torusGeometry args={[1, 0.5, 16, 32]} />
		<meshStandardMaterial color={'mediumpurple'} />
	</mesh>
</RigidBody>

3. Controll rigidbody movement

typescript 复制代码
const cubeRigid = useRef<RapierRigidBody>(null!);

const { camera } = useThree();

const cubeJump = (event: ThreeEvent<MouseEvent>) => {
	cubeRigid.current.applyImpulse({ x: 0, y: 2, z: 0 }, false)
	cubeRigid.current.applyTorqueImpulse({ x: 0, y: 1, z: 0 }, false)

	const { eventObject } = event;

	// console.log(eventObject.position)

	const [epx,epy,epz] = eventObject.position

	camera.position.set(epx, epy - 4, epz + 4);
	camera.rotation.set(0, 0, 0);
}

<Physics debug={true}>

	<RigidBody
		colliders={'cuboid'}
		type={"dynamic"}
		ref={cubeRigid}
	>
		<mesh
			castShadow={true}
			position={[0, 10, 0]}
			onClick={cubeJump}
		>
			<boxGeometry/>
			<meshStandardMaterial color={'orange'} />
		</mesh>
	</RigidBody>

	<RigidBody type={"fixed"}>
		<mesh receiveShadow={true} position={[0, 0, 0]} scale={1}>
			<boxGeometry args={[10, 0.5, 10]}/>
			<meshStandardMaterial color={"greenyellow"}/>
		</mesh>
	</RigidBody>

</Physics>

4. grvity 重力

You can set the gravity size and direction.

typescript 复制代码
<Physics
  debug={true}
  gravity={[0, -1.6, 0]}
>

5. gravityScale,restitution,friction

  • gravityScale 重力倍率
  • restitution 恢复原状
  • friction 摩擦力(摩擦力是两个对象作用)
typescript 复制代码
<RigidBody
    colliders={'cuboid'}
    type={"dynamic"}
    ref={cubeRigid}
    gravityScale={ 1 }
    restitution={ 1 }
>

6. RigidBody mass 刚体重力

typescript 复制代码
const cubeRigid = useRef<RapierRigidBody>(null!);
const cubeMesh = useRef<Mesh>(null!);
const cubeJump = (_: ThreeEvent<MouseEvent>) => {
	const mass = cubeRigid.current.mass();
	cubeRigid.current.applyImpulse({ x: 0, y: 5 * mass, z: 0 }, false)
	cubeRigid.current.applyTorqueImpulse({ x: 0, y: 1 * mass, z: 0 }, false)
}
return <Physics
	debug={true}
	gravity={[0, -8, 0]}
>
    <RigidBody
        colliders={false}
        type={"dynamic"}
        ref={cubeRigid}
        gravityScale={ 1 }
        restitution={ 1 }
        friction={1}
    >
        <CuboidCollider
            args={[0.5, 0.5, 0.5]}
            position={[0, 10, 0]}
            mass={10}
        />
        <mesh
            ref={cubeMesh}
            castShadow={true}
            position={[0, 10, 0]}
            onClick={cubeJump}
        >
            <boxGeometry/>
            <meshStandardMaterial color={'orange'} />
        </mesh>
    </RigidBody>
</Physics>
相关推荐
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie1 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿2 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161773 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test4 小时前
js下载excel示例demo
前端·javascript·excel
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json