引言
Before you read
React Three Fiber (R3F) 是一个非常方便且强大的 threejs react 封装。 使用它可以利用 React 框架搭建渲染场景。 关于框架的详细内容可以查阅 官方文档。
React Three Fiber (R3F) is a very convenient and powerful threejs react wrapper. It can be used to quickly build rendering scenes using the react framework. For more details about the framework, please refer to the official documentation.
R3F 提供了 EffectComposer
和常用的后处理,如 Bloom
, Dither
等等。但这不能满足需要自定义 Pipeline 的复杂渲染需求,
此时需要使用 three.js的原生接口。在之前的使用场景中我发现这方面的文档和案例都比较少, 所以我决定写一篇文章来分享一下我在使用 R3F 实现多Pass渲染的经验。
R3F provides EffectComposer
and common post-processing effects, such as Bloom
, Dither
, etc.
However, this cannot meet the needs of highly customized scenes. At this time, you need to use the native interface of three.js.
In the previous use cases, I found that there are relatively few documents and cases in this area, so I decided to write an article to share my experience in using R3F to achieve multi-pass rendering.
准备
Requirements
除去基本的 R3F 环境外,还将使用 pmndrs
的 postprocessing
库。你可以通过以下命令安装:
# yarn yarn add postprocessing # npm npm install postprocessing
更多关于 postprocessing
库的信息可以查阅 官方文档。
In addition to the basic R3F environment, the postprocessing
library from pmndrs
will also be used. You can install it with the following command:
# yarn yarn add postprocessing # npm npm install postprocessing
For more information about the postprocessing
library, please refer to the official documentation.
禁用 R3F RenderLoop
Disable R3F RenderLoop
R3F 默认的渲染循环对自定义 Pipeline 的支持不是很好,因此首先我们需要禁用它,建立我们自己的渲染循环来控制渲染流程。
做完这步后,你的场景将不会被渲染,Canvas
将会显示一个空白画布。
<Canvas frameLoop="never"> {/* Scene */} </Canvas>
注意 如果你已经通过 <EffectComposer />
设置了一些效果,这么做可能会导致原有的效果失效。
建议在后续自定义渲染循环中重新建立相关效果。
The default rendering loop of R3F does not support custom pipelines very well, so we need to disable it first and establish our own rendering loop to control the rendering process.
<Canvas frameLoop="never"> {/* Scene */} </Canvas>
Note If you have set some effects through <EffectComposer />
, doing this may cause the original effects to fail.
It is recommended to re-establish the relevant effects in the subsequent custom rendering loop.
创建 EffectComposer
Create EffectComposer
首先我们需要创建一个新的 EffectComposer
实例,用于管理我们的渲染流程。在我的使用场景里,Canvas
组件会在多处复用,
因此我将 EffectComposer
放在场景组件内。
First, we need to create a new EffectComposer
instance to manage our rendering process. In my use case,
the Canvas
component will be reused in multiple places, so I put the EffectComposer
in the scene
component.
// make sure to import the necessary modules from right package import { EffectComposer, RenderPass, ShaderPass } from 'postprocessing' function Scene() { const composerRef = useRef<EffectComposer>() const { gl, scene, camera } = useThree() function initComposer() { // 1. Create a new EffectComposer instance const composer = new EffectComposer(gl) // 2. Usually we want to leverage the existing scene setup inside r3f, // then apply post processing effects on top of that. composer.addPass(new RenderPass(scene, camera)) // 3. here you can add more passes, // e.g. composer.addPass(new ShaderPass(…)) // 4. Save the composer instance to a ref composerRef.current = composer; }; function disposeComposer() { if (composerRef.current) { composerRef.current.dispose() const composer = composerRef.current; for (var i = composer.passes.length - 1; i >= 0; i--) { composer.passes[i].dispose(); } composerRef.current = undefined; } }; useEffect(() => { // 1. initialize and setup the composer on scene && camera change initComposer(); // 2. dispose the composer when the component unmounts return disposeComposer }, [gl, scene, camera]) }
到此为止,我们已经创建了一个基本的 EffectComposer
实例,它包含了一个 RenderPass
用于渲染场景。
但 Canvas
依旧是空白的,因为我们还没有将 EffectComposer
的输出渲染到屏幕上。
So far, we have created a basic EffectComposer
instance, which contains a RenderPass
for rendering the scene.
However, the <Canvas />
is still blank because we have not yet rendered the output of the EffectComposer
to the screen.
自定义渲染循环
Custom Rendering Loop
frameLoop
模式: demand
和 auto
。frameLoop
modes of R3F: demand
and auto
.demand - 使用 react 渲染机制
demand - Use react rendering mechanism
function Scene() { const composerRef = useRef<EffectComposer>(); composerRef.current?.render() {...} }
Canvas
动态响应 react 的 state 更新。 Canvas
can dynamically respond to state updates from react. auto - 使用 requestAnimationLoop
auto - Use requestAnimationLoop
function Scene() { const composerRef = useRef<EffectComposer>(); const renderLoopIdRef = useRef<number>(); useEffect(() = { // 1. cancel exisiting render loop if (renderLoopIdRef.current) { cancelAnimationFrame(renderLoopIdRef.current) } function renderLoop() { composerRef.current?.render(); renderLoopIdRef.current = requestAnimationLoop(renderLoop) } // 2. start a new render loop renderLoopIdRef.current = requestAnimationLoop(renderLoop) return () => { cancelAnimationFrame(renderLoopIdRef.current) } }, []) }
参考
References
- pmndrs/postprocessing https://pmndrs.github.io/postprocessing/public/docs/