Search code examples
javascripttypescriptthree.jsreact-three-fiber

Three.js React Fiber - Transparent background


I'm using react fiber, and cannot figure out how to get the background of my child scene to be transparent

This is my root component with the main Canvas

export const Splash = () => {
  const { intensity, distance, color, ambientIntensity } = useControls('Main lights', {
    intensity: { value: 9, min: 0, max: 200, step: 0.5 },
    distance: { value: 100, min: -100, max: 100, step: 0.5 },
    color: "#7c0505",
    ambientIntensity: { value: 0.5, min: -3, max: 3, step: 0.1 },
  });
    
  return (<>
      <Canvas
        dpr={window.devicePixelRatio}
        onCreated={({ gl }) => {
          gl.setClearColor(0xFF0000, 0);
          gl.autoClear = false;
          gl.clearDepth()
        }}
        gl={{ antialias: true, alpha: true }}
        camera={{ fov: 65, position: [0, 0, 30] }}>
        <Main>
          <ambientLight intensity={ambientIntensity} color={color} />
          <RadialGradientPlane />
        </Main>
        <ApplyEffects>
         <RadialGradientPlane />
        </ApplyEffects>
        
      </Canvas>
    </>
  )
}

The Main Component simply just renders a child scene

function Main({ children }) {
  const scene = useRef()
  const { gl, camera } = useThree()
  useFrame(() => {
    gl.autoClear = false
    gl.clearDepth()
    gl.render(scene.current, camera)
  }, 1)
  return <scene ref={scene}>{children}</scene>
}

This IS transparent, if i remove the ApplyEffects component i see the RadialGradientPlane and my normal document background colour.

It's something to do with the ApplyEffects component, which is:

import { Effects } from '@react-three/drei';
import { UnrealBloomPass, RenderPass, WaterPass } from 'three-stdlib';
export default function ApplyEffects({ children }: { children: React.ReactNode }}) {
  const waterPass = useRef<WaterPass>(null!);
  const composer = useRef<EffectComposerGeneric<WebGLRenderTarget>>(null!)
  const { size, camera } = useThree();
  const aspect = useMemo(() => new Vector2(2046, 2046), [])
  const [scene, setScene] = useState<Scene>();
  const {strength, threshold, radius} = useControls('Bloom', {
    threshold: { value: 0.1, min: 0, max: 1, step: 0.01 },
    strength: { value: 1.5, min: 0, max: 10, step: 0.01 },
    radius: { value: 0.05, min: 0, max: 1, step: 0.01 },
  })
  useEffect(() => {
    if (composer.current) {
      composer.current.setSize(size.width, size.height)
    }
  }, [size])
  useFrame(() => {
    composer.current?.render();
    if (waterPass.current) {
      waterPass.current.time += 0.05;
    }
    composer.current.renderer.autoClear = false
    composer.current.renderer.clearDepth()
  }, 2);
  return <>
    <scene ref={setScene}>{children}</scene>
    <Effects ref={composer} disableGamma={true} disableRenderPass={true}>
      <renderPass scene={scene} camera={camera} />
      <waterPass ref={waterPass} attach="passes" factor={0.6} />
      <unrealBloomPass
        threshold={threshold}
        strength={strength}
        radius={radius}
        resolution={aspect} />
    </Effects>
  </>;
}

I feel like I've tried everything here, i cannot figure it out!

Any help would be greatly appreciated!

Here's an example of the same issue: small example

If you remove the "Effects" the background is transparent and shows the white body colour.

Shannon


Solution

  • This is a well-known issue reported and heavily discussed here.

    One of the solutions is implemented here and seem to solve the problem based on this demo.

    Please let me know if this helps.