-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhancement: Tunnel Vision #450
Merged
Merged
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
99adaa7
added tunnel effect into hub
tony056 0bd5a32
working tunnel effect for 2d, but needs to modify to work in VR
tony056 ea6f19e
image version of tunnel vision
tony056 aa824ac
moved tunnel-effect to 'systems' folder.
tony056 682f89f
removed image approach for tunnel vision.
tony056 3b84442
added fadingEffect for entering/exiting tunnel vision.
tony056 661d0c0
changed the path of 'tunnel-effect'.
tony056 9988451
included post processing essential files.
tony056 c0d6d55
applied vignetteShader to two eyes.
tony056 22bcc2f
detect the movement by the change of the camera's position.
tony056 c834cdf
moved two tunnels closer to each other and combined left and right vi…
tony056 6e7f751
used the character's velocity to trigger tunnel effect.
tony056 690d0c5
renamed the func name.
tony056 6e56358
Adjusted the offset dynamically to make sure two tunnels won't overla…
tony056 912139a
cleaned up the event listener.
tony056 4bb5b0f
moved postprocessing & shader files to utils.
tony056 bdb4b36
deleted unused images.
tony056 009f802
added comments at some unclear parts.
tony056 76813ee
Merge branch 'master' into enhancement/locomotion
tony056 41e267e
style modification.
tony056 25f0d9c
Merge branch 'enhancement/locomotion' of github.com:mozilla/hubs into…
tony056 8e0f561
added missing parameters.
tony056 31914e6
checkout back to previous one commit to undo teleport parameter changes.
tony056 a5eb72a
enabled the effect while it's in vr mode.
tony056 d3414d9
Merge branch 'master' of github.com:mozilla/hubs into enhancement/loc…
tony056 7f19a1d
avoid creating a new function everytime we switched to the postproces…
tony056 8cdab8b
removed unused var.
tony056 da5d4bc
disabled tunnel while exit vr.
tony056 d9c195d
fading out when the v is 0.
tony056 c3f1976
renamed unit to ratio.
tony056 08d4470
added disableTunnel parameter to disable the effect with qs_truthy.
tony056 7a6090b
Add some information about where the effect code came from.
johnshaughnessy a8203ac
Minor edits. Only call _initComposer once.
johnshaughnessy a304b8a
init deltaR & deltaS. added disableTuneel param in README.
tony056 7b1fe60
moved deltaR and deltaS init to the init func.
tony056 487ae33
deltaR should be TARGET_RADIUS - value of the radius while the user s…
tony056 c2440b8
Merge branch 'master' into enhancement/locomotion
johnshaughnessy b52512d
Fix issue where tunnel would "pop" at the end
johnshaughnessy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import "../utils/postprocessing/EffectComposer"; | ||
import "../utils/postprocessing/RenderPass"; | ||
import "../utils/postprocessing/ShaderPass"; | ||
import "../utils/postprocessing/MaskPass"; | ||
import "../utils/shaders/CopyShader"; | ||
import "../utils/shaders/VignetteShader"; | ||
import qsTruthy from "../utils/qs_truthy"; | ||
|
||
const disabledByQueryString = qsTruthy("disableTunnel"); | ||
const CLAMP_SPEED = 0.01; | ||
const CLAMP_RADIUS = 0.001; | ||
const NO_TUNNEL_RADIUS = 10.0; | ||
const NO_TUNNEL_SOFTNESS = 0.0; | ||
|
||
function lerp(start, end, t) { | ||
return (1 - t) * start + t * end; | ||
} | ||
|
||
function f(t) { | ||
const x = t - 1; | ||
return 1 + x * x * x * x * x; | ||
} | ||
|
||
AFRAME.registerSystem("tunneleffect", { | ||
schema: { | ||
targetComponent: { type: "string", default: "character-controller" }, | ||
radius: { type: "number", default: 1.0, min: 0.25 }, | ||
minRadius: { type: "number", default: 0.25, min: 0.1 }, | ||
maxSpeed: { type: "number", default: 0.5, min: 0.1 }, | ||
softest: { type: "number", default: 0.1, min: 0.0 }, | ||
opacity: { type: "number", default: 1, min: 0.0 } | ||
}, | ||
|
||
init: function() { | ||
this.scene = this.el; | ||
this.isMoving = false; | ||
this.isVR = false; | ||
this.dt = 0; | ||
this.isPostProcessingReady = false; | ||
this.characterEl = document.querySelector(`a-entity[${this.data.targetComponent}]`); | ||
if (this.characterEl) { | ||
this._initPostProcessing = this._initPostProcessing.bind(this); | ||
this.characterEl.addEventListener("componentinitialized", this._initPostProcessing); | ||
} else { | ||
console.warn("Could not find target component."); | ||
} | ||
this._enterVR = this._enterVR.bind(this); | ||
this._exitVR = this._exitVR.bind(this); | ||
this.scene.addEventListener("enter-vr", this._enterVR); | ||
this.scene.addEventListener("exit-vr", this._exitVR); | ||
}, | ||
|
||
pause: function() { | ||
if (!this.characterEl) { | ||
return; | ||
} | ||
this.characterEl.removeEventListener("componentinitialized", this._initPostProcessing); | ||
this.scene.removeEventListener("enter-vr", this._enterVR); | ||
this.scene.removeEventListener("exit-vr", this._exitVR); | ||
}, | ||
|
||
play: function() { | ||
this.scene.addEventListener("enter-vr", this._enterVR); | ||
this.scene.addEventListener("exit-vr", this._exitVR); | ||
}, | ||
|
||
tick: function(t, dt) { | ||
this.dt = dt; | ||
|
||
if (disabledByQueryString || !this.isPostProcessingReady || !this.isVR) { | ||
return; | ||
} | ||
|
||
const { maxSpeed, minRadius, softest } = this.data; | ||
const characterSpeed = this.characterComponent.velocity.length(); | ||
const shaderRadius = this.vignettePass.uniforms["radius"].value || NO_TUNNEL_RADIUS; | ||
if (!this.enabled && characterSpeed > CLAMP_SPEED) { | ||
this.enabled = true; | ||
this._bindRenderFunc(); | ||
} else if ( | ||
this.enabled && | ||
characterSpeed < CLAMP_SPEED && | ||
Math.abs(NO_TUNNEL_RADIUS - shaderRadius) < CLAMP_RADIUS | ||
) { | ||
this.enabled = false; | ||
this._exitTunnel(); | ||
} | ||
if (this.enabled) { | ||
const clampedSpeed = characterSpeed > maxSpeed ? maxSpeed : characterSpeed; | ||
const speedRatio = clampedSpeed / maxSpeed; | ||
this.targetRadius = lerp(NO_TUNNEL_RADIUS, minRadius, f(speedRatio)); | ||
this.targetSoftness = lerp(NO_TUNNEL_SOFTNESS, softest, f(speedRatio)); | ||
this._updateVignettePass(this.targetRadius, this.targetSoftness, this.data.opacity); | ||
} | ||
}, | ||
|
||
_exitTunnel: function() { | ||
this.scene.renderer.render = this.originalRenderFunc; | ||
this.isMoving = false; | ||
}, | ||
|
||
_initPostProcessing: function(event) { | ||
if (event.detail.name === this.data.targetComponent) { | ||
this.characterEl.removeEventListener("componentinitialized", this._initPostProcessing); | ||
this.characterComponent = this.characterEl.components[this.data.targetComponent]; | ||
this._initComposer(); | ||
} | ||
}, | ||
|
||
_enterVR: function() { | ||
this.isVR = true; //TODO: This is called in 2D mode when you press "f", which is bad | ||
}, | ||
|
||
_exitVR: function() { | ||
this._exitTunnel(); | ||
this.isVR = false; | ||
}, | ||
|
||
_initComposer: function() { | ||
this.renderer = this.scene.renderer; | ||
this.camera = this.scene.camera; | ||
this.originalRenderFunc = this.scene.renderer.render; | ||
this.isDigest = false; | ||
const render = this.scene.renderer.render; | ||
const system = this; | ||
this.postProcessingRenderFunc = function() { | ||
if (system.isDigest) { | ||
render.apply(this, arguments); | ||
} else { | ||
system.isDigest = true; | ||
system.composer.render(system.dt); | ||
system.isDigest = false; | ||
} | ||
}; | ||
this.composer = new THREE.EffectComposer(this.renderer); | ||
this.composer.resize(); | ||
this.scenePass = new THREE.RenderPass(this.scene.object3D, this.camera); | ||
this.vignettePass = new THREE.ShaderPass(THREE.VignetteShader); | ||
this._updateVignettePass(this.data.radius, this.data.softness, this.data.opacity); | ||
this.composer.addPass(this.scenePass); | ||
this.composer.addPass(this.vignettePass); | ||
this.isPostProcessingReady = true; | ||
}, | ||
|
||
_updateVignettePass: function(radius, softness, opacity) { | ||
const { width, height } = this.renderer.getSize(); | ||
const pixelRatio = this.renderer.getPixelRatio(); | ||
this.vignettePass.uniforms["radius"].value = radius; | ||
this.vignettePass.uniforms["softness"].value = softness; | ||
this.vignettePass.uniforms["opacity"].value = opacity; | ||
this.vignettePass["resolution"] = new THREE.Uniform(new THREE.Vector2(width * pixelRatio, height * pixelRatio)); | ||
if (!this.vignettePass.renderToScreen) { | ||
this.vignettePass.renderToScreen = true; | ||
} | ||
}, | ||
|
||
/** | ||
* use the render func of the effect composer when we need the postprocessing | ||
*/ | ||
_bindRenderFunc: function() { | ||
this.scene.renderer.render = this.postProcessingRenderFunc; | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
THREE.EffectComposer = function(renderer, renderTarget) { | ||
this.renderer = renderer; | ||
this.delta = 0; | ||
window.addEventListener("vrdisplaypresentchange", this.resize.bind(this)); | ||
|
||
if (renderTarget === undefined) { | ||
const parameters = { | ||
minFilter: THREE.LinearFilter, | ||
magFilter: THREE.LinearFilter, | ||
format: THREE.RGBAFormat, | ||
stencilBuffer: false | ||
}; | ||
|
||
const size = renderer.getDrawingBufferSize(); | ||
renderTarget = new THREE.WebGLRenderTarget(size.width, size.height, parameters); | ||
renderTarget.texture.name = "EffectComposer.rt1"; | ||
} | ||
|
||
this.renderTarget1 = renderTarget; | ||
this.renderTarget2 = renderTarget.clone(); | ||
this.renderTarget2.texture.name = "EffectComposer.rt2"; | ||
|
||
this.writeBuffer = this.renderTarget1; | ||
this.readBuffer = this.renderTarget2; | ||
|
||
this.passes = []; | ||
this.maskActive = false; | ||
|
||
// dependencies | ||
|
||
if (THREE.CopyShader === undefined) { | ||
console.error("THREE.EffectComposer relies on THREE.CopyShader"); | ||
} | ||
|
||
if (THREE.ShaderPass === undefined) { | ||
console.error("THREE.EffectComposer relies on THREE.ShaderPass"); | ||
} | ||
|
||
this.copyPass = new THREE.ShaderPass(THREE.CopyShader); | ||
}; | ||
|
||
Object.assign(THREE.EffectComposer.prototype, { | ||
swapBuffers: function(pass) { | ||
if (pass.needsSwap) { | ||
if (this.maskActive) { | ||
const context = this.renderer.context; | ||
context.stencilFunc(context.NOTEQUAL, 1, 0xffffffff); | ||
this.copyPass.render(this.renderer, this.writeBuffer, this.readBuffer, this.delta); | ||
context.stencilFunc(context.EQUAL, 1, 0xffffffff); | ||
} | ||
|
||
const tmp = this.readBuffer; | ||
this.readBuffer = this.writeBuffer; | ||
this.writeBuffer = tmp; | ||
} | ||
|
||
if (THREE.MaskPass !== undefined) { | ||
if (pass instanceof THREE.MaskPass) { | ||
this.maskActive = true; | ||
} else if (pass instanceof THREE.ClearMaskPass) { | ||
this.maskActive = false; | ||
} | ||
} | ||
}, | ||
|
||
addPass: function(pass) { | ||
this.passes.push(pass); | ||
const size = this.renderer.getDrawingBufferSize(); | ||
pass.setSize(size.width, size.height); | ||
}, | ||
|
||
insertPass: function(pass, index) { | ||
this.passes.splice(index, 0, pass); | ||
}, | ||
|
||
render: function(delta, starti) { | ||
const maskActive = this.maskActive; | ||
let pass; | ||
let i; | ||
const il = this.passes.length; | ||
const scope = this; | ||
let currentOnAfterRender; | ||
this.delta = delta; | ||
|
||
for (i = starti || 0; i < il; i++) { | ||
pass = this.passes[i]; | ||
if (pass.enabled === false) continue; | ||
|
||
// If VR mode is enabled and rendering the whole scene is required. | ||
// The pass renders the scene and and postprocessing is resumed before | ||
// submitting the frame to the headset by using the onAfterRender callback. | ||
if (this.renderer.vr.enabled && pass.scene) { | ||
currentOnAfterRender = pass.scene.onAfterRender; | ||
pass.scene.onAfterRender = function() { | ||
// Disable stereo rendering when doing postprocessing | ||
// on a render target. | ||
scope.renderer.vr.enabled = false; | ||
scope.render(delta, i + 1, maskActive); | ||
|
||
// Renable vr mode. | ||
scope.renderer.vr.enabled = true; | ||
}; | ||
|
||
pass.render(this.renderer, this.writeBuffer, this.readBuffer); | ||
|
||
// Restore onAfterRender | ||
pass.scene.onAfterRender = currentOnAfterRender; | ||
this.swapBuffers(pass); | ||
return; | ||
} | ||
|
||
pass.render(this.renderer, this.writeBuffer, this.readBuffer); | ||
this.swapBuffers(pass); | ||
} | ||
}, | ||
|
||
reset: function(renderTarget) { | ||
if (renderTarget === undefined) { | ||
const size = this.renderer.getDrawingBufferSize(); | ||
renderTarget = this.renderTarget1.clone(); | ||
renderTarget.setSize(size.width, size.height); | ||
} | ||
|
||
this.renderTarget1.dispose(); | ||
this.renderTarget2.dispose(); | ||
this.renderTarget1 = renderTarget; | ||
this.renderTarget2 = renderTarget.clone(); | ||
|
||
this.writeBuffer = this.renderTarget1; | ||
this.readBuffer = this.renderTarget2; | ||
}, | ||
|
||
setSize: function(width, height) { | ||
this.renderTarget1.setSize(width, height); | ||
this.renderTarget2.setSize(width, height); | ||
for (let i = 0; i < this.passes.length; i++) { | ||
this.passes[i].setSize(width, height); | ||
} | ||
}, | ||
|
||
resize: function() { | ||
const rendererSize = this.renderer.getDrawingBufferSize(); | ||
this.setSize(rendererSize.width, rendererSize.height); | ||
} | ||
}); | ||
|
||
THREE.Pass = function() { | ||
// if set to true, the pass is processed by the composer | ||
this.enabled = true; | ||
|
||
// if set to true, the pass indicates to swap read and write buffer after rendering | ||
this.needsSwap = true; | ||
|
||
// if set to true, the pass clears its buffer before rendering | ||
this.clear = false; | ||
|
||
// if set to true, the result of the pass is rendered to screen | ||
this.renderToScreen = false; | ||
}; | ||
|
||
Object.assign(THREE.Pass.prototype, { | ||
setSize: function() {}, | ||
|
||
render: function() { | ||
console.error("THREE.Pass: .render() must be implemented in derived pass."); | ||
} | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to disable this effect when not in VR.