Since React Three Fiber (R3F) expects a DOM-like canvas and does not natively support the React Native WebGPU present() requirement, you must create a bridge component (e.g., FiberCanvas).
Key implementation requirements:
- Register Three.js: Call
extend(THREE) to allow using Three.js elements like <mesh /> as JSX. - Async Initialization: You must
await state.gl.init() inside the onCreated hook to ensure the WebGPURenderer is ready before the first draw. - Manual Presentation: R3F does not call
present(). You must wrap the gl.render method to call the native context.present() after every frame. - Pixel Ratio: Set
dpr: 1 in the R3F configuration because physical pixel sizing is handled manually on the native canvas using PixelRatio.get().
export const FiberCanvas = ({ children, style, scene, camera }) => {
const root = useRef(null);
React.useMemo(() => extend(THREE), []);
const canvasRef = useRef(null);
useEffect(() => {
const context = canvasRef.current!.getContext("webgpu")!;
const renderer = makeWebGPURenderer(context);
const canvas = context.canvas as HTMLCanvasElement;
canvas.width = canvas.clientWidth * PixelRatio.get();
canvas.height = canvas.clientHeight * PixelRatio.get();
const size = {
top: 0,
left: 0,
width: canvas.clientWidth,
height: canvas.clientHeight,
};
if (!root.current) {
root.current = createRoot(canvas);
}
root.current.configure({
size,
events,
scene,
camera,
gl: renderer,
frameloop: "always",
dpr: 1, // canvas already sized with PixelRatio
onCreated: async (state) => {
await state.gl.init();
const renderFrame = state.gl.render.bind(state.gl);
state.gl.render = (s, c) => {
renderFrame(s, c);
context.present();
};
},
});
root.current.render(children);
return () => unmountComponentAtNode(canvas);
});
return <Canvas ref={canvasRef} style={style} />;
};