Hi, I’m really motivated to learn about light probes but I find it hard to apply in WebGPU, I am running in different way to code Light Probes but it seems like it isn’t really working whatever techniques I am doing.
This is the example of WebGL. lightprobewebgl
currently this what I code have in light probe.
<Canvas
camera={{ position: [0, 5, 2], fov: 75 }}
shadows
dpr={[1, 2]}
flat
// frameloop="demand"
gl={async (props) => {
// const r = new THREE.WebGPURenderer({
const r = new THREE.WebGLRenderer({
...props,
antialias: true,
powerPreference: 'high-performance',
});
r.shadowMap.enabled = true;
// r.inspector = new InspectorNode()
r.toneMapping = THREE.ACESFilmicToneMapping;
// r.toneMapping = THREE.NoToneMapping;
r.toneMappingExposure = 1;
await r.init();
return r;
}}
style={{ width: '100%', height: '100vh' }}
>
...
</Canvas>
const GlobalIlluminationGrid = ({ resolution = 6, showHelper = false, visible = true }) => {
const { scene, gl } = useThree()
const gridRef = useRef(null)
const helperRef = useRef(null)
useEffect(() => {
// ── 0. Force renderer to match vanilla settings ───────────────────────
// The vanilla sets these before anything is drawn. If your <Canvas>
// didn't set them, do it here. Setting them again when already correct
// is a no-op so this is always safe.
gl.shadowMap.enabled = true
gl.shadowMap.type = THREE.PCFSoftShadowMap
gl.toneMapping = THREE.ACESFilmicToneMapping
gl.toneMappingExposure = 1.0
// ── 1. Tear down previous probes ─────────────────────────────────────
if (gridRef.current) {
scene.remove(gridRef.current)
gridRef.current.dispose()
gridRef.current = null
}
if (helperRef.current) {
// LightProbeGridHelper has no .dispose() — remove only
scene.remove(helperRef.current)
helperRef.current = null
}
// ── 2. Force world matrix update ─────────────────────────────────────
// Vanilla adds all scene objects synchronously then bakes.
// In R3F, JSX children are added by the time useEffect fires, but we
// must call updateMatrixWorld so the probe cube-cameras see correct transforms.
scene.updateMatrixWorld(true)
// ── 3. Create probe grid — dimensions match vanilla exactly ──────────
// new LightProbeGrid(5.6, 4.7, 5.6, res, res, res)
// probes.position.set(0, 2.45, 0)
const probes = new LightProbeGrid(5.6, 4.7, 5.6, resolution, resolution, resolution)
probes.position.set(0, 2.45, 0)
probes.updateMatrixWorld(true)
// ── 4. Bake — gl IS the renderer in R3F ─────────────────────────────
probes.bake(gl, scene, { cubemapSize: 32, near: 0.05, far: 20 })
probes.visible = visible
scene.add(probes)
gridRef.current = probes
// ── 5. Force material shader recompile ───────────────────────────────
// Materials that compiled before the probe grid was in the scene don't
// have SH light probe support in their shaders. needsUpdate forces a
// full shader recompile on the next render frame.
scene.traverse((child) => {
if (child.isMesh && child.material) {
child.material.needsUpdate = true
}
})
// ── 6. Optional debug helper ─────────────────────────────────────────
if (showHelper) {
const helper = new LightProbeGridHelper(probes)
scene.add(helper)
helperRef.current = helper
}
return () => {
if (gridRef.current) {
scene.remove(gridRef.current)
gridRef.current.dispose()
gridRef.current = null
}
if (helperRef.current) {
scene.remove(helperRef.current)
helperRef.current = null
}
}
}, [scene, gl, resolution, showHelper, visible])
return null
}
vs
my R3F + WebGPU.
Note: I also edited my Canvas with WebGPURenderer since I don’t want WebGLRenderer for this.
import * as THREE from 'three/webgpu'
import { useThree } from '@react-three/fiber'
import { useEffect, useMemo, useRef } from 'react'
// ─── WGPUMesh ─────────────────────────────────────────────────────────────────
const WGPUMesh = ({ geometry, color, ...meshProps }) => {
const meshRef = useRef()
useEffect(() => {
if (!meshRef.current) return
const mat = new THREE.MeshStandardNodeMaterial()
mat.color.set(color)
meshRef.current.material = mat
return () => mat.dispose()
}, [color])
return (
<mesh ref={meshRef} {...meshProps}>
{geometry}
</mesh>
)
}
// ─── Single Probe ─────────────────────────────────────────────────────────────
const RealisticProbe = ({ position, showHelper }) => {
const { scene } = useThree()
const probeRef = useRef()
const lightProbe = useMemo(() => {
const probe = new THREE.LightProbe()
probe.intensity = 1.0
probe.sh.coefficients[0].set(0.38, 0.36, 0.32)
probe.sh.coefficients[1].set(0.00, 0.25, 0.00)
probe.sh.coefficients[2].set(0.00, 0.10, 0.00)
probe.sh.coefficients[3].set(0.00, 0.00, 0.00)
probe.sh.coefficients[4].set(-0.08, 0.00, 0.00)
probe.sh.coefficients[5].set(0.00, 0.00, 0.00)
probe.sh.coefficients[6].set(0.00, 0.06, 0.00)
probe.sh.coefficients[7].set(0.00, 0.00, 0.00)
probe.sh.coefficients[8].set(0.00, 0.00, 0.00)
return probe
}, [])
useEffect(() => {
if (probeRef.current) {
probeRef.current.position.set(...position)
}
}, [position])
useEffect(() => {
if (!showHelper) return
let helper
import('three/addons/helpers/LightProbeHelper.js')
.then(({ LightProbeHelper }) => {
helper = new LightProbeHelper(lightProbe, 0.2)
helper.position.set(...position)
scene.add(helper)
})
.catch(() => { })
return () => {
if (helper) {
scene.remove(helper)
helper.dispose()
}
}
}, [scene, lightProbe, showHelper, position])
return <primitive ref={probeRef} object={lightProbe} position={position} />
}
And this is the error I received when I switch to WebGPU.

