How can I use postprocess in Sky Effects?

Hi all!
I would like to apply post-processing to camera textures in Sky Effects.
I have created the following two projects as reference, but when I apply glitchPass, etc., the screen goes blank.
Why is this?

  • It draws properly when I just add RenderPass to EffectComposer.
  • I’m using three@0.155.0

https://www.8thwall.com/evan/sky-custom-threejs
https://www.8thwall.com/evan/clean-threejs-postprocessing

const options_ = {'layerNames': ['sky']}

const customThreejsPipelineModule = () => {
  let scene3
  let engaged = false

  let canvasWidth_
  let canvasHeight_

  // Layer name -> WebGLRenderTarget.
  const layerRenderTargets_ = {}
  let renderTargets_ = null

  let composer
  let cameraTexture
  let effectRenderTarget

  const updateCamera = (camera, position, rotation, intrinsics) => {
    for (let i = 0; i < 16; i++) {
      camera.projectionMatrix.elements[i] = intrinsics[i]
    }

    // Fix for broken raycasting in r103 and higher. Related to:
    //   https://github.com/mrdoob/three.js/pull/15996
    // Note: camera.projectionMatrixInverse wasn't introduced until r96 so check before setting
    // the inverse
    if (camera.projectionMatrixInverse) {
      if (camera.projectionMatrixInverse.invert) {
        // THREE 123 preferred version
        camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert()
      } else {
        // Backwards compatible version
        camera.projectionMatrixInverse.getInverse(camera.projectionMatrix)
      }
    }

    if (rotation) {
      camera.setRotationFromQuaternion(rotation)
    }
    if (position) {
      camera.position.set(position.x, position.y, position.z)
    }
  }

  // A method which GlTextureRenderer will call and use in rendering. Returns a list where each
  // element contains two WebGLTexture's:
  // 1) foregroundTexture: The render target texture which we've rendered the LayerScene to.
  // 2) foregroundMaskTexture: Semantics results. Used for alpha blending of foregroundTexture.
  const customForegroundTexture = ({processCpuResult}) => {
    if (options_.layerNames.length === 0 || !processCpuResult.layerscontroller ||
      !processCpuResult.layerscontroller.layers) {
      return []
    }

    // If we are using XrController then update the scene using its extrinsic.
    const {rotation, position, intrinsics} =
        processCpuResult.reality || processCpuResult.layerscontroller

    const {renderer, layerScenes} = scene3

    // Here we 1) update the layer scene camera 2) render the layer scene to a texture 3) return
    // the layer scene texture as well as the alpha mask for that layer. Note that we need to do
    // #2 here rather than in onRender() or onUpdate() because we need the ouput availabe in this
    // function, which is called by GlTextureRenderer's onUpdate(). And GlTextureRenderer's
    // onUpdate() can be called before ThreejsRenderer's onUpdate().
    const foregroundTexturesAndMasks = options_.layerNames.map((layerName) => {
      // 1) Update the camera in the layer scene.
      updateCamera(layerScenes[layerName].camera, position, rotation, intrinsics)

      // 2) Render the layer scene to a texture
      if (layerRenderTargets_[layerName].width !== canvasWidth_ ||
          layerRenderTargets_[layerName].height !== canvasHeight_) {
        // We change the size here instead of in updateSize() because setSize() will call
        // this.dispose() which frees the texture. We only want to do this right before drawing
        // a new texture. Example:
        // - https://r105.threejsfundamentals.org/threejs/lessons/threejs-rendertargets.html
        layerRenderTargets_[layerName].setSize(canvasWidth_, canvasHeight_)
      }

      // Update the encoding in case it has changed.
      if (layerRenderTargets_[layerName].texture.colorSpace !== renderer.outputColorSpace) {
        layerRenderTargets_[layerName].texture.colorSpace = renderer.outputColorSpace
      }
      renderer.setRenderTarget(layerRenderTargets_[layerName])
      renderer.clear()
      renderer.render(layerScenes[layerName].scene, layerScenes[layerName].camera)
      renderer.setRenderTarget(null)

      // 3) Return the layer scene texture as well as the alpha mask for that layer.
      // Get a handle to the WebGLTexture from the WebGLRenderTarget's Texture. See:
      // - https://stackoverflow.com/a/44176225/4979029
      // - https://stackoverflow.com/a/51097136/4979029
      const layerRenderTargetTexture =
          renderer.properties.get(layerRenderTargets_[layerName].texture)
      return {
        foregroundTexture: layerRenderTargetTexture.__webglTexture,
        foregroundMaskTexture: processCpuResult.layerscontroller.layers[layerName].texture,
        foregroundTextureFlipY: true,
        foregroundMaskTextureFlipY: false,
      }
    })
    return foregroundTexturesAndMasks
  }

  const customTexture = ({processCpuResult}) => {
    // If we are using XrController then update the scene using its extrinsic.
    const {rotation, position, intrinsics} =
        processCpuResult.reality || processCpuResult.layerscontroller

    const {renderer, scene, camera} = scene3

    // 1) Update the camera in the layer scene.
    updateCamera(camera, position, rotation, intrinsics)

    renderer.setRenderTarget(renderTargets_)
    renderer.clear()
    renderer.render(scene, camera)
    renderer.setRenderTarget(null)

    return renderer.properties.get(renderTargets_.texture)
  }

  const engage = ({canvas, canvasWidth, canvasHeight, GLctx}) => {
    if (engaged) {
      return
    }

    // Set up the three.js scene
    const scene = new window.THREE.Scene()
    const camera = new window.THREE.PerspectiveCamera(
      60.0, /* initial field of view; will get set based on device info later. */
      canvasWidth / canvasHeight,
      0.01,
      1000.0
    )
    scene.add(camera)

    const renderer = new window.THREE.WebGLRenderer({
      canvas,
      context: GLctx,
      alpha: false,
      antialias: true,
    })
    renderer.autoClear = false
    renderer.setSize(canvasWidth, canvasHeight)

    composer = new THREE.EffectComposer(renderer)
    composer.setSize(canvasWidth, canvasHeight)

    const renderPass = new THREE.RenderPass(scene, camera)
    renderPass.clearAlpha = true
    composer.addPass(renderPass)

    const glitchPass = new THREE.GlitchPass()
    glitchPass.clear = false
    composer.addPass(glitchPass)

    const outputPass = new THREE.OutputPass()
    outputPass.clear = false
    composer.addPass(outputPass)

    scene3 = {scene, camera, renderer}
    window.xrScene = scene3
    engaged = true

    if (options_.layerNames) {
      const layerScenes = {}
      options_.layerNames.forEach((layerName) => {
        // Create a scene for each layer.
        const layerScene = new window.THREE.Scene()
        layerScene.name = layerName

        const layerCamera = camera.clone()
        layerScene.add(layerCamera)

        layerScenes[layerName] = {'scene': layerScene, 'camera': layerCamera}

        // Create a render target which we will render the layer scene to.
        layerRenderTargets_[layerName] = new THREE.WebGLRenderTarget(canvasWidth, canvasHeight, {
          depthBuffer: true,
          colorSpace: renderer.outputColorSpace,
        })
      })

      renderTargets_ = new THREE.WebGLRenderTarget(canvasWidth, canvasHeight, {
        depthBuffer: true,
        colorSpace: renderer.outputColorSpace,
      })

      scene3 = {...scene3, layerScenes}
      window.xrScene = scene3

      window.XR8.GlTextureRenderer.setForegroundTextureProvider(customForegroundTexture)
    }
  }
  return {
    name: 'customthreejs',
    onStart: args => engage(args),
    onAttach: args => engage(args),
    onDetach: () => {
      engaged = false
    },
    onUpdate: ({frameStartResult, processCpuResult}) => {
      const realitySource = processCpuResult.reality || processCpuResult.facecontroller ||
      processCpuResult.handcontroller || processCpuResult.layerscontroller

      if (!realitySource) {
        return
      }

      // cameraTexture = frameStartResult.cameraTexture

      const {rotation, position, intrinsics} = realitySource
      const {camera} = scene3
      for (let i = 0; i < 16; i++) {
        camera.projectionMatrix.elements[i] = intrinsics[i]
      }
      // Fix for broken raycasting in r103 and higher. Related to:
      //   https://github.com/mrdoob/three.js/pull/15996
      // Note: camera.projectionMatrixInverse wasn't introduced until r96 so check before setting
      // the inverse
      if (camera.projectionMatrixInverse) {
        if (camera.projectionMatrixInverse.invert) {
          // THREE 123 preferred version
          camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert()
        } else {
          // Backwards compatible version
          camera.projectionMatrixInverse.getInverse(camera.projectionMatrix)
        }
      }
      if (rotation) {
        camera.setRotationFromQuaternion(rotation)
      }
      if (position) {
        camera.position.set(position.x, position.y, position.z)
      }
    },
    onCanvasSizeChange: ({canvasWidth, canvasHeight}) => {
      if (!engaged) {
        return
      }
      canvasWidth_ = canvasWidth
      canvasHeight_ = canvasHeight
      const {renderer} = scene3
      renderer.setSize(canvasWidth, canvasHeight)
    },
    onRender: () => {
      const {scene, renderer, camera} = scene3
      renderer.clearDepth()
      composer.render(scene, camera)
    },
    // Get a handle to the xr scene, camera and renderer. Returns:
    // {
    //   scene: The Threejs scene.
    //   camera: The Threejs main camera.
    //   renderer: The Threejs renderer.
    // }
    xrScene: () => scene3,
  }
}
export {customThreejsPipelineModule}

Hi Ken!

Can you provide some more detail on what you’re trying to achieve? Are you trying to apply a postprocessing effect to the entire camera feed?

Evan, thanks for your reply!

Yes, I want to post-process the entire camera feed.
What I want to do is to change the appearance of the real world according to what is displayed in the sky scene, so that if the sky scene is at night, the real world is also looks dark.

the example here was for three.js 103+ has there been an update to this example of a custom three.js pipeline the camera overlay seems off from the 8thwall three.js pipeline when using this version.

1 Like