Skip to content

[v11] Verify Perspective/OrthographicCamera: FBO render path on WebGPU #2823

Description

@DennisSmolek

What they do

OrthographicCamera and PerspectiveCamera share one code path — when given children they render the scene into an FBO from the camera's point of view:

src/core/Cameras/PerspectiveCamera/PerspectiveCamera.tsx:77-85 (and the orthographic twin at :79-87):

useFrame((state) => {
  ...
  state.gl.setRenderTarget(fbo)
  ...
  state.gl.render(state.scene, cameraRef.current)
  ...
  state.gl.setRenderTarget(null)
})

Filed as one issue because the two files are near-identical here; fix one, apply to both.

What to check

  • state.gl is deprecated in r3f v10 and warns on every access — these should be state.renderer. That is not cosmetic here: it is two accesses per camera per frame.
  • setRenderTarget(null) unconditionally restores to the default framebuffer rather than to whatever was previously bound. Under View, RenderTexture or a post-processing pipeline that is already wrong on WebGL; on WebGPU it may fail differently. Compare with RenderTexture, which does save and restore properly.
  • Whether the FBO created here is a WebGLRenderTarget or a RenderTarget under the WebGPU build — it comes from Fbo, which claims to handle both.

Shared context (identical across all seven)

These components are classified agnostic — they live in core/, so they are assumed to work on both renderers. That assumption held for 104 of the 106 agnostic components when swept: 73 never touch the renderer at all, and 31 touch only members both renderers have. These seven are different: they drive their own render calls, and that is where semantics diverge even when the API surface matches. See #2818.

setRenderTarget(renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) has an identical signature on both (Renderer.js:2578 vs WebGLRenderer.js:2890), so that part is fine.

render() is where they differ — three/src/renderers/common/Renderer.js:1362:

render( scene, camera ) {
  if ( this._initialized === false ) {
    throw new Error( 'THREE.Renderer: .render() called before the backend is initialized. Use "await renderer.init();" before rendering.' );
  }
  this._renderScene( scene, camera );
}

WebGL has no such gate. A render() that happens before the backend is ready throws on WebGPU and silently works on WebGL.

This is the class of bug that produced #2809: Preload calls gl.compile(), which on WebGPU is a getter aliasing compileAsync — same name, returns an unawaited promise, so preloading never happens. Grep cannot find these; they need reading.

Acceptance

Read the component against the WebGPU Renderer implementation and either confirm it is correct or fix it and say what was wrong. A story exercising the render path on WebGPU is the evidence — note Setup sets frameloop: 'never' under test, so useFrame does not run there and a green story proves nothing about this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    v11For v11 related taskswebgpuWebGPU Related issue

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions