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}