通过使用three和react-three-fiber的demo实现一样的功能来探究react-three-fiber究竟为我们做了什么事情
让我们先来实现一个最简单的正方体
three版本
import {
BoxGeometry,
Mesh,
MeshStandardMaterial,
PerspectiveCamera,
Scene,
WebGLRenderer,
} from "three";
// Create "Canvas"
const scene = new Scene();
const camera = new PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
const cube = new Mesh();
const geometry = new BoxGeometry();
const material = new MeshStandardMaterial();
cube.geometry = geometry;
cube.material = material;
scene.add(cube)
// Render
const renderer = new WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const container = document.getElementById("__next");
if (container) {
container.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
react-three-fiber:
import { Canvas } from "@react-three/fiber";
export default function R3fDemo() {
return (
<Canvas>
<mesh>
<boxGeometry/>
<meshStandardMaterial/>
</mesh>
</Canvas>
);
}
以上两个版本代码都能实现一样的功能:一个黑色正方体
在threejs中,我们创建了一个scene,并且在我们的animation中使用renderer进行渲染。这些操作,在react-three-fiber中,其实都交由来控制,也就是说,创建了scene,也创建了camera,animate&request animation的逻辑也一起由Canvas控制。
这些处理我理解都是为了将这些逻辑对我们隐藏起来,我们只需要直接使用Canvas,将我们的mesh放置在这里Canvas中,就能轻松实现我们想要的功能。
那如果我们需要自己控制camera的位置呢?其实也是可以的
使用
<Canvas camera={{position: [0,0,10]}}>
也可以从@react-three/drei 引入这个 组件来实现
说到@react-three/drei 和 @react-three/fiber
@react-three/fiber包含threejs相关的核心功能,@react-three/drei 包含更加高级的功能
我们可以将@react-three/fiber中的Canvas认为是 three中的scene,Canvas元素里面的元素都可以看成是scene.add(XXX)
也就是说我们在three中做的
const cube = new Mesh();
const geometry = new BoxGeometry();
const material = new MeshStandardMaterial();
cube.geometry = geometry;
cube.material = material;
scene.add(cube)
相当于
<Canvas>
<mesh>
XXX
</mesh>
</Canvas>
如果有多个mesh,在three中
const group = new Group();
group.add(XXX)
在react-three-fiber中可以使用,
<group>
<mesh/>
<mesh/>
</group>
和在react-three-fiber中作为Canvas的子元素,映射到three中,并不是以cube.add(xxx)这样子的方式,而是作为cube的属性geometry和material存在,那么这个在react-three-fiber中是如何实现的呢?
在react-three-fiber中,有一个叫attaching的关键概念,每个子元素如果没有attach的属性,那么都会以add执行,如果元素设置了attach属性,那么这个元素将会作为其父元素的属性
在react-three-fiber,为了方便,已经帮我们把Geometry和Material标签都加上了attach属性
总而言之,react-three-fiber做的事情,就是将three实现的东西,支持我们用react 标签的方式直接去使用
import {
ACESFilmicToneMapping,
AmbientLight,
BoxGeometry,
Mesh,
MeshStandardMaterial,
PerspectiveCamera,
PointLight,
Scene,
sRGBEncoding,
WebGLRenderer,
} from "three";
class Cube extends Mesh {
constructor() {
super();
const geometry = new BoxGeometry();
const material = new MeshStandardMaterial();
material.color.set("blue");
this.geometry = geometry;
this.material = material;
}
update() {
this.rotation.x += 0.01;
this.rotation.y += 0.01;
}
dispose() {
this.geometry.dispose();
}
}
// Create "Canvas"
const scene = new Scene();
const camera = new PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
// Add elements
const ambientLight = new AmbientLight();
scene.add(ambientLight);
const pointLight = new PointLight();
pointLight.position.set(10, 10, 10);
scene.add(pointLight);
const cube = new Cube();
scene.add(cube);
// Render
const renderer = new WebGLRenderer({ alpha: true, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.toneMapping = ACESFilmicToneMapping;
renderer.outputEncoding = sRGBEncoding;
renderer.setSize(window.innerWidth, window.innerHeight);
const container = document.getElementById("__next");
if (container) {
container.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
cube.update();
}
animate();
import { PerspectiveCamera } from "@react-three/drei";
import { Canvas, useFrame } from "@react-three/fiber";
import { useRef } from "react";
import { Mesh } from "three";
function Cube() {
const meshRef = useRef<Mesh>(null);
useFrame(() => {
if (!meshRef.current) {
return;
}
meshRef.current.rotation.x += 0.01;
meshRef.current.rotation.y += 0.01;
});
return (
<mesh ref={meshRef}>
<PerspectiveCamera />
<boxGeometry />
<meshStandardMaterial color="blue" />
</mesh>
);
}
export default function R3fDemo() {
return (
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Cube />
</Canvas>
);
}