Scene transition [render targets]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Transition</title>
</head>
<body></body>
<style>
body {
overflow: hidden;
margin: 0;
}
</style>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.165.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.165.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
class Transition {
constructor(scene1, scene2) {
this.scenes = [scene1, scene2];
this.uniforms = {
aspect: {
value: innerWidth / innerHeight
},
action: {
value: 0
},
timeStart: {
value: -1000
},
duration: { value: 3 }
};
this.renderTargets = Array.from({ length: 2 }, () => {
let rt = new THREE.WebGLRenderTarget(innerWidth, innerHeight, { colorSpace: "srgb", samples: 4 });
return rt;
})
this.screen = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), new THREE.ShaderMaterial({
uniforms: {
scene0: { value: this.renderTargets[0].texture },
scene1: { value: this.renderTargets[1].texture },
aspect: this.uniforms.aspect,
action: this.uniforms.action,
time: gu.time,
timeStart: this.uniforms.timeStart,
duration: this.uniforms.duration,
},
vertexShader: `
varying vec2 vUv;
void main(){vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);}
`,
fragmentShader: `
uniform sampler2D scene0;
uniform sampler2D scene1;
uniform float aspect;
uniform float action;
uniform float time;
uniform float timeStart;
uniform float duration;
varying vec2 vUv;
void main(){
vec2 uv = vUv;
vec4 s0 = texture(scene0, uv);
vec4 s1 = texture(scene1, uv);
vec2 transUV = (vUv - 0.5) * 2. * vec2(aspect, 1.);
float d = length(transUV);
float waveWidth = 0.25;
float halfWave = waveWidth * 0.5;
float maxLength = sqrt(aspect * aspect + 2.) + waveWidth;
float currWavePos = -halfWave + maxLength * clamp((time - timeStart) / duration, 0., 1.);
float f = smoothstep(currWavePos + halfWave, currWavePos - halfWave, d);
vec3 col = mix(s0.rgb, s1.rgb, f);
gl_FragColor = vec4(col, 1.);
}
`
}))
this.camera = new THREE.Camera();
}
generateTransitionTexture() {
}
render() {
this.scenes.forEach((s, sIdx) => {
renderer.setRenderTarget(this.renderTargets[sIdx]);
renderer.render(s, camera);
})
renderer.setRenderTarget(null);
renderer.render(this.screen, this.camera);
}
setRTSize(w, h) {
this.uniforms.aspect.value = w / h;
this.renderTargets.forEach(rt => rt.setSize(w, h));
}
}
class SceneFabric extends THREE.Scene {
constructor(name) {
super();
console.log(name)
let g = name == "sphere" ? new THREE.SphereGeometry() :
name == "box" ? new THREE.BoxGeometry(2, 2, 2) :
new THREE.TetrahedronGeometry();
let m = Math.random() < 0.5 ? new THREE.MeshBasicMaterial() : new THREE.MeshLambertMaterial();
m.wireframe = Math.random() < 0.5;
m.color.setHSL(Math.random(), 0.75, 0.75);
let io = new THREE.InstancedMesh(g, m, 100);
let dummy = new THREE.Object3D();
for (let i = 0; i < 100; i++) {
dummy.position.random().subScalar(0.5).multiplyScalar(50);
dummy.rotation.set(
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2
);
dummy.scale.random().multiplyScalar(0.9).addScalar(0.1).multiplyScalar(5);
dummy.updateMatrix();
io.setMatrixAt(i, dummy.matrix);
}
this.add(io);
let light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.randomDirection();
this.add(light, new THREE.AmbientLight(0xffffff, Math.PI * 0.5));
}
}
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 60);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
transition.setRTSize(innerWidth, innerHeight);
renderer.setSize(innerWidth, innerHeight);
});
window.addEventListener("dblclick", event => {
console.log(t);
transition.uniforms.timeStart.value = t;
transition.scenes[0] = scenes[destSceneIndex];
destSceneIndex = Math.floor(Math.random() * 7);
transition.scenes[1] = scenes[destSceneIndex];
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
let gu = {
time: { value: 0 }
}
let scenes = Array.from({ length: 7 }, () => {
let geoms = ["sphere", "box", ""];
return new SceneFabric(geoms[Math.floor(Math.random() * geoms.length * 2) % geoms.length]);
});
let destSceneIndex = Math.floor(Math.random() * 5);
let transition = new Transition(scenes[0], scenes[destSceneIndex]);
let clock = new THREE.Clock();
let t = 0;
renderer.setAnimationLoop(() => {
let dt = clock.getDelta();
t += dt;
gu.time.value = t;
controls.update();
transition.render();
//renderer.render(scene, camera);
})
</script>
</html>
Source: codepen by Paul