├── .gitignore
├── README.md
├── docs
├── index.html
├── main.a6c128a5.js
├── matcap.6a80787e.jpg
└── social.jpg
├── package-lock.json
├── package.json
└── src
├── assets
├── matcap.jpg
├── matcap.png
└── social.jpg
├── index.html
└── js
├── assets.js
├── camera.js
├── main.js
├── postfx
├── postfx.frag
├── postfx.js
└── postfx.vert
├── renderer.js
├── scene.js
├── settings.js
├── tube
├── position.frag
├── tube.frag
├── tube.js
├── tube.vert
└── velocity.frag
└── utils
├── deferred.js
├── fbo.js
└── pointer.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 | .cache
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TUBBBO - https://luruke.github.io/tubbbo/
2 |
3 | [](https://luruke.github.io/tubbbo/)
4 |
5 | A little WebGL experiment.
6 |
7 | > << Non capisci proprio un tubo >>
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
Tubbbo – WebGL experiment by @luruke | luruke.com
--------------------------------------------------------------------------------
/docs/matcap.6a80787e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luruke/tubbbo/e85be5b107b805659939d3170b61074a32869541/docs/matcap.6a80787e.jpg
--------------------------------------------------------------------------------
/docs/social.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luruke/tubbbo/e85be5b107b805659939d3170b61074a32869541/docs/social.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "boilerthree",
3 | "version": "0.0.1",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "parcel src/index.html --no-autoinstall --open",
9 | "build": "rm -rf ./docs; parcel build src/index.html -d docs --public-url '.' --no-source-maps; cp ./src/assets/social.jpg ./docs/social.jpg"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/luruke/boilerthree.git"
14 | },
15 | "author": "",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/luruke/boilerthree/issues"
19 | },
20 | "homepage": "https://github.com/luruke/boilerthree#readme",
21 | "devDependencies": {
22 | "bidello": "0.0.6",
23 | "detect-gpu": "^1.0.0",
24 | "glsl-curl-noise": "0.0.4",
25 | "glsl-fxaa": "^3.0.0",
26 | "glslify-bundle": "^5.1.1",
27 | "glslify-deps": "^1.3.1",
28 | "magicshader": "^0.1.2",
29 | "math-toolbox": "^1.12.0",
30 | "orbit-controls-es6": "^2.0.0",
31 | "parcel-bundler": "^1.12.3",
32 | "resource-loader": "^2.2.4",
33 | "three": "^0.105.2"
34 | },
35 | "dependencies": {}
36 | }
37 |
--------------------------------------------------------------------------------
/src/assets/matcap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luruke/tubbbo/e85be5b107b805659939d3170b61074a32869541/src/assets/matcap.jpg
--------------------------------------------------------------------------------
/src/assets/matcap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luruke/tubbbo/e85be5b107b805659939d3170b61074a32869541/src/assets/matcap.png
--------------------------------------------------------------------------------
/src/assets/social.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luruke/tubbbo/e85be5b107b805659939d3170b61074a32869541/src/assets/social.jpg
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tubbbo – WebGL experiment by @luruke | luruke.com
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/js/assets.js:
--------------------------------------------------------------------------------
1 | import Loader from 'resource-loader';
2 | import bidello from 'bidello';
3 | import deferred from './utils/deferred';
4 |
5 | const Resource = Loader.Resource;
6 | const RESOURCES = [
7 | {
8 | name: 'matcap',
9 | url: require('/assets/matcap.jpg')
10 | },
11 |
12 | // {
13 | // name: 'photo',
14 | // url: require('/assets/photo.glb'),
15 | // loadType: Resource.LOAD_TYPE.XHR,
16 | // xhrType: Resource.XHR_RESPONSE_TYPE.BLOB,
17 | // },
18 | ];
19 |
20 | /*
21 | assets.resources.photo.loading.then(res => {
22 | console.log(res.meta.data);
23 | });
24 | */
25 |
26 | class Assets {
27 | constructor() {
28 | this.resources = {};
29 |
30 | RESOURCES.forEach(entry => {
31 | this.resources[entry.name] = entry;
32 | this.resources[entry.name].loading = deferred();
33 | });
34 | }
35 |
36 | load() {
37 | this.deferred = deferred();
38 | this.loader = new Loader();
39 |
40 | bidello.trigger({ name: 'loadStart' });
41 |
42 | RESOURCES.forEach(res => {
43 | this.loader.add(res);
44 | });
45 |
46 | this.loader.onProgress.add(this.onProgress.bind(this));
47 | this.loader.load(this.finish.bind(this));
48 |
49 | return deferred;
50 | }
51 |
52 | onProgress(loader, meta) {
53 | bidello.trigger({ name: 'loadProgress' }, { progress: this.loader.progress });
54 | const res = this.resources[meta.name];
55 | res.meta = meta;
56 | res.loading.resolve(res);
57 | }
58 |
59 | finish() {
60 | this.deferred.resolve();
61 | bidello.trigger({ name: 'loadEnd' }, { resources: this.resources });
62 | }
63 | }
64 |
65 | export default new Assets();
66 |
--------------------------------------------------------------------------------
/src/js/camera.js:
--------------------------------------------------------------------------------
1 | import { PerspectiveCamera, Vector3 } from 'three';
2 | import { component } from 'bidello';
3 | import OrbitControls from 'orbit-controls-es6';
4 | import renderer from './renderer';
5 | import pointer from './utils/pointer';
6 |
7 | import { map, lerp } from 'math-toolbox';
8 |
9 | class Camera extends component(PerspectiveCamera) {
10 | constructor() {
11 | super(35, 0, 0.1, 1000);
12 |
13 | this.position.set(0, 0, 80);
14 | this.lookAt(new Vector3(0, 0, 0));
15 |
16 | // this.targetRot = new Vector3().copy(this.rotation);
17 | }
18 |
19 | initOrbitControl() {
20 | const controls = new OrbitControls(this, renderer.domElement);
21 |
22 | controls.enabled = true;
23 | controls.maxDistance = 900;
24 | controls.minDistance = 30;
25 | }
26 |
27 | onResize({ ratio }) {
28 | this.aspect = ratio;
29 | this.updateProjectionMatrix();
30 | }
31 |
32 | onRaf({ delta }) {
33 | const x = map(pointer.normalized.y, -1, 1, -0.2, 0.2);
34 | const y = map(pointer.normalized.x, -1, 1, 0.2, -0.2);
35 |
36 | this.rotation.x = lerp(this.rotation.x, x, .01)
37 | this.rotation.y = lerp(this.rotation.y, y, .01)
38 | }
39 | }
40 |
41 | export default new Camera();
--------------------------------------------------------------------------------
/src/js/main.js:
--------------------------------------------------------------------------------
1 | import * as helpers from 'bidello/helpers';
2 | import pointer from './utils/pointer';
3 |
4 | import renderer from './renderer';
5 | import camera from './camera';
6 | import scene from './scene';
7 | import { component } from 'bidello';
8 | import settings from './settings';
9 | import postfx from './postfx/postfx';
10 | import assets from './assets';
11 |
12 | class Site extends component() {
13 | init() {
14 | assets.load();
15 | document.body.appendChild(renderer.domElement);
16 | }
17 |
18 | onRaf() {
19 | // renderer.render(scene, camera);
20 | postfx.render(scene, camera);
21 | }
22 |
23 | onLoadEnd() {
24 | console.log('finished loader!');
25 | }
26 | }
27 |
28 | new Site();
--------------------------------------------------------------------------------
/src/js/postfx/postfx.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | uniform sampler2D uScene;
3 | uniform vec2 uResolution;
4 |
5 | #ifdef FXAA
6 | #pragma glslify: fxaa = require(glsl-fxaa)
7 | #endif
8 |
9 | float gamma = 2.2;
10 |
11 | vec3 linearToneMapping(vec3 color)
12 | {
13 | float exposure = 1.;
14 | color = clamp(exposure * color, 0., 1.);
15 | color = pow(color, vec3(1. / gamma));
16 | return color;
17 | }
18 |
19 | vec3 simpleReinhardToneMapping(vec3 color)
20 | {
21 | float exposure = 1.5;
22 | color *= exposure/(1. + color / exposure);
23 | color = pow(color, vec3(1. / gamma));
24 | return color;
25 | }
26 |
27 | vec3 lumaBasedReinhardToneMapping(vec3 color)
28 | {
29 | float luma = dot(color, vec3(0.2126, 0.7152, 0.0722));
30 | float toneMappedLuma = luma / (1. + luma);
31 | color *= toneMappedLuma / luma;
32 | color = pow(color, vec3(1. / gamma));
33 | return color;
34 | }
35 |
36 | vec3 whitePreservingLumaBasedReinhardToneMapping(vec3 color)
37 | {
38 | float white = 2.;
39 | float luma = dot(color, vec3(0.2126, 0.7152, 0.0722));
40 | float toneMappedLuma = luma * (1. + luma / (white*white)) / (1. + luma);
41 | color *= toneMappedLuma / luma;
42 | color = pow(color, vec3(1. / gamma));
43 | return color;
44 | }
45 |
46 | vec3 RomBinDaHouseToneMapping(vec3 color)
47 | {
48 | color = exp( -1.0 / ( 2.72*color + 0.15 ) );
49 | color = pow(color, vec3(1. / gamma));
50 | return color;
51 | }
52 |
53 | vec3 filmicToneMapping(vec3 color)
54 | {
55 | color = max(vec3(0.), color - vec3(0.004));
56 | color = (color * (6.2 * color + .5)) / (color * (6.2 * color + 1.7) + 0.06);
57 | return color;
58 | }
59 |
60 | vec3 Uncharted2ToneMapping(vec3 color)
61 | {
62 | float A = 0.15;
63 | float B = 0.50;
64 | float C = 0.10;
65 | float D = 0.20;
66 | float E = 0.02;
67 | float F = 0.30;
68 | float W = 11.2;
69 | float exposure = 2.;
70 | color *= exposure;
71 | color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
72 | float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
73 | color /= white;
74 | color = pow(color, vec3(1. / gamma));
75 | return color;
76 | }
77 |
78 |
79 | void main() {
80 | #ifdef FXAA
81 | vec3 color = fxaa(uScene, gl_FragCoord.xy, uResolution).rgb;
82 | #else
83 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
84 | vec3 color = texture2D(uScene, uv).rgb;
85 | #endif
86 |
87 | color = linearToneMapping(color);
88 |
89 | gl_FragColor = vec4(color, 1.0);
90 | }
--------------------------------------------------------------------------------
/src/js/postfx/postfx.js:
--------------------------------------------------------------------------------
1 | import {
2 | WebGLRenderTarget,
3 | Camera,
4 | RGBFormat,
5 | BufferGeometry,
6 | BufferAttribute,
7 | Mesh,
8 | Scene,
9 | RawShaderMaterial,
10 | Vector2,
11 | } from 'three';
12 |
13 | import renderer from '../renderer';
14 | import settings from '../settings';
15 | import { component } from 'bidello';
16 | import vertexShader from './postfx.vert';
17 | import fragmentShader from './postfx.frag';
18 |
19 | class PostFX extends component() {
20 | init() {
21 | this.renderer = renderer;
22 | this.scene = new Scene();
23 | this.dummyCamera = new Camera();
24 | this.geometry = new BufferGeometry();
25 |
26 | const vertices = new Float32Array([
27 | -1.0, -1.0,
28 | 3.0, -1.0,
29 | -1.0, 3.0
30 | ]);
31 |
32 | this.geometry.addAttribute('position', new BufferAttribute(vertices, 2));
33 | this.resolution = new Vector2();
34 | this.renderer.getDrawingBufferSize(this.resolution);
35 |
36 | this.target = new WebGLRenderTarget(this.resolution.x, this.resolution.y, {
37 | format: RGBFormat,
38 | stencilBuffer: false,
39 | depthBuffer: true,
40 | });
41 |
42 | const defines = {};
43 |
44 | settings.fxaa && (defines.FXAA = true);
45 |
46 | this.material = new RawShaderMaterial({
47 | defines,
48 | fragmentShader,
49 | vertexShader,
50 | uniforms: {
51 | uScene: { value: this.target.texture },
52 | uResolution: { value: this.resolution },
53 | },
54 | });
55 |
56 | this.triangle = new Mesh(this.geometry, this.material);
57 | this.triangle.frustumCulled = false;
58 | this.scene.add(this.triangle);
59 | }
60 |
61 | onResize() {
62 | this.renderer.getDrawingBufferSize(this.resolution);
63 | this.target.setSize(this.resolution.x, this.resolution.y);
64 | }
65 |
66 | render(scene, camera) {
67 | this.renderer.setRenderTarget(this.target);
68 | this.renderer.render(scene, camera);
69 | this.renderer.setRenderTarget(null);
70 | this.renderer.render(this.scene, this.dummyCamera);
71 | }
72 | }
73 |
74 | export default new PostFX();
--------------------------------------------------------------------------------
/src/js/postfx/postfx.vert:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec2 position;
3 |
4 | void main() {
5 | gl_Position = vec4(position, 1.0, 1.0);
6 | }
--------------------------------------------------------------------------------
/src/js/renderer.js:
--------------------------------------------------------------------------------
1 | import { WebGLRenderer } from 'three';
2 | import { component } from 'bidello';
3 | import settings from './settings';
4 |
5 | class Renderer extends component(WebGLRenderer) {
6 | constructor() {
7 | super({
8 | powerPreference: 'high-performance',
9 | antialiasing: false,
10 | })
11 |
12 | this.setPixelRatio(settings.dpr);
13 | }
14 |
15 | onResize({ width, height }) {
16 | this.setSize(width, height);
17 | }
18 | }
19 |
20 | export default new Renderer();
21 |
--------------------------------------------------------------------------------
/src/js/scene.js:
--------------------------------------------------------------------------------
1 | // import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
2 | import { Scene } from 'three';
3 | import { component } from 'bidello';
4 | import Tube from './tube/tube';
5 |
6 | class Stage extends component(Scene) {
7 | init() {
8 | this.add(new Tube());
9 | }
10 | }
11 |
12 | export default new Stage();
--------------------------------------------------------------------------------
/src/js/settings.js:
--------------------------------------------------------------------------------
1 | import { getGPUTier } from 'detect-gpu';
2 |
3 | const tier = getGPUTier({
4 | mobileBenchmarkPercentages: [10, 40, 30, 20], // (Default) [TIER_0, TIER_1, TIER_2, TIER_3]
5 | desktopBenchmarkPercentages: [10, 40, 30, 20], // (Default) [TIER_0, TIER_1, TIER_2, TIER_3]
6 | // forceRendererString: 'Apple A11 GPU', // (Development) Force a certain renderer string
7 | // forceMobile: true, // (Development) Force the use of mobile benchmarking scores
8 | });
9 |
10 | // const dpr = Math.min(1.5, window.devicePixelRatio || 1);
11 | const dpr = Math.min(1.5, window.devicePixelRatio || 1);
12 |
13 | const settings = {
14 | tier,
15 | dpr,
16 | fxaa: dpr <= 1,
17 | };
18 |
19 | console.log(`⚙️ settings`, settings);
20 |
21 | export default settings;
22 |
--------------------------------------------------------------------------------
/src/js/tube/position.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform sampler2D texture;
4 | uniform sampler2D velocity;
5 | uniform float uTime;
6 | uniform float uMouseAngle;
7 | uniform vec3 uMousePos;
8 |
9 | // float qinticOut(float t) {
10 | // return 1.0 - (pow(t - 1.0, 5.0));
11 | // }
12 |
13 | // https://gist.github.com/yiwenl/3f804e80d0930e34a0b33359259b556c
14 | mat4 rotationMatrix(vec3 axis, float angle) {
15 | axis = normalize(axis);
16 | float s = sin(angle);
17 | float c = cos(angle);
18 | float oc = 1.0 - c;
19 |
20 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
21 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
22 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
23 | 0.0, 0.0, 0.0, 1.0);
24 | }
25 |
26 | vec3 rotate(vec3 v, vec3 axis, float angle) {
27 | mat4 m = rotationMatrix(axis, angle);
28 | return (m * vec4(v, 1.0)).xyz;
29 | }
30 |
31 |
32 | float rand(vec2 co){
33 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
34 | }
35 |
36 | void main() {
37 | float pixelWidth = 1.0 / RESOLUTION.x;
38 | vec2 uv = gl_FragCoord.xy / RESOLUTION.xy;
39 | vec4 oldValues = texture2D(texture, uv);
40 |
41 | vec4 head = texture2D(velocity, vec2(0.0, uv.y));
42 |
43 | if (uv.x <= pixelWidth) {
44 | // vec4 velocityData = texture2D(velocity, uv);
45 | // float speed = clamp(smoothstep(0.2, 0.5, velocityData.a), 0.0, 1.0);
46 |
47 | if (head.a >= 1.0) {
48 | // float speed = 1.0;
49 |
50 | // speed += 1.0 - smoothstep(0.0, 50.0, head.a) * 4.0;
51 | oldValues.xyz += head.xyz;// * speed;
52 | }
53 |
54 | // if (velocityData.a > RESOLUTION.x * 2.0) {
55 | // // oldValues.xyz = vec3(0.0);
56 | // } else {
57 | // // oldValues.xyz = vec3(0.0);
58 | // }
59 |
60 | } else {
61 | vec3 toFollow = texture2D(texture, uv - vec2(pixelWidth, 0.0)).xyz;
62 |
63 | // length of tube
64 | // float speed = .8 * uv.y;
65 | // float t = .1 + speed;
66 |
67 | float t = 0.2;
68 |
69 | // float time = (sin(uTime + uv.y * 40.0) + 1.0) / 2.0;
70 |
71 | // t += qinticOut(1.0 - uv.x) * 0.8;
72 |
73 | oldValues.xyz = mix(oldValues.xyz, toFollow, t);
74 | }
75 |
76 | if (head.a <= 0.0) {
77 | // oldValues.xyz = vec3(0.0);
78 | oldValues.xyz = uMousePos - vec3(0.0, 15.0, 0.0);
79 | oldValues.y += uv.x * 20.0;
80 |
81 | oldValues.x += rand(vec2(uv.y, uTime)) * 10.0;
82 | oldValues.y += rand(vec2(uv.y * 321.3, uTime * 0.2)) * 10.0;
83 | oldValues.z += rand(vec2(uMousePos + uTime));
84 |
85 | // vec3 pivot = vec3(0.0, -10.0, 0.0);
86 | // oldValues.xyz -= pivot;
87 | oldValues.xyz -= uMousePos;
88 | oldValues.xyz = rotate(oldValues.xyz, vec3(0.0, 0.0, 1.0), -(3.14 / 2.0) + uMouseAngle);
89 | oldValues.xyz += uMousePos;
90 | // oldValues.xyz += pivot;
91 | }
92 |
93 | // oldValues.y += .01;
94 |
95 | oldValues.a = head.a;
96 |
97 | gl_FragColor = oldValues;
98 | }
--------------------------------------------------------------------------------
/src/js/tube/tube.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform sampler2D uMatcap;
4 | //uniform vec3 cameraPosition;
5 | uniform vec3 uMousePos;
6 |
7 | uniform vec3 color;// ms({ value: '#ff0000' })
8 | varying vec3 vNormal;
9 | varying vec3 vViewPosition;
10 | varying float vAo;
11 | varying float vProgress;
12 | varying vec3 wPos;
13 | varying float vLife;
14 |
15 | vec4 sRGBToLinear( in vec4 value ) {
16 | return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );
17 | }
18 | // https://github.com/hughsk/glsl-fog/blob/master/exp.glsl
19 | float fogFactorExp(
20 | const float dist,
21 | const float density
22 | ) {
23 | return 1.0 - clamp(exp(-density * dist), 0.0, 1.0);
24 | }
25 |
26 | float fogFactorExp2(
27 | const float dist,
28 | const float density
29 | ) {
30 | const float LOG2 = -1.442695;
31 | float d = density * dist;
32 | return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0);
33 | }
34 |
35 | void main(){
36 |
37 | vec3 normal = normalize(vNormal);
38 |
39 |
40 | // if (vProgress >= 0.75) {
41 | // normal = vec3(0.0, 1.0, 0.0);
42 | // // vec3 vPos = vViewPosition;
43 | // // vec3 fdx = vec3( dFdx( vPos.x ), dFdx( vPos.y ), dFdx( vPos.z ) );
44 | // // vec3 fdy = vec3( dFdy( vPos.x ), dFdy( vPos.y ), dFdy( vPos.z ) );
45 | // // normal = normalize( cross( fdx, fdy ) );
46 | // }
47 |
48 | vec3 viewDir = normalize( vViewPosition );
49 | vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );
50 | vec3 y = cross( viewDir, x );
51 | vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;
52 |
53 | vec4 color = sRGBToLinear(texture2D(uMatcap, uv));
54 |
55 | // fake "AO"
56 | float ao = pow(1.0 - vAo, 2.0);
57 | color.rgb = mix(color.rgb * 0.2, color.rgb, ao);
58 |
59 | // SSS - https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
60 |
61 | // vec3 lightPos = vec3(0.0, 4.0, 0.0);
62 | vec3 lightPos = uMousePos;
63 | // lightPos.z -= 40.0;
64 |
65 | vec3 ssLight = vec3(0.7, 0.2, 0.2) * 0.07;
66 | vec3 fLTThickness = ssLight * pow(1.0 - vAo, 6.0);
67 | float fLTScale = 20.6;
68 | float fLTDistortion = 0.18;
69 | float fLTAmbient = 0.0;
70 | float iLTPower = 40.0;
71 | float dist = distance(lightPos, wPos);
72 | float fLightAttenuation = (1.0 - clamp(pow(dist / 20.0, 4.0), 0.0, 1.0));
73 | vec3 vLight = normalize(-vViewPosition - lightPos);
74 | vec3 vLTLight = normalize(vLight + (normal * fLTDistortion));
75 | float fLTDot = pow(clamp(dot(viewDir, -vLTLight), 0.0, 1.0), iLTPower) * fLTScale;
76 | vec3 fLT = fLightAttenuation * (fLTDot + fLTAmbient) * fLTThickness;
77 | color.rgb += fLT;
78 |
79 | // Fog
80 | color.rgb = mix(color.rgb, color.rgb * 0.1, smoothstep(0.0, 150.0, vViewPosition.z));
81 |
82 |
83 | gl_FragColor = color;
84 | }
--------------------------------------------------------------------------------
/src/js/tube/tube.js:
--------------------------------------------------------------------------------
1 | import { component } from 'bidello';
2 |
3 | import {
4 | Object3D,
5 | CylinderBufferGeometry,
6 | Mesh,
7 | DoubleSide,
8 | Vector4,
9 | BufferAttribute,
10 | Vector2,
11 | InstancedBufferGeometry,
12 | InstancedBufferAttribute,
13 | Texture,
14 | RepeatWrapping,
15 | MeshBasicMaterial,
16 | Vector3,
17 | AdditiveBlending,
18 | } from 'three';
19 |
20 | import {
21 | lerp
22 | } from 'math-toolbox';
23 |
24 | import MagicShader, { gui } from 'magicshader';
25 | import FBO from '../utils/fbo';
26 | import assets from '../assets';
27 | import pointer from '../utils/pointer';
28 |
29 | export default class extends component(Object3D) {
30 | init() {
31 | const LIFE = 1500;
32 | const INSTANCES = 100;
33 | const WIDTH = 64;
34 | const HEIGHT = INSTANCES;
35 |
36 | gui.destroy();
37 |
38 | this.mousePos = new Vector3();
39 | this.oldMousePos = new Vector2();
40 | this.angle = 0;
41 | this.stop = false;
42 |
43 | const velocityData = new Float32Array(1 * HEIGHT * 4);
44 |
45 | for (let i = 0; i < velocityData.length; i += 4) {
46 | // velocityData[i + 3] = i / velocityData.length; // take the alpha part
47 | velocityData[i + 3] = (i / velocityData.length) * LIFE; // take the alpha part
48 | }
49 |
50 | this.velocity = new FBO({
51 | data: velocityData,
52 | width: 1, // Only the head needs velocity
53 | height: HEIGHT,
54 | name: 'velocity',
55 | shader: require('./velocity.frag'),
56 | uniforms: {
57 | uTime: { value: 0 },
58 | uLife: { value: LIFE }
59 | // uMousePos: { value: this.mousePos }
60 | },
61 | });
62 |
63 | this.curvepos = new FBO({
64 | width: WIDTH,
65 | height: HEIGHT,
66 | name: 'position',
67 | shader: require('./position.frag'),
68 | uniforms: {
69 | uTime: { value: 0 },
70 | velocity: { value: this.velocity.target },
71 | uMousePos: { value: this.mousePos },
72 | uMouseAngle: { value: this.angle },
73 | },
74 | });
75 |
76 | this.velocity.material.gui.add(this, 'stop');
77 |
78 | this.velocity.uniforms.uPosition = {
79 | value: this.curvepos.target
80 | };
81 |
82 | // this.curvepos.update();
83 |
84 | const radialSegment = 20;// 50;
85 | const heightSegment = 150;// 50;
86 |
87 | const cylinder = new CylinderBufferGeometry(1, 1, 1, radialSegment, heightSegment, false);
88 | cylinder.rotateZ(Math.PI / 2);
89 |
90 | this.geometry = new InstancedBufferGeometry().copy(cylinder);
91 |
92 | // const pp = this.geometry.attributes.position.array;
93 | // let min = undefined;
94 | // let max = undefined;
95 | // for (let i = 0; i < pp.length; i+=4) {
96 | // let x = pp[i + 0]
97 | // let y = pp[i + 1]
98 | // let z = pp[i + 2]
99 |
100 | // let t = y;
101 |
102 | // if (t < min || typeof min === 'undefined') {
103 | // min = t;
104 | // }
105 |
106 | // if (t > max || typeof max === 'undefined') {
107 | // max = t;
108 | // }
109 | // }
110 |
111 | // console.log('min ', min);
112 | // console.log('max ', max);
113 |
114 | const tmp = new Vector2();
115 | const angles = [];
116 | const indexes = [];
117 | const dat = this.geometry.attributes.position.array;
118 |
119 | for (let i = 0; i < this.geometry.attributes.position.count; i++) {
120 | const index = i * 3;
121 | const x = dat[index + 0]; // x
122 | const y = dat[index + 1]; // y
123 | const z = dat[index + 2]; // z
124 |
125 | tmp.set(y, z).normalize();
126 | angles.push(Math.atan2(tmp.y, tmp.x));
127 | }
128 |
129 | for (let i = 0; i < INSTANCES; i++) {
130 | indexes.push(i / INSTANCES);
131 | }
132 |
133 | this.geometry.addAttribute('aAngle', new BufferAttribute(new Float32Array(angles), 1));
134 | this.geometry.addAttribute('aIndex', new InstancedBufferAttribute(new Float32Array(indexes), 1));
135 |
136 | this.matcap = new Texture();
137 |
138 | assets.resources.matcap.loading.then(res => {
139 | const tex = new Texture(res.meta.data, RepeatWrapping, RepeatWrapping);
140 | tex.needsUpdate = true;
141 | this.material.uniforms.uMatcap.value = tex;
142 | });
143 |
144 | this.material = new MagicShader({
145 | // wireframe: true,
146 | transparent: true,
147 | name: 'Tube',
148 | extensions: {
149 | derivatives: true,
150 | },
151 | defines: {
152 | RESOLUTION: `vec2(${WIDTH.toFixed(1)}, ${HEIGHT.toFixed(1)})`,
153 | LIFE: LIFE.toFixed(1),
154 | },
155 | uniforms: {
156 | uData: { value: this.curvepos.target },
157 | uMatcap: { value: this.matcap },
158 | uMousePos: { value: this.mousePos },
159 | uMouseAngle: { value: this.angle },
160 | },
161 | // side: DoubleSide,
162 | vertexShader: require('./tube.vert'),
163 | fragmentShader: require('./tube.frag')
164 | });
165 |
166 | this.mesh = new Mesh(this.geometry, this.material);
167 | this.mesh.frustumCulled = false;
168 |
169 | this.add(this.mesh);
170 | }
171 |
172 | stop() {
173 | this.stop = !this.stop;
174 | }
175 |
176 | // onPointerMove({ pointer }) {
177 |
178 | // }
179 |
180 | onRaf({ delta }) {
181 | this.mousePos.lerp(pointer.world, .1);
182 |
183 | if (pointer.y !== this.oldMousePos.y && pointer.x !== this.oldMousePos.x) {
184 | this.angle = Math.atan2(pointer.y - this.oldMousePos.y, pointer.x - this.oldMousePos.x);
185 | }
186 |
187 | this.curvepos.uniforms.uMouseAngle.value = lerp(this.curvepos.uniforms.uMouseAngle.value, this.angle, .1);
188 | this.material.uniforms.uMouseAngle.value = this.curvepos.uniforms.uMouseAngle.value;
189 |
190 | this.oldMousePos.set(pointer.x, pointer.y);
191 |
192 | if (this.stop) {
193 | return;
194 | }
195 |
196 | this.curvepos.uniforms.uTime.value += delta * 2;
197 | this.velocity.uniforms.uTime.value += delta * 2;
198 |
199 | this.velocity.uniforms.uPosition.value = this.curvepos.target;
200 | this.velocity.update();
201 |
202 | this.curvepos.uniforms.velocity.value = this.velocity.target;
203 | this.curvepos.update();
204 | }
205 | }
--------------------------------------------------------------------------------
/src/js/tube/tube.vert:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec3 position;
3 | attribute vec2 uv;
4 | attribute vec3 normal;
5 | attribute float aAngle;
6 | attribute float aIndex;
7 |
8 | uniform mat4 modelMatrix;
9 | uniform mat4 viewMatrix;
10 | uniform vec3 cameraPosition;
11 | uniform mat4 modelViewMatrix;
12 | uniform mat4 projectionMatrix;
13 | uniform mat3 normalMatrix;
14 | uniform sampler2D uData;
15 | uniform float uMouseAngle;
16 |
17 | varying float vProgress;
18 | varying vec3 vNormal;
19 | varying vec3 vViewPosition;
20 | varying vec3 wPos;
21 | varying float vAo;
22 | varying float vLife;
23 |
24 | const float pixelWidth = 1.0 / (RESOLUTION.x);
25 |
26 | float qinticOut(float t) {
27 | return 1.0 - (pow(t - 1.0, 5.0));
28 | }
29 |
30 | float cubicOut(float t) {
31 | float f = t - 1.0;
32 | return f * f * f + 1.0;
33 | }
34 |
35 | float map(float value, float min1, float max1, float min2, float max2) {
36 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
37 | }
38 |
39 | void main(){
40 | float progress = 1.0 - (position.x + 1.0) / 2.0;
41 | vProgress = progress;
42 |
43 | // https://mattdesl.svbtle.com/shaping-curves-with-parametric-equations
44 |
45 | vec4 data = texture2D(uData, vec2(progress, aIndex));
46 | vLife = data.a;
47 |
48 | vec2 volume = vec2(0.8);
49 | // float volume = 0.8;
50 |
51 | volume *= cubicOut(smoothstep(50.0, 200.0, vLife));
52 | volume *= cubicOut(clamp(smoothstep(LIFE, LIFE - 100.0, vLife), 0.0, 1.0));
53 |
54 | if (vLife <= 0.0) {
55 | volume = vec2(0.0);
56 | }
57 |
58 | vec3 cur = data.xyz;
59 | vec3 next = texture2D(uData, vec2(progress - pixelWidth, aIndex)).xyz;
60 | vec3 next2 = texture2D(uData, vec2(progress - pixelWidth * 2.0, aIndex)).xyz;
61 |
62 | float introMul = cubicOut(clamp(smoothstep(LIFE, LIFE - 100.0, vLife), 0.0, 1.0));
63 |
64 | float val = map(introMul, 0.0, 1.0, 0.88, 1.0);
65 |
66 | cur *= val;
67 | next *= val;
68 | next2 *= val;
69 |
70 | // compute the Frenet-Serret frame
71 | vec3 T = normalize(next - cur);
72 | vec3 B = normalize(cross(T, next + cur));
73 | vec3 N = -normalize(cross(B, T));
74 |
75 | // extrude outward to create a tube
76 | float tubeAngle = aAngle;
77 | float circX = cos(tubeAngle);
78 | float circY = sin(tubeAngle);
79 |
80 | vec3 calculatedNormal = normalize(B * circX + N * circY);
81 | vNormal = normalize(normalMatrix * calculatedNormal);
82 |
83 | // vec3 pos = cur + B * volume.x * circX + N * volume.y * circY;
84 | vec3 pos = cur + calculatedNormal * volume.x;
85 |
86 | // pos.xyz -= cur;
87 | // pos.yz *= cubicOut(clamp(smoothstep(LIFE, LIFE - 100.0, vLife), 0.0, 1.0));
88 | // pos.xyz += cur;
89 |
90 | vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
91 | vViewPosition = - mvPosition.xyz;
92 |
93 | vAo = length(abs(cross(
94 | normalize(cur - next),
95 | normalize(next - next2)
96 | )));
97 |
98 | if (position.x > 0.49) {
99 | vNormal = normalize(cur - next);
100 | }
101 | // } else if (vProgress == 1.0) {
102 | // vNormal = normalize(next - cur);
103 | // }
104 |
105 | wPos = (modelMatrix * vec4(pos, 1.0)).xyz;
106 |
107 |
108 | gl_Position = projectionMatrix * mvPosition;
109 | }
--------------------------------------------------------------------------------
/src/js/tube/velocity.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform sampler2D texture;
4 | uniform sampler2D uPosition;
5 | uniform float uTime;
6 | uniform float uLife;
7 |
8 | const int OCTAVES = 2;
9 |
10 | vec4 mod289(vec4 x) {
11 | return x - floor(x * (1.0 / 289.0)) * 289.0;
12 | }
13 | float mod289(float x) {
14 | return x - floor(x * (1.0 / 289.0)) * 289.0;
15 | }
16 | vec4 permute(vec4 x) {
17 | return mod289(((x*34.0)+1.0)*x);
18 | }
19 | float permute(float x) {
20 | return mod289(((x*34.0)+1.0)*x);
21 | }
22 | vec4 taylorInvSqrt(vec4 r) {
23 | return 1.79284291400159 - 0.85373472095314 * r;
24 | }
25 | float taylorInvSqrt(float r) {
26 | return 1.79284291400159 - 0.85373472095314 * r;
27 | }
28 | vec4 grad4(float j, vec4 ip) {
29 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
30 | vec4 p, s;
31 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
32 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
33 | s = vec4(lessThan(p, vec4(0.0)));
34 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;
35 | return p;
36 | }
37 | #define F4 0.309016994374947451
38 |
39 | vec4 simplexNoiseDerivatives (vec4 v) {
40 | const vec4 C = vec4( 0.138196601125011, 0.276393202250021, 0.414589803375032, -0.447213595499958);
41 | vec4 i = floor(v + dot(v, vec4(F4)) );
42 | vec4 x0 = v - i + dot(i, C.xxxx);
43 | vec4 i0;
44 | vec3 isX = step( x0.yzw, x0.xxx );
45 | vec3 isYZ = step( x0.zww, x0.yyz );
46 | i0.x = isX.x + isX.y + isX.z;
47 | i0.yzw = 1.0 - isX;
48 | i0.y += isYZ.x + isYZ.y;
49 | i0.zw += 1.0 - isYZ.xy;
50 | i0.z += isYZ.z;
51 | i0.w += 1.0 - isYZ.z;
52 | vec4 i3 = clamp( i0, 0.0, 1.0 );
53 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
54 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );
55 | vec4 x1 = x0 - i1 + C.xxxx;
56 | vec4 x2 = x0 - i2 + C.yyyy;
57 | vec4 x3 = x0 - i3 + C.zzzz;
58 | vec4 x4 = x0 + C.wwww;
59 | i = mod289(i);
60 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
61 | vec4 j1 = permute( permute( permute( permute (
62 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
63 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
64 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
65 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
66 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;
67 | vec4 p0 = grad4(j0, ip);
68 | vec4 p1 = grad4(j1.x, ip);
69 | vec4 p2 = grad4(j1.y, ip);
70 | vec4 p3 = grad4(j1.z, ip);
71 | vec4 p4 = grad4(j1.w, ip);
72 | vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
73 | p0 *= norm.x;
74 | p1 *= norm.y;
75 | p2 *= norm.z;
76 | p3 *= norm.w;
77 | p4 *= taylorInvSqrt(dot(p4, p4));
78 | vec3 values0 = vec3(dot(p0, x0), dot(p1, x1), dot(p2, x2)); //value of contributions from each corner at point
79 |
80 | vec2 values1 = vec2(dot(p3, x3), dot(p4, x4));
81 | vec3 m0 = max(0.5 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0); //(0.5 - x^2) where x is the distance
82 |
83 | vec2 m1 = max(0.5 - vec2(dot(x3, x3), dot(x4, x4)), 0.0);
84 | vec3 temp0 = -6.0 * m0 * m0 * values0;
85 | vec2 temp1 = -6.0 * m1 * m1 * values1;
86 | vec3 mmm0 = m0 * m0 * m0;
87 | vec2 mmm1 = m1 * m1 * m1;
88 | float dx = temp0[0] * x0.x + temp0[1] * x1.x + temp0[2] * x2.x + temp1[0] * x3.x + temp1[1] * x4.x + mmm0[0] * p0.x + mmm0[1] * p1.x + mmm0[2] * p2.x + mmm1[0] * p3.x + mmm1[1] * p4.x;
89 | float dy = temp0[0] * x0.y + temp0[1] * x1.y + temp0[2] * x2.y + temp1[0] * x3.y + temp1[1] * x4.y + mmm0[0] * p0.y + mmm0[1] * p1.y + mmm0[2] * p2.y + mmm1[0] * p3.y + mmm1[1] * p4.y;
90 | float dz = temp0[0] * x0.z + temp0[1] * x1.z + temp0[2] * x2.z + temp1[0] * x3.z + temp1[1] * x4.z + mmm0[0] * p0.z + mmm0[1] * p1.z + mmm0[2] * p2.z + mmm1[0] * p3.z + mmm1[1] * p4.z;
91 | float dw = temp0[0] * x0.w + temp0[1] * x1.w + temp0[2] * x2.w + temp1[0] * x3.w + temp1[1] * x4.w + mmm0[0] * p0.w + mmm0[1] * p1.w + mmm0[2] * p2.w + mmm1[0] * p3.w + mmm1[1] * p4.w;
92 | return vec4(dx, dy, dz, dw) * 49.0;
93 | }
94 |
95 | vec3 curl(in vec3 p, in float noiseTime, in float persistence) {
96 | vec4 xNoisePotentialDerivatives = vec4(0.0);
97 | vec4 yNoisePotentialDerivatives = vec4(0.0);
98 | vec4 zNoisePotentialDerivatives = vec4(0.0);
99 | for(int i = 0; i < OCTAVES; ++i) {
100 | float twoPowI = pow(2.0, float(i));
101 | float scale = 0.5 * twoPowI * pow(persistence, float(i));
102 |
103 | xNoisePotentialDerivatives += simplexNoiseDerivatives(vec4(p * twoPowI, noiseTime)) * scale;
104 | yNoisePotentialDerivatives += simplexNoiseDerivatives(vec4((p + vec3(123.4, 129845.6, -1239.1)) * twoPowI, noiseTime)) * scale;
105 | zNoisePotentialDerivatives += simplexNoiseDerivatives(vec4((p + vec3(-9519.0, 9051.0, -123.0)) * twoPowI, noiseTime)) * scale;
106 | }
107 | return vec3(
108 | zNoisePotentialDerivatives[1] - yNoisePotentialDerivatives[2], xNoisePotentialDerivatives[2] - zNoisePotentialDerivatives[0], yNoisePotentialDerivatives[0] - xNoisePotentialDerivatives[1]
109 | );
110 | }
111 |
112 | void main() {
113 | float pixelWidth = 1.0 / RESOLUTION.x;
114 | vec2 uv = gl_FragCoord.xy / RESOLUTION.xy;
115 |
116 | vec4 oldValue = texture2D(texture, uv);
117 | vec4 currentPosition = texture2D(uPosition, uv);
118 | vec4 newValue = oldValue;
119 |
120 | float curlSize = 0.001;
121 | float speed = 0.02;
122 | float spacing = uv.y * 20.0;
123 |
124 | newValue.xyz += curl(currentPosition.xyz * curlSize, uTime + spacing, 0.3) * speed;
125 | newValue.xyz = mix(newValue.xyz, -newValue.xyz, length(newValue.xyz) * 0.3);
126 |
127 |
128 | if (newValue.a <= 0.0) {
129 | newValue.a += uLife;
130 | } else {
131 | newValue.a -= 1.0;
132 | }
133 |
134 | gl_FragColor = newValue;
135 | }
--------------------------------------------------------------------------------
/src/js/utils/deferred.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | let _resolve = undefined;
3 | let _reject = undefined;
4 | const promise = new Promise((resolve, reject) => {
5 | _resolve = resolve;
6 | _reject = reject;
7 | });
8 |
9 | promise.resolve = _resolve;
10 | promise.reject = _reject;
11 |
12 | return promise;
13 | };
14 |
--------------------------------------------------------------------------------
/src/js/utils/fbo.js:
--------------------------------------------------------------------------------
1 | import {
2 | WebGLRenderTarget,
3 | NearestFilter,
4 | DataTexture,
5 | RGBAFormat,
6 | FloatType,
7 | HalfFloatType,
8 | Camera,
9 | BufferGeometry,
10 | BufferAttribute,
11 | Scene,
12 | Mesh,
13 | } from 'three';
14 |
15 | import renderer from '../renderer';
16 | import MagicShader from 'magicshader';
17 |
18 | export const isAvailable = (() => {
19 | const gl = renderer.getContext();
20 |
21 | if (!gl.getExtension('OES_texture_float')) {
22 | return false;
23 | }
24 |
25 | if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) === 0) {
26 | return false;
27 | }
28 |
29 | return true;
30 | })();
31 |
32 | const iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
33 | const type = iOS ? HalfFloatType : FloatType;
34 |
35 | export default class FBO {
36 | constructor({
37 | width,
38 | height,
39 | data,
40 | name,
41 | shader,
42 | uniforms = {}
43 | }) {
44 | this.options = arguments[0];
45 |
46 | const vertices = new Float32Array([
47 | -1.0, -1.0,
48 | 3.0, -1.0,
49 | -1.0, 3.0
50 | ]);
51 |
52 | this.renderer = renderer;
53 | this.camera = new Camera();
54 | this.scene = new Scene();
55 | this.index = 0;
56 | this.copyData = true;
57 | this.texture = new DataTexture(
58 | data || new Float32Array((width * height * 4)),
59 | width,
60 | height,
61 | RGBAFormat,
62 | FloatType,
63 | // type,
64 | );
65 | this.texture.needsUpdate = true;
66 |
67 | this.rt = [this.createRT(), this.createRT()];
68 |
69 | this.geometry = new BufferGeometry();
70 | this.geometry.addAttribute('position', new BufferAttribute(vertices, 2));
71 |
72 | this.material = new MagicShader({
73 | name: name || 'FBO',
74 | defines: {
75 | RESOLUTION: `vec2(${width.toFixed(1)}, ${height.toFixed(1)})`
76 | },
77 | uniforms: {
78 | ...uniforms,
79 | texture: { value: this.texture }
80 | },
81 | vertexShader: `
82 | precision highp float;
83 | attribute vec3 position;
84 |
85 | void main() {
86 | gl_Position = vec4(position, 1.0);
87 | }
88 | `,
89 | fragmentShader: shader || `
90 | precision highp float;
91 | uniform sampler2D texture;
92 | void main() {
93 | vec2 uv = gl_FragCoord.xy / RESOLUTION.xy;
94 | gl_FragColor = texture2D(texture, uv);
95 | }
96 | `,
97 | });
98 |
99 | this.mesh = new Mesh(this.geometry, this.material);
100 | this.mesh.frustumCulled = false;
101 | this.scene.add(this.mesh);
102 | }
103 |
104 | createRT() {
105 | return new WebGLRenderTarget(this.options.width, this.options.height, {
106 | minFilter: NearestFilter,
107 | magFilter: NearestFilter,
108 | stencilBuffer: false,
109 | depthBuffer: false,
110 | type,
111 | });
112 | }
113 |
114 | get target() {
115 | return this.rt[this.index].texture;
116 | }
117 |
118 | get uniforms() {
119 | return this.material.uniforms;
120 | }
121 |
122 | update(switchBack = true) {
123 | const destIndex = this.index === 0 ? 1 : 0;
124 | const old = this.rt[this.index];
125 | const dest = this.rt[destIndex];
126 |
127 | this.material.uniforms.texture.value = this.copyData ? this.texture : old.texture;
128 |
129 | const oldMainTarget = this.renderer.getRenderTarget();
130 | this.renderer.setRenderTarget(dest);
131 | this.renderer.render(this.scene, this.camera);
132 | switchBack && this.renderer.setRenderTarget(oldMainTarget);
133 |
134 | this.index = destIndex;
135 | this.copyData = false;
136 | }
137 | }
--------------------------------------------------------------------------------
/src/js/utils/pointer.js:
--------------------------------------------------------------------------------
1 | import {
2 | Vector2,
3 | Vector3
4 | } from 'three';
5 |
6 | import { clamp } from 'math-toolbox';
7 | import bidello from 'bidello';
8 | import { viewport } from 'bidello/helpers';
9 | import camera from '../camera';
10 |
11 | // TODO use pointer velocity/direction to push noise tubes.
12 |
13 | class Pointer {
14 | constructor() {
15 | this.x = 0;
16 | this.y = 0;
17 | this.isTouching = true;
18 | this.distance = 0;
19 |
20 | this.hold = new Vector2();
21 | this.last = new Vector2();
22 | this.delta = new Vector2();
23 | this.move = new Vector2();
24 | this.world = new Vector3();
25 | this.normalized = new Vector2();
26 | this._tmp = new Vector3();
27 |
28 | this.bind();
29 | }
30 |
31 | bind() {
32 | const container = window;
33 |
34 | container.addEventListener('touchstart', this.onStart.bind(this), { passive: false });
35 | container.addEventListener('touchmove', this.onMove.bind(this), { passive: false });
36 | container.addEventListener('touchend', this.onEnd.bind(this), { passive: false });
37 | container.addEventListener('touchcancel', this.onEnd.bind(this), { passive: false });
38 |
39 | container.addEventListener('mousedown', this.onStart.bind(this));
40 | container.addEventListener('mousemove', this.onMove.bind(this));
41 | container.addEventListener('mouseup', this.onEnd.bind(this));
42 | container.addEventListener('contextmenu', this.onEnd.bind(this));
43 | }
44 |
45 | convertEvent(e) {
46 | e.preventDefault();
47 | e.stopPropagation();
48 |
49 | const t = {
50 | x: 0,
51 | y: 0,
52 | };
53 |
54 | if (!e) {
55 | return t;
56 | }
57 |
58 | if (e.windowsPointer) {
59 | return e;
60 | }
61 |
62 | if (e.touches || e.changedTouches) {
63 | if (e.touches.length) {
64 | t.x = e.touches[0].pageX;
65 | t.y = e.touches[0].pageY;
66 | } else {
67 | t.x = e.changedTouches[0].pageX;
68 | t.y = e.changedTouches[0].pageY;
69 | }
70 | } else {
71 | t.x = e.pageX;
72 | t.y = e.pageY;
73 | }
74 |
75 | t.x = clamp(0, viewport.width, t.x);
76 | t.y = clamp(0, viewport.height, t.y);
77 |
78 | return t;
79 | }
80 |
81 | onStart(event) {
82 | const e = this.convertEvent(event);
83 |
84 | this.isTouching = true;
85 | this.x = e.x;
86 | this.y = e.y;
87 |
88 | this.hold.set(e.x, e.y);
89 | this.last.set(e.x, e.y);
90 | this.delta.set(0, 0);
91 | this.move.set(0, 0);
92 |
93 | this.normalized.x = ((this.x / viewport.width) * 2) - 1;
94 | this.normalized.y = (-(this.y / viewport.height) * 2) + 1;
95 | this.distance = 0;
96 |
97 | bidello.trigger({ name: 'pointerStart' }, {
98 | pointer: this,
99 | });
100 | }
101 |
102 | onMove(event) {
103 | const e = this.convertEvent(event);
104 |
105 | if (this.isTouching) {
106 | this.move.x = e.x - this.hold.x;
107 | this.move.y = e.y - this.hold.y;
108 | }
109 |
110 | // if (this.last.x !== e.x || this.last.y !== e.y) {
111 | // this.last.set(this.x, this.y);
112 | // }
113 |
114 | this.x = e.x;
115 | this.y = e.y;
116 | this.delta.x = e.x - this.last.x;
117 | this.delta.y = e.y - this.last.y;
118 |
119 | this.distance += this.delta.length();
120 |
121 | this.normalized.x = ((this.x / viewport.width) * 2) - 1;
122 | this.normalized.y = (-(this.y / viewport.height) * 2) + 1;
123 |
124 | this._tmp.x = this.normalized.x;
125 | this._tmp.y = this.normalized.y;
126 | this._tmp.z = 0.5;
127 | this._tmp.unproject(camera);
128 | const dir = this._tmp.sub(camera.position).normalize();
129 | const dist = -camera.position.z / dir.z;
130 | this.world.copy(camera.position).add(dir.multiplyScalar(dist));
131 |
132 | bidello.trigger({ name: 'pointerMove' }, {
133 | pointer: this,
134 | });
135 |
136 | if (this.isTouching) {
137 | bidello.trigger({ name: 'pointerDrag' }, {
138 | pointer: this,
139 | });
140 | }
141 | }
142 |
143 | onEnd() {
144 | this.isTouching = false;
145 | this.move.set(0, 0);
146 |
147 | bidello.trigger({ name: 'pointerEnd' }, {
148 | pointer: this,
149 | });
150 | }
151 | }
152 |
153 | export default new Pointer();
--------------------------------------------------------------------------------