A Pen by Dennis Christensen on CodePen.
Created
January 9, 2019 17:00
-
-
Save DNRN/453bfebe16ed0b58694c210845c359ea to your computer and use it in GitHub Desktop.
Holographic Projection
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
THREE.VolumetericLightShader = { | |
uniforms: { | |
tDiffuse: {value:null}, | |
lightPosition: {value: new THREE.Vector2(0.5, 0.5)}, | |
exposure: {value: 1}, | |
decay: {value: 1}, | |
density: {value: 6}, | |
weight: {value: 0.57}, | |
samples: {value: 30} | |
}, | |
vertexShader: [ | |
"varying vec2 vUv;", | |
"void main() {", | |
"vUv = uv;", | |
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);", | |
"}" | |
].join("\n"), | |
fragmentShader: [ | |
"varying vec2 vUv;", | |
"uniform sampler2D tDiffuse;", | |
"uniform vec2 lightPosition;", | |
"uniform float exposure;", | |
"uniform float decay;", | |
"uniform float density;", | |
"uniform float weight;", | |
"uniform int samples;", | |
"const int MAX_SAMPLES = 100;", | |
"void main()", | |
"{", | |
"vec2 texCoord = vUv;", | |
"vec2 deltaTextCoord = texCoord - lightPosition;", | |
"deltaTextCoord *= 1.0 / float(samples) * density;", | |
"vec4 color = texture2D(tDiffuse, texCoord);", | |
"float illuminationDecay = 1.0;", | |
"for(int i=0; i < MAX_SAMPLES; i++)", | |
"{", | |
"if(i == samples) {", | |
"break;", | |
"}", | |
"texCoord += deltaTextCoord;", | |
"vec4 sample = texture2D(tDiffuse, texCoord);", | |
"sample *= illuminationDecay * weight;", | |
"color += sample;", | |
"illuminationDecay *= decay;", | |
"}", | |
"gl_FragColor = color * exposure;", | |
"}" | |
].join("\n") | |
}; | |
THREE.AdditiveBlendingShader = { | |
uniforms: { | |
tDiffuse: { value:null }, | |
tAdd: { value:null } | |
}, | |
vertexShader: [ | |
"varying vec2 vUv;", | |
"void main() {", | |
"vUv = uv;", | |
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);", | |
"}" | |
].join("\n"), | |
fragmentShader: [ | |
"uniform sampler2D tDiffuse;", | |
"uniform sampler2D tAdd;", | |
"varying vec2 vUv;", | |
"void main() {", | |
"vec4 color = texture2D(tDiffuse, vUv);", | |
"vec4 add = texture2D(tAdd, vUv);", | |
"gl_FragColor = color + add;", | |
"}" | |
].join("\n") | |
}; | |
THREE.PassThroughShader = { | |
uniforms: { | |
tDiffuse: { value: null } | |
}, | |
vertexShader: [ | |
"varying vec2 vUv;", | |
"void main() {", | |
"vUv = uv;", | |
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);", | |
"}" | |
].join("\n"), | |
fragmentShader: [ | |
"uniform sampler2D tDiffuse;", | |
"varying vec2 vUv;", | |
"void main() {", | |
"gl_FragColor = texture2D(tDiffuse, vec2(vUv.x, vUv.y));", | |
"}" | |
].join("\n") | |
}; | |
const getImageTexture = (image, density = 1) => { | |
const canvas = document.createElement('canvas'); | |
const ctx = canvas.getContext('2d'); | |
const { width, height } = image; | |
canvas.setAttribute('width', width * density); | |
canvas.setAttribute('height', height * density); | |
canvas.style.width = `${width}px`; | |
canvas.style.height = `${height}px`; | |
ctx.drawImage(image, 0, 0, width * density, height * density); | |
return canvas; | |
}; | |
const width = 1280; | |
const height = 720; | |
const lightColor = 0x0099ff; | |
const DEFAULT_LAYER = 0; | |
const OCCLUSION_LAYER = 1; | |
const renderScale = .25; | |
const gui = new dat.GUI(); | |
const clock = new THREE.Clock(); | |
let composer, | |
filmPass, | |
badTVPass, | |
bloomPass, | |
occlusionComposer, | |
itemMesh, | |
occMesh, | |
occRenderTarget, | |
lightSource, | |
vlShaderUniforms; | |
const scene = new THREE.Scene(); | |
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); | |
const renderer = new THREE.WebGLRenderer({ | |
antialias: false | |
}); | |
renderer.setSize(width, height); | |
document.body.appendChild(renderer.domElement); | |
function setupScene() { | |
lightSource = new THREE.Object3D(); | |
lightSource.position.x = 0; | |
lightSource.position.y = -15; | |
lightSource.position.z = -15; | |
const itemGeo = new THREE.PlaneGeometry(9, 2.1); | |
const itemMaterial = new THREE.MeshBasicMaterial({transparent: true, opacity: 0.7}); | |
const img = new Image(); | |
img.src = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/13307/blaster.png'; | |
img.crossOrigin = 'Anonymous'; | |
img.onload = function() { | |
const itemTexture = new THREE.Texture( | |
getImageTexture(img), | |
null, | |
THREE.ClampToEdgeWrapping, | |
THREE.ClampToEdgeWrapping, | |
null, | |
THREE.LinearFilter | |
); | |
itemTexture.needsUpdate = true; | |
itemMaterial.map = itemTexture; | |
itemMesh = new THREE.Mesh(itemGeo, itemMaterial); | |
scene.add(itemMesh); | |
const occItemMaterial = new THREE.MeshBasicMaterial({color: lightColor}); | |
occItemMaterial.map = itemTexture; | |
occMesh = new THREE.Mesh(itemGeo, occItemMaterial); | |
occMesh.layers.set(OCCLUSION_LAYER); | |
scene.add(occMesh); | |
} | |
camera.position.z = 4.5; | |
} | |
function setupPostprocessing() { | |
occRenderTarget = new THREE.WebGLRenderTarget(width * renderScale, height * renderScale); | |
// Blur passes | |
const hBlur = new THREE.ShaderPass(THREE.HorizontalBlurShader); | |
const vBlur = new THREE.ShaderPass(THREE.VerticalBlurShader); | |
const bluriness = 7; | |
hBlur.uniforms.h.value = bluriness / width; | |
vBlur.uniforms.v.value = bluriness / height; | |
// Bad TV Pass | |
badTVPass = new THREE.ShaderPass(THREE.BadTVShader); | |
badTVPass.uniforms.distortion.value = 1.9; | |
badTVPass.uniforms.distortion2.value = 1.2; | |
badTVPass.uniforms.speed.value = 0.1; | |
badTVPass.uniforms.rollSpeed.value = 0; | |
// Volumetric Light Pass | |
const vlPass = new THREE.ShaderPass(THREE.VolumetericLightShader); | |
vlShaderUniforms = vlPass.uniforms; | |
vlPass.needsSwap = false; | |
// Occlusion Composer | |
occlusionComposer = new THREE.EffectComposer(renderer, occRenderTarget); | |
occlusionComposer.addPass(new THREE.RenderPass(scene, camera)); | |
occlusionComposer.addPass(hBlur); | |
occlusionComposer.addPass(vBlur); | |
occlusionComposer.addPass(hBlur); | |
occlusionComposer.addPass(vBlur); | |
occlusionComposer.addPass(hBlur); | |
occlusionComposer.addPass(badTVPass); | |
occlusionComposer.addPass(vlPass); | |
// Bloom pass | |
bloomPass = new THREE.UnrealBloomPass(width / height, 0.5, .8, .3); | |
// Film pass | |
filmPass = new THREE.ShaderPass(THREE.FilmShader); | |
filmPass.uniforms.sCount.value = 1200; | |
filmPass.uniforms.grayscale.value = false; | |
filmPass.uniforms.sIntensity.value = 1.5; | |
filmPass.uniforms.nIntensity.value = 0.2; | |
// Blend occRenderTarget into main render target | |
const blendPass = new THREE.ShaderPass(THREE.AdditiveBlendingShader); | |
blendPass.uniforms.tAdd.value = occRenderTarget.texture; | |
blendPass.renderToScreen = true; | |
// Main Composer | |
composer = new THREE.EffectComposer(renderer); | |
composer.addPass(new THREE.RenderPass(scene, camera)); | |
composer.addPass(bloomPass); | |
composer.addPass(badTVPass); | |
composer.addPass(filmPass); | |
composer.addPass(blendPass); | |
} | |
function onFrame() { | |
requestAnimationFrame(onFrame); | |
update(); | |
render(); | |
} | |
function update() { | |
const timeDelta = clock.getDelta(); | |
const elapsed = clock.getElapsedTime(); | |
filmPass.uniforms.time.value += timeDelta; | |
badTVPass.uniforms.time.value += 0.01; | |
if (itemMesh) { | |
itemMesh.rotation.y = Math.sin(elapsed / 2) / 15; | |
itemMesh.rotation.z = Math.cos(elapsed / 2) / 50; | |
occMesh.rotation.copy(itemMesh.rotation); | |
} | |
} | |
function render() { | |
camera.layers.set(OCCLUSION_LAYER); | |
//renderer.setClearColor(0x000000); | |
occlusionComposer.render(); | |
camera.layers.set(DEFAULT_LAYER); | |
//renderer.setClearColor(0x000000); | |
composer.render(); | |
} | |
function setupGUI() { | |
let folder, | |
min, | |
max, | |
step, | |
updateShaderLight = function() { | |
const p = lightSource.position.clone(), | |
vector = p.project(camera), | |
x = (vector.x + 1) / 2, | |
y = (vector.y + 1) / 2; | |
vlShaderUniforms.lightPosition.value.set(x, y); | |
}; | |
updateShaderLight(); | |
// Bloom Controls | |
folder = gui.addFolder('Bloom'); | |
folder.add(bloomPass, 'radius') | |
.min(0) | |
.max(10) | |
.name('Radius'); | |
folder.add(bloomPass, 'threshold') | |
.min(0) | |
.max(1) | |
.name('Threshold'); | |
folder.add(bloomPass, 'strength') | |
.min(0) | |
.max(10) | |
.name('Strength'); | |
folder.open(); | |
// Bad TV Controls | |
folder = gui.addFolder('TV'); | |
folder.add(badTVPass.uniforms.distortion, 'value') | |
.min(0) | |
.max(10) | |
.name('Distortion 1'); | |
folder.add(badTVPass.uniforms.distortion2, 'value') | |
.min(0) | |
.max(10) | |
.name('Distortion 2'); | |
folder.add(badTVPass.uniforms.speed, 'value') | |
.min(0) | |
.max(1) | |
.name('Speed'); | |
folder.add(badTVPass.uniforms.rollSpeed, 'value') | |
.min(0) | |
.max(10) | |
.name('Roll Speed'); | |
folder.open(); | |
// Light Controls | |
folder = gui.addFolder('Light Position'); | |
folder.add(lightSource.position, 'x') | |
.min(-50) | |
.max(50) | |
.onChange(updateShaderLight); | |
folder.add(lightSource.position, 'y') | |
.min(-50) | |
.max(50) | |
.onChange(updateShaderLight); | |
folder.add(lightSource.position, 'z') | |
.min(-50) | |
.max(50) | |
.onChange(updateShaderLight); | |
folder.open(); | |
// Volumetric Light Controls | |
folder = gui.addFolder('Volumeteric Light Shader'); | |
folder.add(vlShaderUniforms.exposure, 'value') | |
.min(0) | |
.max(1) | |
.name('Exposure'); | |
folder.add(vlShaderUniforms.decay, 'value') | |
.min(0) | |
.max(1) | |
.name('Decay'); | |
folder.add(vlShaderUniforms.density, 'value') | |
.min(0) | |
.max(10) | |
.name('Density'); | |
folder.add(vlShaderUniforms.weight, 'value') | |
.min(0) | |
.max(1) | |
.name('Weight'); | |
folder.add(vlShaderUniforms.samples, 'value') | |
.min(1) | |
.max(100) | |
.name('Samples'); | |
folder.open(); | |
} | |
function addRenderTargetImage() { | |
const material = new THREE.ShaderMaterial(THREE.PassThroughShader); | |
material.uniforms.tDiffuse.value = occRenderTarget.texture; | |
const mesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), material); | |
composer.passes[1].scene.add(mesh); | |
mesh.visible = false; | |
const folder = gui.addFolder('Light Pass Render Image'); | |
folder.add(mesh, 'visible'); | |
folder.open(); | |
} | |
setupScene(); | |
setupPostprocessing(); | |
onFrame(); | |
setupGUI(); | |
addRenderTargetImage(); |
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
<script src="https://unpkg.com/three"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5.1/dat.gui.min.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/shaders/CopyShader.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/postprocessing/EffectComposer.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/postprocessing/RenderPass.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/postprocessing/ShaderPass.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/shaders/HorizontalBlurShader.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/shaders/VerticalBlurShader.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/shaders/FilmShader.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/shaders/LuminosityHighPassShader.js"></script> | |
<script src="https://unpkg.com/three@0.83.0/examples/js/postprocessing/UnrealBloomPass.js"></script> | |
<script src="https://cdn.rawgit.com/felixturner/bad-tv-shader/master/BadTVShader.js"></script> |
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
body{ | |
background: #000; | |
} | |
canvas{ | |
display: block; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment