├── screenshot.png
├── README.md
└── Cornell box
├── shaders
├── material.js
├── position.js
├── normals.js
├── display.js
├── momentMove.js
├── radianceAccum.js
├── history.js
├── atrous.js
└── radiance.js
├── index.html
├── dependencies
├── HDRCubeTextureLoader.js
├── utils.js
├── RGBELoader.js
├── DRACOLoader.js
└── orbitControls.js
└── main.js
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Domenicobrz/Realtime-path-tracing-in-chrome/HEAD/screenshot.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Realtime-path-tracing-in-chrome
2 | A (bad) attempt at implementing wavelet filtering and temporal reprojection in threejs
3 |
4 | [Live demo here](https://domenicobrz.github.io/webgl/projects/RTPTCornellBox/)
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Cornell box/shaders/material.js:
--------------------------------------------------------------------------------
1 | let material_vs = `
2 | attribute vec4 aMaterial;
3 |
4 | varying vec4 vMaterial;
5 |
6 | void main() {
7 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
8 |
9 | vMaterial = aMaterial;
10 | }
11 | `;
12 |
13 | let material_fs = `
14 | varying vec4 vMaterial;
15 |
16 | void main() {
17 | gl_FragColor = vMaterial;
18 | }
19 | `;
20 |
21 | export { material_fs, material_vs };
--------------------------------------------------------------------------------
/Cornell box/shaders/position.js:
--------------------------------------------------------------------------------
1 | let position_vs = `
2 | varying vec3 vWorldSpacePosition;
3 |
4 | void main() {
5 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
6 | vWorldSpacePosition = position;
7 | }
8 | `;
9 |
10 | let position_fs = `
11 | varying vec3 vWorldSpacePosition;
12 |
13 | void main() {
14 | gl_FragColor = vec4(vWorldSpacePosition, 1.0);
15 | }
16 | `;
17 |
18 | export { position_fs, position_vs };
--------------------------------------------------------------------------------
/Cornell box/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Cornell box/shaders/normals.js:
--------------------------------------------------------------------------------
1 | let normal_vs = `
2 | varying vec3 vWorldSpaceNormal;
3 | varying vec3 vWorldSpacePosition;
4 |
5 | void main() {
6 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
7 | vWorldSpaceNormal = normal;
8 | vWorldSpacePosition = position;
9 | }
10 | `;
11 |
12 | let normal_fs = `
13 | varying vec3 vWorldSpaceNormal;
14 | varying vec3 vWorldSpacePosition;
15 |
16 | uniform vec3 uCameraPos;
17 |
18 | void main() {
19 | // vec3 viewDir = normalize(vWorldSpacePosition - uCameraPos);
20 |
21 | vec3 normal = normalize(vWorldSpaceNormal);
22 | // if(dot(viewDir, normal) > 0.0) normal = -normal;
23 |
24 | gl_FragColor = vec4(normal, 1.0);
25 | }
26 | `;
27 |
28 | export { normal_fs, normal_vs };
--------------------------------------------------------------------------------
/Cornell box/shaders/display.js:
--------------------------------------------------------------------------------
1 | let display_vs = `
2 | varying vec2 vUv;
3 |
4 | void main() {
5 | gl_Position = vec4(position, 1.0);
6 | vUv = uv;
7 | }
8 | `;
9 |
10 | let display_fs = `
11 | varying vec2 vUv;
12 |
13 | uniform sampler2D uTexture;
14 |
15 | vec3 acesFilm(const vec3 x) {
16 | const float a = 2.51;
17 | const float b = 0.03;
18 | const float c = 2.43;
19 | const float d = 0.59;
20 | const float e = 0.14;
21 | return clamp((x * (a * x + b)) / (x * (c * x + d ) + e), 0.0, 1.0);
22 | }
23 |
24 | void main() {
25 | vec3 color = texture2D(uTexture, vUv).xyz;
26 | color *= pow(2.0, -1.0);
27 | vec3 mapped = acesFilm(color);
28 |
29 | // // gamma correction
30 | // mapped = pow(mapped, 1.0 / 2.2);
31 |
32 |
33 |
34 |
35 | // float exposure = 0.1;
36 | // vec3 mapped = color;
37 | // mapped *= pow(2.0, exposure);
38 |
39 | // // -- filmic correction
40 | // mapped *= 0.6;
41 | // mapped = ((mapped * mapped) * 2.51 + mapped * 0.03) / (mapped * mapped * 2.43 + mapped * 0.59 + 0.14);
42 |
43 | // -- gamma correction + clamp
44 | mapped = pow(mapped, vec3(1.0 / 2.2));
45 | mapped = clamp(mapped, 0.0, 1.0);
46 |
47 |
48 |
49 |
50 | gl_FragColor = vec4(mapped, 1.0);
51 | }
52 | `;
53 |
54 | export { display_fs, display_vs };
--------------------------------------------------------------------------------
/Cornell box/shaders/momentMove.js:
--------------------------------------------------------------------------------
1 | let momentMove_vs = `
2 | attribute vec3 oldPosition;
3 |
4 |
5 | varying vec3 vFragPos;
6 | varying vec3 vOldFragPos;
7 |
8 | varying mat4 modelViewMat;
9 | varying mat4 vProjectionMatrix;
10 |
11 | // varying vec2 vOldNDCPos;
12 |
13 | void main() {
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
15 |
16 | vFragPos = position;
17 | vOldFragPos = oldPosition;
18 |
19 | modelViewMat = modelViewMatrix;
20 | vProjectionMatrix = projectionMatrix;
21 | }
22 | `;
23 |
24 | let momentMove_fs = `
25 | varying vec3 vFragPos;
26 | varying vec3 vOldFragPos;
27 |
28 | uniform mat4 uOldModelViewMatrix;
29 |
30 | varying mat4 modelViewMat;
31 | varying mat4 vProjectionMatrix;
32 |
33 | // varying vec2 vOldNDCPos;
34 |
35 | void main() {
36 |
37 | vec4 ndcOldPos = vProjectionMatrix * uOldModelViewMatrix * vec4(vOldFragPos, 1.0);
38 | vec4 ndcNewPos = vProjectionMatrix * modelViewMat * vec4(vFragPos, 1.0);
39 |
40 | ndcOldPos.xyzw /= ndcOldPos.w;
41 | ndcNewPos.xyzw /= ndcNewPos.w;
42 |
43 | ndcOldPos.xy = ndcOldPos.xy * 0.5 + 0.5;
44 | ndcNewPos.xy = ndcNewPos.xy * 0.5 + 0.5;
45 |
46 | gl_FragColor = vec4(ndcOldPos.xy - ndcNewPos.xy, 0.0, 1.0);
47 | // gl_FragColor = vec4(normalize(ndcOldPos.xy - ndcNewPos.xy) * 0.5 + 0.5, 0.0, 1.0);
48 | // gl_FragColor = vec4(ndcOldPos.xy, 0.0, 1.0);
49 | }
50 | `;
51 |
52 | export { momentMove_fs, momentMove_vs };
53 |
--------------------------------------------------------------------------------
/Cornell box/shaders/radianceAccum.js:
--------------------------------------------------------------------------------
1 | let radianceAccum_vs = `
2 | varying vec2 vUv;
3 |
4 | void main() {
5 | gl_Position = vec4(position, 1.0);
6 | vUv = uv;
7 | }
8 | `;
9 |
10 | let radianceAccum_fs = `
11 | varying vec2 vUv;
12 |
13 | uniform sampler2D uCurrentRadiance;
14 | uniform sampler2D uAccumulatedRadiance;
15 | uniform sampler2D uHistoryBuffer;
16 | uniform sampler2D uMomentMoveBuffer;
17 |
18 | uniform float uMaxFramesHistory;
19 |
20 | void main() {
21 |
22 | vec2 reprojUVOffs = texture2D(uMomentMoveBuffer, vUv).xy;
23 |
24 | vec3 newRad = texture2D(uCurrentRadiance, vUv).xyz;
25 | vec3 accumulatedRad = texture2D(uAccumulatedRadiance, vUv + reprojUVOffs).xyz;
26 |
27 |
28 | float maxFrames = uMaxFramesHistory;
29 | float history = min(texture2D(uHistoryBuffer, vUv).x, maxFrames);
30 |
31 | // float lambda = ((maxFrames - history) / maxFrames) * 0.95 + 0.05;
32 | // REMEMBER: this is an exponential average, and apparently the perceived variance
33 | // WILL be lower if we accumulate more than $maxFrames samples (the variance reduction
34 | // stops at around 2 * $maxFrames samples, that's why the atrous filter multiplies
35 | // the clamped history by 0.5
36 | float lambda = (((maxFrames+1.0) - history) / (maxFrames+1.0));
37 |
38 |
39 | // float material = texture2D(uMaterialBuffer, vUv).w;
40 | // lambda = material == 3.0 ? 0.1 : lambda;
41 |
42 | vec3 updatedAccum = newRad * lambda + accumulatedRad * (1.0 - lambda);
43 |
44 | gl_FragColor = vec4(updatedAccum, 1.0);
45 | }
46 | `;
47 |
48 | export { radianceAccum_fs, radianceAccum_vs};
--------------------------------------------------------------------------------
/Cornell box/shaders/history.js:
--------------------------------------------------------------------------------
1 | let historyTest_vs = `
2 | varying mat4 vProjectionViewMatrix;
3 | varying vec3 vFragPos;
4 | varying vec3 vNormal;
5 |
6 | uniform vec3 uCameraPos;
7 |
8 | void main() {
9 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
10 |
11 | vProjectionViewMatrix = projectionMatrix * modelViewMatrix;
12 | vFragPos = position;
13 | vNormal = normal;
14 |
15 | // vec3 viewDir = normalize(vFragPos - uCameraPos);
16 | // if(dot(viewDir, normal) > 0.0) {
17 | // vNormal = -vNormal;
18 | // }
19 | }
20 | `;
21 |
22 | let historyTest_fs = `
23 | varying mat4 vProjectionViewMatrix;
24 | varying vec3 vFragPos;
25 | varying vec3 vNormal;
26 |
27 | uniform sampler2D uNormalBuffer;
28 | uniform sampler2D uPositionBuffer;
29 | uniform sampler2D uMomentMove;
30 | uniform vec2 uInvScreenSize;
31 |
32 | void main() {
33 | vec4 projected = vProjectionViewMatrix * vec4(vFragPos, 1.0);
34 | projected /= projected.w;
35 | vec2 uv = projected.xy * 0.5 + 0.5;
36 |
37 |
38 | // vec2 testOffsets[5];
39 | // testOffsets[0] = vec2(0.0, 0.0);
40 | // testOffsets[1] = vec2(0.0, -1.0 * uInvScreenSize.y);
41 | // testOffsets[2] = vec2(0.0, 1.0 * uInvScreenSize.y);
42 | // testOffsets[3] = vec2(-1.0 * uInvScreenSize.x, 0.0);
43 | // testOffsets[4] = vec2(1.0 * uInvScreenSize.x, 0.0);
44 |
45 | // vec3 success = vec3(1.0);
46 | // vec2 olduv = uv + texture2D(uMomentMove, uv).xy;
47 | // for(int i = 0; i < 5; i++) {
48 | // // reprojection test
49 | // vec3 oldNormal = texture2D(uNormalBuffer, olduv + testOffsets[i]).xyz;
50 | // vec3 oldPosition = texture2D(uPositionBuffer, olduv + testOffsets[i]).xyz;
51 | // vec3 normal = normalize(vNormal);
52 | // if(dot(oldNormal, normal) < 0.9) {
53 | // success = vec3(0.0);
54 | // break;
55 | // }
56 |
57 | // // if(length(oldPosition - vFragPos) > 0.1) success = vec3(0.0);
58 | // }
59 |
60 |
61 |
62 | vec3 success = vec3(1.0);
63 |
64 | // reprojection test
65 | vec2 olduv = uv + texture2D(uMomentMove, uv).xy;
66 | vec3 oldNormal = texture2D(uNormalBuffer, olduv).xyz;
67 | vec3 oldPosition = texture2D(uPositionBuffer, olduv).xyz;
68 |
69 | vec3 normal = normalize(vNormal);
70 |
71 |
72 | if(dot(oldNormal, normal) < 0.9) success = vec3(0.0);
73 | if(length(oldPosition - vFragPos) > 0.25) success = vec3(0.0);
74 |
75 | gl_FragColor = vec4(success, 1.0);
76 | }
77 | `;
78 |
79 |
80 |
81 |
82 |
83 |
84 | let historyAccum_vs = `
85 | varying vec2 vUv;
86 |
87 | void main() {
88 | gl_Position = vec4(position, 1.0);
89 | vUv = uv;
90 | }
91 | `;
92 |
93 | let historyAccum_fs = `
94 | varying vec2 vUv;
95 |
96 | uniform sampler2D uHistoryTest;
97 | uniform sampler2D uHistoryAccum;
98 | uniform sampler2D uMomentMove;
99 |
100 | void main() {
101 |
102 | // quesito interessante.. l'accumulazione va fatta basandosi
103 | // sul valore accumulato del pixel reproiettato o su quello corrente?
104 | vec2 olduv = vUv + texture2D(uMomentMove, vUv).xy;
105 |
106 | float lastTestResult = texture2D(uHistoryTest, vUv).x;
107 | float accum = texture2D(uHistoryAccum, olduv).x;
108 |
109 | float updatedAccum = lastTestResult < 0.5 ? 0.0 : accum + 1.0;
110 | gl_FragColor = vec4(vec3(updatedAccum), 1.0);
111 | }
112 | `;
113 |
114 |
115 | export { historyTest_fs, historyTest_vs, historyAccum_vs, historyAccum_fs };
--------------------------------------------------------------------------------
/Cornell box/dependencies/HDRCubeTextureLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Prashant Sharma / spidersharma03
3 | * @author Ben Houston / http://clara.io / bhouston
4 | */
5 |
6 | import {
7 | CubeTexture,
8 | DataTexture,
9 | FileLoader,
10 | FloatType,
11 | HalfFloatType,
12 | LinearEncoding,
13 | LinearFilter,
14 | Loader,
15 | NearestFilter,
16 | RGBAFormat,
17 | RGBEEncoding,
18 | RGBFormat,
19 | UnsignedByteType
20 | } from "./three.module.js";
21 | import { RGBELoader } from "./RGBELoader.js";
22 |
23 | var HDRCubeTextureLoader = function ( manager ) {
24 |
25 | Loader.call( this, manager );
26 |
27 | this.hdrLoader = new RGBELoader();
28 | this.type = UnsignedByteType;
29 |
30 | };
31 |
32 | HDRCubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
33 |
34 | constructor: HDRCubeTextureLoader,
35 |
36 | load: function ( urls, onLoad, onProgress, onError ) {
37 |
38 | if ( ! Array.isArray( urls ) ) {
39 |
40 | console.warn( 'THREE.HDRCubeTextureLoader signature has changed. Use .setDataType() instead.' );
41 |
42 | this.setDataType( urls );
43 |
44 | urls = onLoad;
45 | onLoad = onProgress;
46 | onProgress = onError;
47 | onError = arguments[ 4 ];
48 |
49 | }
50 |
51 | var texture = new CubeTexture();
52 |
53 | texture.type = this.type;
54 |
55 | switch ( texture.type ) {
56 |
57 | case UnsignedByteType:
58 |
59 | texture.encoding = RGBEEncoding;
60 | texture.format = RGBAFormat;
61 | texture.minFilter = NearestFilter;
62 | texture.magFilter = NearestFilter;
63 | texture.generateMipmaps = false;
64 | break;
65 |
66 | case FloatType:
67 |
68 | texture.encoding = LinearEncoding;
69 | texture.format = RGBFormat;
70 | texture.minFilter = LinearFilter;
71 | texture.magFilter = LinearFilter;
72 | texture.generateMipmaps = false;
73 | break;
74 |
75 | case HalfFloatType:
76 |
77 | texture.encoding = LinearEncoding;
78 | texture.format = RGBFormat;
79 | texture.minFilter = LinearFilter;
80 | texture.magFilter = LinearFilter;
81 | texture.generateMipmaps = false;
82 | break;
83 |
84 | }
85 |
86 | var scope = this;
87 |
88 | var loaded = 0;
89 |
90 | function loadHDRData( i, onLoad, onProgress, onError ) {
91 |
92 | new FileLoader( scope.manager )
93 | .setPath( scope.path )
94 | .setResponseType( 'arraybuffer' )
95 | .load( urls[ i ], function ( buffer ) {
96 |
97 | loaded ++;
98 |
99 | var texData = scope.hdrLoader.parse( buffer );
100 |
101 | if ( ! texData ) return;
102 |
103 | if ( texData.data !== undefined ) {
104 |
105 | var dataTexture = new DataTexture( texData.data, texData.width, texData.height );
106 |
107 | dataTexture.type = texture.type;
108 | dataTexture.encoding = texture.encoding;
109 | dataTexture.format = texture.format;
110 | dataTexture.minFilter = texture.minFilter;
111 | dataTexture.magFilter = texture.magFilter;
112 | dataTexture.generateMipmaps = texture.generateMipmaps;
113 |
114 | texture.images[ i ] = dataTexture;
115 |
116 | }
117 |
118 | if ( loaded === 6 ) {
119 |
120 | texture.needsUpdate = true;
121 | if ( onLoad ) onLoad( texture );
122 |
123 | }
124 |
125 | }, onProgress, onError );
126 |
127 | }
128 |
129 | for ( var i = 0; i < urls.length; i ++ ) {
130 |
131 | loadHDRData( i, onLoad, onProgress, onError );
132 |
133 | }
134 |
135 | return texture;
136 |
137 | },
138 |
139 | setDataType: function ( value ) {
140 |
141 | this.type = value;
142 | this.hdrLoader.setDataType( value );
143 |
144 | return this;
145 |
146 | }
147 |
148 | } );
149 |
150 | export { HDRCubeTextureLoader };
151 |
--------------------------------------------------------------------------------
/Cornell box/dependencies/utils.js:
--------------------------------------------------------------------------------
1 | import * as THREE from "./three.module.js";
2 |
3 | let Utils = { }
4 |
5 | Utils.smoothstep = function(t) {
6 | return t * t * (3 - 2 * t);
7 | }
8 |
9 | let onceMemory = { }
10 | Utils.once = function(tag) {
11 | if(!onceMemory[tag]) {
12 | onceMemory[tag] = true;
13 | return true;
14 | }
15 |
16 | return false;
17 | }
18 |
19 | Utils.parseIncludes = function( string ) {
20 | var utils_includepattern = /#include <(.*)>/gm;
21 |
22 | function replace( match , include ) {
23 | var replace = THREE.ShaderChunk[ include ];
24 | return Utils.parseIncludes( replace );
25 | }
26 |
27 | return string.replace( utils_includepattern, replace );
28 | }
29 |
30 | Utils.last = function(array) {
31 | return array[array.length - 1];
32 | }
33 |
34 | Utils.vec3Equal = function(a, b) {
35 | return a.x === b.x && a.y === b.y && a.z === b.z;
36 | }
37 |
38 | Utils.easings = {
39 | // no easing, no acceleration
40 | linear: function (t) { return t },
41 | // accelerating from zero velocity
42 | easeInQuad: function (t) { return t*t },
43 | // decelerating to zero velocity
44 | easeOutQuad: function (t) { return t*(2-t) },
45 | // acceleration until halfway, then deceleration
46 | easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t },
47 | // accelerating from zero velocity
48 | easeInCubic: function (t) { return t*t*t },
49 | // decelerating to zero velocity
50 | easeOutCubic: function (t) { return (--t)*t*t+1 },
51 | // acceleration until halfway, then deceleration
52 | easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 },
53 | // accelerating from zero velocity
54 | easeInQuart: function (t) { return t*t*t*t },
55 | // decelerating to zero velocity
56 | easeOutQuart: function (t) { return 1-(--t)*t*t*t },
57 | // acceleration until halfway, then deceleration
58 | easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t },
59 | // accelerating from zero velocity
60 | easeInQuint: function (t) { return t*t*t*t*t },
61 | // decelerating to zero velocity
62 | easeOutQuint: function (t) { return 1+(--t)*t*t*t*t },
63 | // acceleration until halfway, then deceleration
64 | easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t }
65 | }
66 |
67 | Utils.toCamelCase = function(str, splitter) {
68 | if(!splitter) splitter = " ";
69 |
70 | return str.split(splitter).map(function(word,index) {
71 | // If it is the first word make sure to lowercase all the chars.
72 | if(index == 0){
73 | return word.toLowerCase();
74 | }
75 | // If it is not the first word only upper case the first char and lowercase the rest.
76 | return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
77 | }).join('');
78 | }
79 |
80 | Utils.removeAllChildren = function(element) {
81 | while (element.firstChild) {
82 | element.removeChild(element.firstChild);
83 | }
84 | }
85 |
86 | Utils.setAllAsUnactive = function(array) {
87 | for(let i = 0; i < array.length; i++) {
88 | let element = array[i];
89 | element.classList.remove("active");
90 | }
91 | }
92 |
93 | Utils.inchesToHeightString = function(value) {
94 | let feet = Math.floor(value / 12);
95 | let inches = value % 12;
96 |
97 | return "" + feet + "' " + inches + "''";
98 | }
99 |
100 | Utils.getJSON = function(file, callback) {
101 | var rawFile = new XMLHttpRequest();
102 | rawFile.overrideMimeType("application/json");
103 | rawFile.open("GET", file, true);
104 | rawFile.onreadystatechange = function() {
105 | if (rawFile.readyState === 4 && rawFile.status == "200") {
106 | var jsonData = JSON.parse(rawFile.responseText);
107 | callback(jsonData);
108 | }
109 | };
110 | rawFile.send(null);
111 | }
112 |
113 | export default Utils;
--------------------------------------------------------------------------------
/Cornell box/shaders/atrous.js:
--------------------------------------------------------------------------------
1 | let atrous_vs = `
2 | varying vec2 vUv;
3 |
4 | void main() {
5 | gl_Position = vec4(position, 1.0);
6 | vUv = uv;
7 | }
8 | `;
9 |
10 | let atrous_fs = `varying vec2 vUv;
11 |
12 | uniform sampler2D uRadiance;
13 | uniform sampler2D uPosition;
14 | uniform sampler2D uNormal;
15 | uniform sampler2D uMaterial;
16 | uniform sampler2D uHistoryAccum;
17 | uniform float uFilterHistoryModulation;
18 | uniform float uMaxFramesHistory;
19 | uniform float uStep;
20 | uniform vec2 uScreenSize;
21 | uniform float uC_phi;
22 | uniform float uN_phi;
23 | uniform float uP_phi;
24 |
25 |
26 | void main() {
27 |
28 | #ifdef atrous5x5
29 | float kernel[25];
30 | kernel[20] = 0.00390625;
31 | kernel[21] = 0.015625;
32 | kernel[22] = 0.0234375;
33 | kernel[23] = 0.015625;
34 | kernel[24] = 0.00390625;
35 | kernel[15] = 0.015625; kernel[16] = 0.0625; kernel[17] = 0.09375; kernel[18] = 0.0625; kernel[19] = 0.015625;
36 | kernel[10] = 0.0234375; kernel[11] = 0.09375; kernel[12] = 0.140625; kernel[13] = 0.09375; kernel[14] = 0.0234375;
37 | kernel[5] = 0.015625; kernel[6] = 0.0625; kernel[7] = 0.09375; kernel[8] = 0.0625; kernel[9] = 0.015625;
38 | kernel[0] = 0.00390625; kernel[1] = 0.015625; kernel[2] = 0.0234375; kernel[3] = 0.015625; kernel[4] = 0.00390625;
39 |
40 | vec2 offs[25];
41 | offs[20] = vec2(-2.0, +2.0); offs[21] = vec2(-1.0, +2.0); offs[22] = vec2(+0.0, +2.0); offs[23] = vec2(+1.0, +2.0); offs[24] = vec2(+2.0, +2.0);
42 | offs[15] = vec2(-2.0, +1.0); offs[16] = vec2(-1.0, +1.0); offs[17] = vec2(+0.0, +1.0); offs[18] = vec2(+1.0, +1.0); offs[19] = vec2(+2.0, +1.0);
43 | offs[10] = vec2(-2.0, +0.0); offs[11] = vec2(-1.0, +0.0); offs[12] = vec2(+0.0, +0.0); offs[13] = vec2(+1.0, +0.0); offs[14] = vec2(+2.0, +0.0);
44 | offs[5] = vec2(-2.0, -1.0); offs[6] = vec2(-1.0, -1.0); offs[7] = vec2(+0.0, -1.0); offs[8] = vec2(+1.0, -1.0); offs[9] = vec2(+2.0, -1.0);
45 | offs[0] = vec2(-2.0, -2.0); offs[1] = vec2(-1.0, -2.0); offs[2] = vec2(+0.0, -2.0); offs[3] = vec2(+1.0, -2.0); offs[4] = vec2(+2.0, -2.0);
46 | const int loopSteps = 25;
47 | #endif
48 |
49 |
50 | #ifdef atrous3x3
51 | float kernel[9];
52 | kernel[6] = 0.0625; kernel[7] = 0.125; kernel[8] = 0.0625;
53 | kernel[3] = 0.125; kernel[4] = 0.25; kernel[5] = 0.125;
54 | kernel[0] = 0.0625; kernel[1] = 0.125; kernel[2] = 0.0625;
55 |
56 | vec2 offs[9];
57 | offs[6] = vec2(-1.0, +1.0); offs[7] = vec2(+0.0, +1.0); offs[8] = vec2(+1.0, +1.0);
58 | offs[3] = vec2(-1.0, +0.0); offs[4] = vec2(+0.0, +0.0); offs[5] = vec2(+1.0, +0.0);
59 | offs[0] = vec2(-1.0, -1.0); offs[1] = vec2(+0.0, -1.0); offs[2] = vec2(+1.0, -1.0);
60 | const int loopSteps = 9;
61 | #endif
62 |
63 |
64 | float c_phi = uC_phi;
65 | float n_phi = uN_phi;
66 | float p_phi = uP_phi;
67 | float stepwidth = uStep;
68 |
69 | vec4 sum = vec4(0.0);
70 | vec2 step = vec2(1./uScreenSize.x, 1./uScreenSize.y);
71 | vec2 hstep = step * 0.0;
72 | vec4 cval = texture2D(uRadiance, vUv.st + hstep);
73 | vec4 nval = texture2D(uNormal, vUv.st + hstep);
74 | vec4 pval = texture2D(uPosition, vUv.st + hstep);
75 |
76 |
77 | float history = texture2D(uHistoryAccum, vUv.st + hstep).x;
78 | // here I'm multiplying the history by 0.5 because the
79 | // perceived variance in the samples that are exactly at maxFrameHistory
80 | // is almost 2x lower than the samples that are at maxFrameHistory * 2
81 | // in practice: if some fragments have been accumulated 40 times each
82 | // (with maxframehis. == 20) they will show a much reduced variance
83 | // compared to a set of fragments that have been accumulated for 20 frames.
84 | // So essentially when a frame is at "uMaxFramesHistory * 2", it will have the same
85 | // perceived variance of the frames that have accumulated much
86 | // longer than $uMaxFramesHistory
87 | float clampedHistory = min(history * 0.5, uMaxFramesHistory);
88 | stepwidth *= 1.0 - (1.0 - (uMaxFramesHistory - clampedHistory) / uMaxFramesHistory) * uFilterHistoryModulation;
89 | // stepwidth *= history >= uMaxFramesHistory * 4.0 ? 0.0 : 1.0;
90 |
91 | // // **************** mirror-like materials
92 | // vec4 material = texture2D(uMaterial, vUv.st + hstep);
93 | // if(material.w == 3.0) stepwidth *= 0.75;
94 | // // **************** mirror-like materials
95 |
96 |
97 | float cum_w = 0.0;
98 | for(int i = 0; i < loopSteps; i++) {
99 | vec2 uv = vUv.st + hstep + offs[i] * step * stepwidth;
100 |
101 | vec4 ctmp = texture2D(uRadiance, uv);
102 | vec4 t = cval - ctmp;
103 | float dist2 = dot(t,t);
104 | float c_w = min(exp(-(dist2)/c_phi), 1.0);
105 |
106 | vec4 ntmp = texture2D(uNormal, uv);
107 | t = nval - ntmp;
108 | dist2 = max(dot(t,t)/(stepwidth*stepwidth),0.0);
109 | float n_w = min(exp(-(dist2)/n_phi), 1.0);
110 |
111 | vec4 ptmp = texture2D(uPosition, uv);
112 | t = pval - ptmp;
113 | dist2 = dot(t,t);
114 | float p_w = min(exp(-(dist2)/p_phi), 1.0);
115 |
116 |
117 | float weight = c_w * n_w * p_w;
118 | sum += ctmp * weight * kernel[i];
119 |
120 | cum_w += weight * kernel[i];
121 | }
122 |
123 | vec4 color = sum / cum_w;
124 |
125 | gl_FragColor = color;
126 | }
127 | `;
128 |
129 | export { atrous_fs, atrous_vs };
--------------------------------------------------------------------------------
/Cornell box/shaders/radiance.js:
--------------------------------------------------------------------------------
1 | function makeSceneShaders(tot_triangles) {
2 |
3 | window.radiance_vs = `
4 | varying vec2 vUv;
5 |
6 | void main() {
7 | gl_Position = vec4(position, 1.0);
8 |
9 | vUv = uv;
10 | }
11 | `;
12 |
13 | window.radiance_fs = `
14 | uniform vec4 uScene[${tot_triangles * 3}];
15 | // uniform vec3 uScene[6];
16 |
17 | uniform vec3 uCameraPos;
18 | uniform vec3 uCameraTarget;
19 | uniform float uAspectRatio;
20 | uniform float uRadMult;
21 | uniform float uTime;
22 | uniform float uMirrorIndex;
23 | uniform vec4 uRandom;
24 |
25 | uniform sampler2D uPositionBuffer;
26 |
27 | varying vec2 vUv;
28 |
29 | struct intersectionResult
30 | {
31 | vec3 n;
32 | float t;
33 | bool hit;
34 | float meshIndex;
35 | };
36 |
37 |
38 | #define PI 3.14159265359
39 |
40 |
41 | bool rayTriangleIntersect(vec3 O, vec3 D, vec3 A, vec3 B, vec3 C, inout float t, inout vec3 normal) {
42 | // Ray-triangle isect:
43 | vec3 E1=B-A; vec3 E2=C-A;
44 |
45 | vec3 N=cross(E1,E2);
46 | normal = normalize(N);
47 |
48 | float det = -dot(D,N);
49 | vec3 AO = O - A;
50 | vec3 DAO = cross(AO,D);
51 |
52 | float u = dot(E2,DAO)/det;
53 | float v = -dot(E1,DAO)/det;
54 | t = dot(AO,N)/det;
55 |
56 | return (
57 | t > 0.0 && u > 0.0 && v > 0.0 && (u+v) < 1.0
58 | );
59 | }
60 |
61 | // bool rayTriangleIntersect(vec3 ro, vec3 rd, vec3 v0, vec3 v1, vec3 v2, inout float t) {
62 | // float kEpsilon = 0.0001;
63 |
64 | // // return t;
65 | // vec3 v0v1 = v1 - v0;
66 | // vec3 v0v2 = v2 - v0;
67 | // vec3 pvec = cross(rd, v0v2);
68 | // float det = dot(v0v1, pvec);
69 |
70 | // if (abs(det) < kEpsilon) return false;
71 |
72 | // float invDet = 1.0 / det;
73 |
74 | // vec3 tvec = ro - v0;
75 | // float u = dot(tvec, pvec) * invDet;
76 | // if (u < 0.0 || u > 1.0) return false;
77 |
78 | // vec3 qvec = cross(tvec, v0v1);
79 | // float v = dot(rd, qvec) * invDet;
80 | // if (v < 0.0 || u + v > 1.0) return false;
81 |
82 | // t = dot(v0v2, qvec) * invDet;
83 |
84 | // return true;
85 | // }
86 |
87 |
88 | intersectionResult scene(vec3 ro, vec3 rd) {
89 | // vec3 triangles[6];
90 | // triangles[0] = vec3(-1.0, 0.0, 0.0);
91 | // triangles[1] = vec3(+1.0, 0.0, 0.0);
92 | // triangles[2] = vec3( 0.0, 1.7, 0.0);
93 |
94 | // triangles[3] = vec3(-17.0, 0.0, 8.0);
95 | // triangles[4] = vec3(+17.0, 0.0, 8.0);
96 | // triangles[5] = vec3( 0.0, 0.0, -13.0);
97 |
98 |
99 |
100 | vec3 normal = vec3(0.0);
101 | float mint = 9999999999.0;
102 | bool hit = false;
103 | float meshIndex = 0.0;
104 |
105 | // for(int i = 0; i < 2; i++) {
106 | for(int i = 0; i < ${tot_triangles}; i++) {
107 | vec3 v0 = uScene[i*3 + 0].xyz;
108 | vec3 v1 = uScene[i*3 + 1].xyz;
109 | vec3 v2 = uScene[i*3 + 2].xyz;
110 |
111 | vec3 n = vec3(0.0);
112 | float t = 999999999.0;
113 |
114 | if(rayTriangleIntersect(ro, rd, v0, v1, v2, t, n)) {
115 | if(t < mint) {
116 | mint = t;
117 | hit = true;
118 | normal = n;
119 | meshIndex = uScene[i*3 + 0].w;
120 | }
121 | }
122 | }
123 |
124 | if(dot(normal, rd) > 0.0) {
125 | normal = -normal;
126 | }
127 |
128 | return intersectionResult( normal, mint, hit, meshIndex );
129 | }
130 |
131 | // one out, three in
132 | float rand(vec3 p3)
133 | {
134 | p3 = fract(p3 * .1031);
135 | p3 += dot(p3, p3.yzx + 33.33);
136 | return fract((p3.x + p3.y) * p3.z);
137 | }
138 |
139 | // 3 out, 3 in...
140 | vec3 hash33(vec3 p3)
141 | {
142 | p3 = fract(p3 * vec3(.1031, .1030, .0973));
143 | p3 += dot(p3, p3.yxz+33.33);
144 | return fract((p3.xxy + p3.yxx)*p3.zyx);
145 |
146 | }
147 |
148 | vec3 sampleDiffuseHemisphere(vec3 normal, vec3 pos) {
149 | float theta = 2.0 * PI * rand(pos * 100.0 + uRandom.x * 127.0);
150 | float phi = acos(2.0 * rand(pos * 100.0 + uRandom.y * 127.0) - 1.0);
151 |
152 | vec3 unitSphereSample = vec3(
153 | cos(theta) * sin(phi),
154 | sin(theta) * sin(phi),
155 | cos(phi)
156 | );
157 |
158 | vec3 np = pos + normal + unitSphereSample;
159 | return normalize(np - pos);
160 | }
161 |
162 | vec3 sampleGlossyHemisphere(vec3 normal, vec3 pos, vec3 dir) {
163 | vec3 wout = reflect(dir, normal);
164 |
165 | float theta = 2.0 * PI * rand(pos * 100.0 + uRandom.x * 127.0);
166 | float phi = acos(2.0 * rand(pos * 100.0 + uRandom.y * 127.0) - 1.0);
167 |
168 | vec3 unitSphereSample = vec3(
169 | cos(theta) * sin(phi),
170 | sin(theta) * sin(phi),
171 | cos(phi)
172 | );
173 |
174 | vec3 np = pos + wout + unitSphereSample * 0.001;
175 | return normalize(np - pos);
176 | }
177 |
178 | void main() {
179 | vec3 radiance = vec3(0.0);
180 | vec2 ndcuv = (vUv * 2.0 - 1.0) * vec2(uAspectRatio, 1.0);
181 |
182 | // "height normalizer"
183 | float hn = tan(/* IN RADIANTI, NON IN GRADI! */ (PI * 45.0 * 0.5) / 180.0);
184 | ndcuv *= hn;
185 |
186 |
187 | vec3 ro = uCameraPos;
188 |
189 | vec3 w = normalize(uCameraTarget - uCameraPos);
190 | vec3 u = normalize(cross(w, vec3(0.0, 1.0, 0.0)));
191 | vec3 v = normalize(cross(u, w));
192 |
193 | vec3 d = normalize(vec3(ndcuv, 1.0));
194 | vec3 nd = normalize( u * d.x + v * d.y + w * d.z );
195 |
196 | vec3 rd = nd;
197 |
198 |
199 | vec3 posBuff = texture2D(uPositionBuffer, vUv).xyz;
200 | // why posBuff minus rd ?
201 | // remember: the wall's front-faces are culled!
202 | // so the first position that you "see" in the positionBuffer,
203 | // is already far into the scene!
204 | ro = posBuff - rd * 0.01;
205 |
206 | if(posBuff == vec3(0.0)) {
207 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
208 | return;
209 | }
210 |
211 |
212 |
213 |
214 | vec3 mult = vec3(1.0);
215 | for(int i = 0; i < 4; i++) {
216 | intersectionResult ir = scene(ro, rd);
217 |
218 | if(ir.hit) {
219 | // radiance = vec3(ir.n);
220 | vec3 albedo = vec3(1.0);
221 |
222 | ro = ro + rd * (ir.t - 0.001);
223 |
224 | float cos;
225 |
226 | // if(ir.meshIndex > 2.7 && ir.meshIndex < 3.2) {
227 | if(ir.meshIndex > uMirrorIndex - 0.2 && ir.meshIndex < uMirrorIndex + 0.2) {
228 | rd = sampleGlossyHemisphere(ir.n, ro, rd);
229 | // albedo = vec3(0.86, 0.86, 0.86);
230 | albedo = vec3(1.0, 1.0, 1.0);
231 | cos = 1.0;
232 | } else {
233 | rd = sampleDiffuseHemisphere(ir.n, ro);
234 | cos = dot(rd, ir.n);
235 | }
236 |
237 | if(ir.meshIndex > 14.0) {
238 | // radiance += vec3(6.0, 5.5, 4.5) * mult;
239 | radiance += vec3(6.0) * mult;
240 | }
241 | if(ir.meshIndex == 1.0) {
242 | // albedo = vec3(1.0, 0.3, 0.15);
243 | albedo = vec3(1.0, 0.3, 0.15);
244 | albedo = vec3(1.0, 0.15, 0.05);
245 | }
246 | if(ir.meshIndex == 2.0) {
247 | // albedo = vec3(0.15, 1.0, 0.3);
248 | albedo = vec3(0.6, 1.0, 0.15);
249 | albedo = vec3(0.3, 1.0, 0.08);
250 | }
251 |
252 |
253 | mult *= albedo;
254 | // sometimes the dot is negative (no idea why) so we need to make sure and guard this operation with max(...)
255 | mult *= max(cos, 0.0);
256 | }
257 | }
258 |
259 |
260 | gl_FragColor = vec4(radiance * uRadMult, 1.0);
261 | }
262 | `;
263 | }
264 |
265 | export { makeSceneShaders };
--------------------------------------------------------------------------------
/Cornell box/dependencies/RGBELoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Nikos M. / https://github.com/foo123/
3 | */
4 |
5 | import {
6 | DataTextureLoader,
7 | FloatType,
8 | HalfFloatType,
9 | LinearEncoding,
10 | LinearFilter,
11 | NearestFilter,
12 | RGBEEncoding,
13 | RGBEFormat,
14 | RGBFormat,
15 | UnsignedByteType
16 | } from "./three.module.js";
17 |
18 | // https://github.com/mrdoob/three.js/issues/5552
19 | // http://en.wikipedia.org/wiki/RGBE_image_format
20 |
21 | var RGBELoader = function ( manager ) {
22 |
23 | DataTextureLoader.call( this, manager );
24 |
25 | this.type = UnsignedByteType;
26 |
27 | };
28 |
29 | RGBELoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype ), {
30 |
31 | constructor: RGBELoader,
32 |
33 | // adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html
34 |
35 | parse: function ( buffer ) {
36 |
37 | var
38 | /* return codes for rgbe routines */
39 | //RGBE_RETURN_SUCCESS = 0,
40 | RGBE_RETURN_FAILURE = - 1,
41 |
42 | /* default error routine. change this to change error handling */
43 | rgbe_read_error = 1,
44 | rgbe_write_error = 2,
45 | rgbe_format_error = 3,
46 | rgbe_memory_error = 4,
47 | rgbe_error = function ( rgbe_error_code, msg ) {
48 |
49 | switch ( rgbe_error_code ) {
50 |
51 | case rgbe_read_error: console.error( "RGBELoader Read Error: " + ( msg || '' ) );
52 | break;
53 | case rgbe_write_error: console.error( "RGBELoader Write Error: " + ( msg || '' ) );
54 | break;
55 | case rgbe_format_error: console.error( "RGBELoader Bad File Format: " + ( msg || '' ) );
56 | break;
57 | default:
58 | case rgbe_memory_error: console.error( "RGBELoader: Error: " + ( msg || '' ) );
59 |
60 | }
61 | return RGBE_RETURN_FAILURE;
62 |
63 | },
64 |
65 | /* offsets to red, green, and blue components in a data (float) pixel */
66 | //RGBE_DATA_RED = 0,
67 | //RGBE_DATA_GREEN = 1,
68 | //RGBE_DATA_BLUE = 2,
69 |
70 | /* number of floats per pixel, use 4 since stored in rgba image format */
71 | //RGBE_DATA_SIZE = 4,
72 |
73 | /* flags indicating which fields in an rgbe_header_info are valid */
74 | RGBE_VALID_PROGRAMTYPE = 1,
75 | RGBE_VALID_FORMAT = 2,
76 | RGBE_VALID_DIMENSIONS = 4,
77 |
78 | NEWLINE = "\n",
79 |
80 | fgets = function ( buffer, lineLimit, consume ) {
81 |
82 | lineLimit = ! lineLimit ? 1024 : lineLimit;
83 | var p = buffer.pos,
84 | i = - 1, len = 0, s = '', chunkSize = 128,
85 | chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) )
86 | ;
87 | while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) {
88 |
89 | s += chunk; len += chunk.length;
90 | p += chunkSize;
91 | chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
92 |
93 | }
94 |
95 | if ( - 1 < i ) {
96 |
97 | /*for (i=l-1; i>=0; i--) {
98 | byteCode = m.charCodeAt(i);
99 | if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++;
100 | else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2;
101 | if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate
102 | }*/
103 | if ( false !== consume ) buffer.pos += len + i + 1;
104 | return s + chunk.slice( 0, i );
105 |
106 | }
107 | return false;
108 |
109 | },
110 |
111 | /* minimal header reading. modify if you want to parse more information */
112 | RGBE_ReadHeader = function ( buffer ) {
113 |
114 | var line, match,
115 |
116 | // regexes to parse header info fields
117 | magic_token_re = /^#\?(\S+)$/,
118 | gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,
119 | exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,
120 | format_re = /^\s*FORMAT=(\S+)\s*$/,
121 | dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,
122 |
123 | // RGBE format header struct
124 | header = {
125 |
126 | valid: 0, /* indicate which fields are valid */
127 |
128 | string: '', /* the actual header string */
129 |
130 | comments: '', /* comments found in header */
131 |
132 | programtype: 'RGBE', /* listed at beginning of file to identify it after "#?". defaults to "RGBE" */
133 |
134 | format: '', /* RGBE format, default 32-bit_rle_rgbe */
135 |
136 | gamma: 1.0, /* image has already been gamma corrected with given gamma. defaults to 1.0 (no correction) */
137 |
138 | exposure: 1.0, /* a value of 1.0 in an image corresponds to watts/steradian/m^2. defaults to 1.0 */
139 |
140 | width: 0, height: 0 /* image dimensions, width/height */
141 |
142 | };
143 |
144 | if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) {
145 |
146 | return rgbe_error( rgbe_read_error, "no header found" );
147 |
148 | }
149 | /* if you want to require the magic token then uncomment the next line */
150 | if ( ! ( match = line.match( magic_token_re ) ) ) {
151 |
152 | return rgbe_error( rgbe_format_error, "bad initial token" );
153 |
154 | }
155 | header.valid |= RGBE_VALID_PROGRAMTYPE;
156 | header.programtype = match[ 1 ];
157 | header.string += line + "\n";
158 |
159 | while ( true ) {
160 |
161 | line = fgets( buffer );
162 | if ( false === line ) break;
163 | header.string += line + "\n";
164 |
165 | if ( '#' === line.charAt( 0 ) ) {
166 |
167 | header.comments += line + "\n";
168 | continue; // comment line
169 |
170 | }
171 |
172 | if ( match = line.match( gamma_re ) ) {
173 |
174 | header.gamma = parseFloat( match[ 1 ], 10 );
175 |
176 | }
177 | if ( match = line.match( exposure_re ) ) {
178 |
179 | header.exposure = parseFloat( match[ 1 ], 10 );
180 |
181 | }
182 | if ( match = line.match( format_re ) ) {
183 |
184 | header.valid |= RGBE_VALID_FORMAT;
185 | header.format = match[ 1 ];//'32-bit_rle_rgbe';
186 |
187 | }
188 | if ( match = line.match( dimensions_re ) ) {
189 |
190 | header.valid |= RGBE_VALID_DIMENSIONS;
191 | header.height = parseInt( match[ 1 ], 10 );
192 | header.width = parseInt( match[ 2 ], 10 );
193 |
194 | }
195 |
196 | if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break;
197 |
198 | }
199 |
200 | if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) {
201 |
202 | return rgbe_error( rgbe_format_error, "missing format specifier" );
203 |
204 | }
205 | if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) {
206 |
207 | return rgbe_error( rgbe_format_error, "missing image size specifier" );
208 |
209 | }
210 |
211 | return header;
212 |
213 | },
214 |
215 | RGBE_ReadPixels_RLE = function ( buffer, w, h ) {
216 |
217 | var data_rgba, offset, pos, count, byteValue,
218 | scanline_buffer, ptr, ptr_end, i, l, off, isEncodedRun,
219 | scanline_width = w, num_scanlines = h, rgbeStart
220 | ;
221 |
222 | if (
223 | // run length encoding is not allowed so read flat
224 | ( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) ||
225 | // this file is not run length encoded
226 | ( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) )
227 | ) {
228 |
229 | // return the flat buffer
230 | return new Uint8Array( buffer );
231 |
232 | }
233 |
234 | if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) {
235 |
236 | return rgbe_error( rgbe_format_error, "wrong scanline width" );
237 |
238 | }
239 |
240 | data_rgba = new Uint8Array( 4 * w * h );
241 |
242 | if ( ! data_rgba || ! data_rgba.length ) {
243 |
244 | return rgbe_error( rgbe_memory_error, "unable to allocate buffer space" );
245 |
246 | }
247 |
248 | offset = 0; pos = 0; ptr_end = 4 * scanline_width;
249 | rgbeStart = new Uint8Array( 4 );
250 | scanline_buffer = new Uint8Array( ptr_end );
251 |
252 | // read in each successive scanline
253 | while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) {
254 |
255 | if ( pos + 4 > buffer.byteLength ) {
256 |
257 | return rgbe_error( rgbe_read_error );
258 |
259 | }
260 |
261 | rgbeStart[ 0 ] = buffer[ pos ++ ];
262 | rgbeStart[ 1 ] = buffer[ pos ++ ];
263 | rgbeStart[ 2 ] = buffer[ pos ++ ];
264 | rgbeStart[ 3 ] = buffer[ pos ++ ];
265 |
266 | if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) {
267 |
268 | return rgbe_error( rgbe_format_error, "bad rgbe scanline format" );
269 |
270 | }
271 |
272 | // read each of the four channels for the scanline into the buffer
273 | // first red, then green, then blue, then exponent
274 | ptr = 0;
275 | while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) {
276 |
277 | count = buffer[ pos ++ ];
278 | isEncodedRun = count > 128;
279 | if ( isEncodedRun ) count -= 128;
280 |
281 | if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) {
282 |
283 | return rgbe_error( rgbe_format_error, "bad scanline data" );
284 |
285 | }
286 |
287 | if ( isEncodedRun ) {
288 |
289 | // a (encoded) run of the same value
290 | byteValue = buffer[ pos ++ ];
291 | for ( i = 0; i < count; i ++ ) {
292 |
293 | scanline_buffer[ ptr ++ ] = byteValue;
294 |
295 | }
296 | //ptr += count;
297 |
298 | } else {
299 |
300 | // a literal-run
301 | scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr );
302 | ptr += count; pos += count;
303 |
304 | }
305 |
306 | }
307 |
308 |
309 | // now convert data from buffer into rgba
310 | // first red, then green, then blue, then exponent (alpha)
311 | l = scanline_width; //scanline_buffer.byteLength;
312 | for ( i = 0; i < l; i ++ ) {
313 |
314 | off = 0;
315 | data_rgba[ offset ] = scanline_buffer[ i + off ];
316 | off += scanline_width; //1;
317 | data_rgba[ offset + 1 ] = scanline_buffer[ i + off ];
318 | off += scanline_width; //1;
319 | data_rgba[ offset + 2 ] = scanline_buffer[ i + off ];
320 | off += scanline_width; //1;
321 | data_rgba[ offset + 3 ] = scanline_buffer[ i + off ];
322 | offset += 4;
323 |
324 | }
325 |
326 | num_scanlines --;
327 |
328 | }
329 |
330 | return data_rgba;
331 |
332 | };
333 |
334 | var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
335 |
336 | var e = sourceArray[ sourceOffset + 3 ];
337 | var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
338 |
339 | destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
340 | destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
341 | destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
342 |
343 | };
344 |
345 | var RGBEByteToRGBHalf = ( function () {
346 |
347 | // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
348 |
349 | var floatView = new Float32Array( 1 );
350 | var int32View = new Int32Array( floatView.buffer );
351 |
352 | /* This method is faster than the OpenEXR implementation (very often
353 | * used, eg. in Ogre), with the additional benefit of rounding, inspired
354 | * by James Tursa?s half-precision code. */
355 | function toHalf( val ) {
356 |
357 | floatView[ 0 ] = val;
358 | var x = int32View[ 0 ];
359 |
360 | var bits = ( x >> 16 ) & 0x8000; /* Get the sign */
361 | var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */
362 | var e = ( x >> 23 ) & 0xff; /* Using int is faster here */
363 |
364 | /* If zero, or denormal, or exponent underflows too much for a denormal
365 | * half, return signed zero. */
366 | if ( e < 103 ) return bits;
367 |
368 | /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
369 | if ( e > 142 ) {
370 |
371 | bits |= 0x7c00;
372 | /* If exponent was 0xff and one mantissa bit was set, it means NaN,
373 | * not Inf, so make sure we set one mantissa bit too. */
374 | bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff );
375 | return bits;
376 |
377 | }
378 |
379 | /* If exponent underflows but not too much, return a denormal */
380 | if ( e < 113 ) {
381 |
382 | m |= 0x0800;
383 | /* Extra rounding may overflow and set mantissa to 0 and exponent
384 | * to 1, which is OK. */
385 | bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 );
386 | return bits;
387 |
388 | }
389 |
390 | bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 );
391 | /* Extra rounding. An overflow will set mantissa to 0 and increment
392 | * the exponent, which is OK. */
393 | bits += m & 1;
394 | return bits;
395 |
396 | }
397 |
398 | return function ( sourceArray, sourceOffset, destArray, destOffset ) {
399 |
400 | var e = sourceArray[ sourceOffset + 3 ];
401 | var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
402 |
403 | destArray[ destOffset + 0 ] = toHalf( sourceArray[ sourceOffset + 0 ] * scale );
404 | destArray[ destOffset + 1 ] = toHalf( sourceArray[ sourceOffset + 1 ] * scale );
405 | destArray[ destOffset + 2 ] = toHalf( sourceArray[ sourceOffset + 2 ] * scale );
406 |
407 | };
408 |
409 | } )();
410 |
411 | var byteArray = new Uint8Array( buffer );
412 | byteArray.pos = 0;
413 | var rgbe_header_info = RGBE_ReadHeader( byteArray );
414 |
415 | if ( RGBE_RETURN_FAILURE !== rgbe_header_info ) {
416 |
417 | var w = rgbe_header_info.width,
418 | h = rgbe_header_info.height,
419 | image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h );
420 |
421 | if ( RGBE_RETURN_FAILURE !== image_rgba_data ) {
422 |
423 | switch ( this.type ) {
424 |
425 | case UnsignedByteType:
426 |
427 | var data = image_rgba_data;
428 | var format = RGBEFormat; // handled as THREE.RGBAFormat in shaders
429 | var type = UnsignedByteType;
430 | break;
431 |
432 | case FloatType:
433 |
434 | var numElements = ( image_rgba_data.length / 4 ) * 3;
435 | var floatArray = new Float32Array( numElements );
436 |
437 | for ( var j = 0; j < numElements; j ++ ) {
438 |
439 | RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 3 );
440 |
441 | }
442 |
443 | var data = floatArray;
444 | var format = RGBFormat;
445 | var type = FloatType;
446 | break;
447 |
448 | case HalfFloatType:
449 |
450 | var numElements = ( image_rgba_data.length / 4 ) * 3;
451 | var halfArray = new Uint16Array( numElements );
452 |
453 | for ( var j = 0; j < numElements; j ++ ) {
454 |
455 | RGBEByteToRGBHalf( image_rgba_data, j * 4, halfArray, j * 3 );
456 |
457 | }
458 |
459 | var data = halfArray;
460 | var format = RGBFormat;
461 | var type = HalfFloatType;
462 | break;
463 |
464 | default:
465 |
466 | console.error( 'THREE.RGBELoader: unsupported type: ', this.type );
467 | break;
468 |
469 | }
470 |
471 | return {
472 | width: w, height: h,
473 | data: data,
474 | header: rgbe_header_info.string,
475 | gamma: rgbe_header_info.gamma,
476 | exposure: rgbe_header_info.exposure,
477 | format: format,
478 | type: type
479 | };
480 |
481 | }
482 |
483 | }
484 |
485 | return null;
486 |
487 | },
488 |
489 | setDataType: function ( value ) {
490 |
491 | this.type = value;
492 | return this;
493 |
494 | },
495 |
496 | load: function ( url, onLoad, onProgress, onError ) {
497 |
498 | function onLoadCallback( texture, texData ) {
499 |
500 | switch ( texture.type ) {
501 |
502 | case UnsignedByteType:
503 |
504 | texture.encoding = RGBEEncoding;
505 | texture.minFilter = NearestFilter;
506 | texture.magFilter = NearestFilter;
507 | texture.generateMipmaps = false;
508 | texture.flipY = true;
509 | break;
510 |
511 | case FloatType:
512 |
513 | texture.encoding = LinearEncoding;
514 | texture.minFilter = LinearFilter;
515 | texture.magFilter = LinearFilter;
516 | texture.generateMipmaps = false;
517 | texture.flipY = true;
518 | break;
519 |
520 | case HalfFloatType:
521 |
522 | texture.encoding = LinearEncoding;
523 | texture.minFilter = LinearFilter;
524 | texture.magFilter = LinearFilter;
525 | texture.generateMipmaps = false;
526 | texture.flipY = true;
527 | break;
528 |
529 | }
530 |
531 | if ( onLoad ) onLoad( texture, texData );
532 |
533 | }
534 |
535 | return DataTextureLoader.prototype.load.call( this, url, onLoadCallback, onProgress, onError );
536 |
537 | }
538 |
539 | } );
540 |
541 | export { RGBELoader };
542 |
--------------------------------------------------------------------------------
/Cornell box/dependencies/DRACOLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Don McCurdy / https://www.donmccurdy.com
3 | */
4 |
5 | import {
6 | BufferAttribute,
7 | BufferGeometry,
8 | FileLoader,
9 | Loader
10 | } from "./three.module.js";
11 |
12 | var DRACOLoader = function ( manager ) {
13 |
14 | Loader.call( this, manager );
15 |
16 | this.decoderPath = '';
17 | this.decoderConfig = {};
18 | this.decoderBinary = null;
19 | this.decoderPending = null;
20 |
21 | this.workerLimit = 4;
22 | this.workerPool = [];
23 | this.workerNextTaskID = 1;
24 | this.workerSourceURL = '';
25 |
26 | this.defaultAttributeIDs = {
27 | position: 'POSITION',
28 | normal: 'NORMAL',
29 | color: 'COLOR',
30 | uv: 'TEX_COORD'
31 | };
32 | this.defaultAttributeTypes = {
33 | position: 'Float32Array',
34 | normal: 'Float32Array',
35 | color: 'Float32Array',
36 | uv: 'Float32Array'
37 | };
38 |
39 | };
40 |
41 | DRACOLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
42 |
43 | constructor: DRACOLoader,
44 |
45 | setDecoderPath: function ( path ) {
46 |
47 | this.decoderPath = path;
48 |
49 | return this;
50 |
51 | },
52 |
53 | setDecoderConfig: function ( config ) {
54 |
55 | this.decoderConfig = config;
56 |
57 | return this;
58 |
59 | },
60 |
61 | setWorkerLimit: function ( workerLimit ) {
62 |
63 | this.workerLimit = workerLimit;
64 |
65 | return this;
66 |
67 | },
68 |
69 | /** @deprecated */
70 | setVerbosity: function () {
71 |
72 | console.warn( 'THREE.DRACOLoader: The .setVerbosity() method has been removed.' );
73 |
74 | },
75 |
76 | /** @deprecated */
77 | setDrawMode: function () {
78 |
79 | console.warn( 'THREE.DRACOLoader: The .setDrawMode() method has been removed.' );
80 |
81 | },
82 |
83 | /** @deprecated */
84 | setSkipDequantization: function () {
85 |
86 | console.warn( 'THREE.DRACOLoader: The .setSkipDequantization() method has been removed.' );
87 |
88 | },
89 |
90 | load: function ( url, onLoad, onProgress, onError ) {
91 |
92 | var loader = new FileLoader( this.manager );
93 |
94 | loader.setPath( this.path );
95 | loader.setResponseType( 'arraybuffer' );
96 |
97 | if ( this.crossOrigin === 'use-credentials' ) {
98 |
99 | loader.setWithCredentials( true );
100 |
101 | }
102 |
103 | loader.load( url, ( buffer ) => {
104 |
105 | var taskConfig = {
106 | attributeIDs: this.defaultAttributeIDs,
107 | attributeTypes: this.defaultAttributeTypes,
108 | useUniqueIDs: false
109 | };
110 |
111 | this.decodeGeometry( buffer, taskConfig )
112 | .then( onLoad )
113 | .catch( onError );
114 |
115 | }, onProgress, onError );
116 |
117 | },
118 |
119 | /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
120 | decodeDracoFile: function ( buffer, callback, attributeIDs, attributeTypes ) {
121 |
122 | var taskConfig = {
123 | attributeIDs: attributeIDs || this.defaultAttributeIDs,
124 | attributeTypes: attributeTypes || this.defaultAttributeTypes,
125 | useUniqueIDs: !! attributeIDs
126 | };
127 |
128 | this.decodeGeometry( buffer, taskConfig ).then( callback );
129 |
130 | },
131 |
132 | decodeGeometry: function ( buffer, taskConfig ) {
133 |
134 | // TODO: For backward-compatibility, support 'attributeTypes' objects containing
135 | // references (rather than names) to typed array constructors. These must be
136 | // serialized before sending them to the worker.
137 | for ( var attribute in taskConfig.attributeTypes ) {
138 |
139 | var type = taskConfig.attributeTypes[ attribute ];
140 |
141 | if ( type.BYTES_PER_ELEMENT !== undefined ) {
142 |
143 | taskConfig.attributeTypes[ attribute ] = type.name;
144 |
145 | }
146 |
147 | }
148 |
149 | //
150 |
151 | var taskKey = JSON.stringify( taskConfig );
152 |
153 | // Check for an existing task using this buffer. A transferred buffer cannot be transferred
154 | // again from this thread.
155 | if ( DRACOLoader.taskCache.has( buffer ) ) {
156 |
157 | var cachedTask = DRACOLoader.taskCache.get( buffer );
158 |
159 | if ( cachedTask.key === taskKey ) {
160 |
161 | return cachedTask.promise;
162 |
163 | } else if ( buffer.byteLength === 0 ) {
164 |
165 | // Technically, it would be possible to wait for the previous task to complete,
166 | // transfer the buffer back, and decode again with the second configuration. That
167 | // is complex, and I don't know of any reason to decode a Draco buffer twice in
168 | // different ways, so this is left unimplemented.
169 | throw new Error(
170 |
171 | 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
172 | 'settings. Buffer has already been transferred.'
173 |
174 | );
175 |
176 | }
177 |
178 | }
179 |
180 | //
181 |
182 | var worker;
183 | var taskID = this.workerNextTaskID ++;
184 | var taskCost = buffer.byteLength;
185 |
186 | // Obtain a worker and assign a task, and construct a geometry instance
187 | // when the task completes.
188 | var geometryPending = this._getWorker( taskID, taskCost )
189 | .then( ( _worker ) => {
190 |
191 | worker = _worker;
192 |
193 | return new Promise( ( resolve, reject ) => {
194 |
195 | worker._callbacks[ taskID ] = { resolve, reject };
196 |
197 | worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
198 |
199 | // this.debug();
200 |
201 | } );
202 |
203 | } )
204 | .then( ( message ) => this._createGeometry( message.geometry ) );
205 |
206 | // Remove task from the task list.
207 | geometryPending
208 | .finally( () => {
209 |
210 | if ( worker && taskID ) {
211 |
212 | this._releaseTask( worker, taskID );
213 |
214 | // this.debug();
215 |
216 | }
217 |
218 | } );
219 |
220 | // Cache the task result.
221 | DRACOLoader.taskCache.set( buffer, {
222 |
223 | key: taskKey,
224 | promise: geometryPending
225 |
226 | } );
227 |
228 | return geometryPending;
229 |
230 | },
231 |
232 | _createGeometry: function ( geometryData ) {
233 |
234 | var geometry = new BufferGeometry();
235 |
236 | if ( geometryData.index ) {
237 |
238 | geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) );
239 |
240 | }
241 |
242 | for ( var i = 0; i < geometryData.attributes.length; i ++ ) {
243 |
244 | var attribute = geometryData.attributes[ i ];
245 | var name = attribute.name;
246 | var array = attribute.array;
247 | var itemSize = attribute.itemSize;
248 |
249 | geometry.setAttribute( name, new BufferAttribute( array, itemSize ) );
250 |
251 | }
252 |
253 | return geometry;
254 |
255 | },
256 |
257 | _loadLibrary: function ( url, responseType ) {
258 |
259 | var loader = new FileLoader( this.manager );
260 | loader.setPath( this.decoderPath );
261 | loader.setResponseType( responseType );
262 |
263 | return new Promise( ( resolve, reject ) => {
264 |
265 | loader.load( url, resolve, undefined, reject );
266 |
267 | } );
268 |
269 | },
270 |
271 | preload: function () {
272 |
273 | this._initDecoder();
274 |
275 | return this;
276 |
277 | },
278 |
279 | _initDecoder: function () {
280 |
281 | if ( this.decoderPending ) return this.decoderPending;
282 |
283 | var useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
284 | var librariesPending = [];
285 |
286 | if ( useJS ) {
287 |
288 | librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
289 |
290 | } else {
291 |
292 | librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
293 | librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
294 |
295 | }
296 |
297 | this.decoderPending = Promise.all( librariesPending )
298 | .then( ( libraries ) => {
299 |
300 | var jsContent = libraries[ 0 ];
301 |
302 | if ( ! useJS ) {
303 |
304 | this.decoderConfig.wasmBinary = libraries[ 1 ];
305 |
306 | }
307 |
308 | var fn = DRACOLoader.DRACOWorker.toString();
309 |
310 | var body = [
311 | '/* draco decoder */',
312 | jsContent,
313 | '',
314 | '/* worker */',
315 | fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
316 | ].join( '\n' );
317 |
318 | this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
319 |
320 | } );
321 |
322 | return this.decoderPending;
323 |
324 | },
325 |
326 | _getWorker: function ( taskID, taskCost ) {
327 |
328 | return this._initDecoder().then( () => {
329 |
330 | if ( this.workerPool.length < this.workerLimit ) {
331 |
332 | var worker = new Worker( this.workerSourceURL );
333 |
334 | worker._callbacks = {};
335 | worker._taskCosts = {};
336 | worker._taskLoad = 0;
337 |
338 | worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
339 |
340 | worker.onmessage = function ( e ) {
341 |
342 | var message = e.data;
343 |
344 | switch ( message.type ) {
345 |
346 | case 'decode':
347 | worker._callbacks[ message.id ].resolve( message );
348 | break;
349 |
350 | case 'error':
351 | worker._callbacks[ message.id ].reject( message );
352 | break;
353 |
354 | default:
355 | console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
356 |
357 | }
358 |
359 | };
360 |
361 | this.workerPool.push( worker );
362 |
363 | } else {
364 |
365 | this.workerPool.sort( function ( a, b ) {
366 |
367 | return a._taskLoad > b._taskLoad ? - 1 : 1;
368 |
369 | } );
370 |
371 | }
372 |
373 | var worker = this.workerPool[ this.workerPool.length - 1 ];
374 | worker._taskCosts[ taskID ] = taskCost;
375 | worker._taskLoad += taskCost;
376 | return worker;
377 |
378 | } );
379 |
380 | },
381 |
382 | _releaseTask: function ( worker, taskID ) {
383 |
384 | worker._taskLoad -= worker._taskCosts[ taskID ];
385 | delete worker._callbacks[ taskID ];
386 | delete worker._taskCosts[ taskID ];
387 |
388 | },
389 |
390 | debug: function () {
391 |
392 | console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
393 |
394 | },
395 |
396 | dispose: function () {
397 |
398 | for ( var i = 0; i < this.workerPool.length; ++ i ) {
399 |
400 | this.workerPool[ i ].terminate();
401 |
402 | }
403 |
404 | this.workerPool.length = 0;
405 |
406 | return this;
407 |
408 | }
409 |
410 | } );
411 |
412 | /* WEB WORKER */
413 |
414 | DRACOLoader.DRACOWorker = function () {
415 |
416 | var decoderConfig;
417 | var decoderPending;
418 |
419 | onmessage = function ( e ) {
420 |
421 | var message = e.data;
422 |
423 | switch ( message.type ) {
424 |
425 | case 'init':
426 | decoderConfig = message.decoderConfig;
427 | decoderPending = new Promise( function ( resolve/*, reject*/ ) {
428 |
429 | decoderConfig.onModuleLoaded = function ( draco ) {
430 |
431 | // Module is Promise-like. Wrap before resolving to avoid loop.
432 | resolve( { draco: draco } );
433 |
434 | };
435 |
436 | DracoDecoderModule( decoderConfig );
437 |
438 | } );
439 | break;
440 |
441 | case 'decode':
442 | var buffer = message.buffer;
443 | var taskConfig = message.taskConfig;
444 | decoderPending.then( ( module ) => {
445 |
446 | var draco = module.draco;
447 | var decoder = new draco.Decoder();
448 | var decoderBuffer = new draco.DecoderBuffer();
449 | decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );
450 |
451 | try {
452 |
453 | var geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );
454 |
455 | var buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
456 |
457 | if ( geometry.index ) buffers.push( geometry.index.array.buffer );
458 |
459 | self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
460 |
461 | } catch ( error ) {
462 |
463 | console.error( error );
464 |
465 | self.postMessage( { type: 'error', id: message.id, error: error.message } );
466 |
467 | } finally {
468 |
469 | draco.destroy( decoderBuffer );
470 | draco.destroy( decoder );
471 |
472 | }
473 |
474 | } );
475 | break;
476 |
477 | }
478 |
479 | };
480 |
481 | function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {
482 |
483 | var attributeIDs = taskConfig.attributeIDs;
484 | var attributeTypes = taskConfig.attributeTypes;
485 |
486 | var dracoGeometry;
487 | var decodingStatus;
488 |
489 | var geometryType = decoder.GetEncodedGeometryType( decoderBuffer );
490 |
491 | if ( geometryType === draco.TRIANGULAR_MESH ) {
492 |
493 | dracoGeometry = new draco.Mesh();
494 | decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );
495 |
496 | } else if ( geometryType === draco.POINT_CLOUD ) {
497 |
498 | dracoGeometry = new draco.PointCloud();
499 | decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );
500 |
501 | } else {
502 |
503 | throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
504 |
505 | }
506 |
507 | if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
508 |
509 | throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
510 |
511 | }
512 |
513 | var geometry = { index: null, attributes: [] };
514 |
515 | // Gather all vertex attributes.
516 | for ( var attributeName in attributeIDs ) {
517 |
518 | var attributeType = self[ attributeTypes[ attributeName ] ];
519 |
520 | var attribute;
521 | var attributeID;
522 |
523 | // A Draco file may be created with default vertex attributes, whose attribute IDs
524 | // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
525 | // a Draco file may contain a custom set of attributes, identified by known unique
526 | // IDs. glTF files always do the latter, and `.drc` files typically do the former.
527 | if ( taskConfig.useUniqueIDs ) {
528 |
529 | attributeID = attributeIDs[ attributeName ];
530 | attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
531 |
532 | } else {
533 |
534 | attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
535 |
536 | if ( attributeID === - 1 ) continue;
537 |
538 | attribute = decoder.GetAttribute( dracoGeometry, attributeID );
539 |
540 | }
541 |
542 | geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
543 |
544 | }
545 |
546 | // Add index.
547 | if ( geometryType === draco.TRIANGULAR_MESH ) {
548 |
549 | // Generate mesh faces.
550 | var numFaces = dracoGeometry.num_faces();
551 | var numIndices = numFaces * 3;
552 | var index = new Uint32Array( numIndices );
553 | var indexArray = new draco.DracoInt32Array();
554 |
555 | for ( var i = 0; i < numFaces; ++ i ) {
556 |
557 | decoder.GetFaceFromMesh( dracoGeometry, i, indexArray );
558 |
559 | for ( var j = 0; j < 3; ++ j ) {
560 |
561 | index[ i * 3 + j ] = indexArray.GetValue( j );
562 |
563 | }
564 |
565 | }
566 |
567 | geometry.index = { array: index, itemSize: 1 };
568 |
569 | draco.destroy( indexArray );
570 |
571 | }
572 |
573 | draco.destroy( dracoGeometry );
574 |
575 | return geometry;
576 |
577 | }
578 |
579 | function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
580 |
581 | var numComponents = attribute.num_components();
582 | var numPoints = dracoGeometry.num_points();
583 | var numValues = numPoints * numComponents;
584 | var dracoArray;
585 |
586 | var array;
587 |
588 | switch ( attributeType ) {
589 |
590 | case Float32Array:
591 | dracoArray = new draco.DracoFloat32Array();
592 | decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray );
593 | array = new Float32Array( numValues );
594 | break;
595 |
596 | case Int8Array:
597 | dracoArray = new draco.DracoInt8Array();
598 | decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
599 | array = new Int8Array( numValues );
600 | break;
601 |
602 | case Int16Array:
603 | dracoArray = new draco.DracoInt16Array();
604 | decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
605 | array = new Int16Array( numValues );
606 | break;
607 |
608 | case Int32Array:
609 | dracoArray = new draco.DracoInt32Array();
610 | decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
611 | array = new Int32Array( numValues );
612 | break;
613 |
614 | case Uint8Array:
615 | dracoArray = new draco.DracoUInt8Array();
616 | decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
617 | array = new Uint8Array( numValues );
618 | break;
619 |
620 | case Uint16Array:
621 | dracoArray = new draco.DracoUInt16Array();
622 | decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
623 | array = new Uint16Array( numValues );
624 | break;
625 |
626 | case Uint32Array:
627 | dracoArray = new draco.DracoUInt32Array();
628 | decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
629 | array = new Uint32Array( numValues );
630 | break;
631 |
632 | default:
633 | throw new Error( 'THREE.DRACOLoader: Unexpected attribute type.' );
634 |
635 | }
636 |
637 | for ( var i = 0; i < numValues; i ++ ) {
638 |
639 | array[ i ] = dracoArray.GetValue( i );
640 |
641 | }
642 |
643 | draco.destroy( dracoArray );
644 |
645 | return {
646 | name: attributeName,
647 | array: array,
648 | itemSize: numComponents
649 | };
650 |
651 | }
652 |
653 | };
654 |
655 | DRACOLoader.taskCache = new WeakMap();
656 |
657 | /** Deprecated static methods */
658 |
659 | /** @deprecated */
660 | DRACOLoader.setDecoderPath = function () {
661 |
662 | console.warn( 'THREE.DRACOLoader: The .setDecoderPath() method has been removed. Use instance methods.' );
663 |
664 | };
665 |
666 | /** @deprecated */
667 | DRACOLoader.setDecoderConfig = function () {
668 |
669 | console.warn( 'THREE.DRACOLoader: The .setDecoderConfig() method has been removed. Use instance methods.' );
670 |
671 | };
672 |
673 | /** @deprecated */
674 | DRACOLoader.releaseDecoderModule = function () {
675 |
676 | console.warn( 'THREE.DRACOLoader: The .releaseDecoderModule() method has been removed. Use instance methods.' );
677 |
678 | };
679 |
680 | /** @deprecated */
681 | DRACOLoader.getDecoderModule = function () {
682 |
683 | console.warn( 'THREE.DRACOLoader: The .getDecoderModule() method has been removed. Use instance methods.' );
684 |
685 | };
686 |
687 | export { DRACOLoader };
688 |
--------------------------------------------------------------------------------
/Cornell box/dependencies/orbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | * @author ScieCode / http://github.com/sciecode
8 | */
9 |
10 | import {
11 | EventDispatcher,
12 | MOUSE,
13 | Quaternion,
14 | Spherical,
15 | TOUCH,
16 | Vector2,
17 | Vector3
18 | } from "./three.module.js";
19 |
20 | // This set of controls performs orbiting, dollying (zooming), and panning.
21 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
22 | //
23 | // Orbit - left mouse / touch: one-finger move
24 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
25 | // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
26 |
27 | var OrbitControls = function ( object, domElement ) {
28 |
29 | this.object = object;
30 |
31 | this.domElement = ( domElement !== undefined ) ? domElement : document;
32 |
33 | // Set to false to disable this control
34 | this.enabled = true;
35 |
36 | // "target" sets the location of focus, where the object orbits around
37 | this.target = new Vector3();
38 |
39 | // How far you can dolly in and out ( PerspectiveCamera only )
40 | this.minDistance = 0;
41 | this.maxDistance = Infinity;
42 |
43 | // How far you can zoom in and out ( OrthographicCamera only )
44 | this.minZoom = 0;
45 | this.maxZoom = Infinity;
46 |
47 | // How far you can orbit vertically, upper and lower limits.
48 | // Range is 0 to Math.PI radians.
49 | this.minPolarAngle = 0; // radians
50 | this.maxPolarAngle = Math.PI; // radians
51 |
52 | // How far you can orbit horizontally, upper and lower limits.
53 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
54 | this.minAzimuthAngle = - Infinity; // radians
55 | this.maxAzimuthAngle = Infinity; // radians
56 |
57 | // Set to true to enable damping (inertia)
58 | // If damping is enabled, you must call controls.update() in your animation loop
59 | this.enableDamping = false;
60 | this.dampingFactor = 0.05;
61 |
62 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
63 | // Set to false to disable zooming
64 | this.enableZoom = true;
65 | this.zoomSpeed = 1.0;
66 |
67 | // Set to false to disable rotating
68 | this.enableRotate = true;
69 | this.rotateSpeed = 1.0;
70 |
71 | // Set to false to disable panning
72 | this.enablePan = true;
73 | this.panSpeed = 1.0;
74 | this.screenSpacePanning = false; // if true, pan in screen-space
75 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
76 |
77 | // Set to true to automatically rotate around the target
78 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
79 | this.autoRotate = false;
80 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
81 |
82 | // Set to false to disable use of the keys
83 | this.enableKeys = true;
84 |
85 | // The four arrow keys
86 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
87 |
88 | // Mouse buttons
89 | this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
90 |
91 | // Touch fingers
92 | this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
93 |
94 | // for reset
95 | this.target0 = this.target.clone();
96 | this.position0 = this.object.position.clone();
97 | this.zoom0 = this.object.zoom;
98 |
99 |
100 | this.lastViewMatrixInverse = null;
101 |
102 | //
103 | // public methods
104 | //
105 |
106 | this.getPolarAngle = function () {
107 |
108 | return spherical.phi;
109 |
110 | };
111 |
112 | this.getAzimuthalAngle = function () {
113 |
114 | return spherical.theta;
115 |
116 | };
117 |
118 | this.saveState = function () {
119 |
120 | scope.target0.copy( scope.target );
121 | scope.position0.copy( scope.object.position );
122 | scope.zoom0 = scope.object.zoom;
123 |
124 | };
125 |
126 | this.reset = function () {
127 |
128 | scope.target.copy( scope.target0 );
129 | scope.object.position.copy( scope.position0 );
130 | scope.object.zoom = scope.zoom0;
131 |
132 | scope.object.updateProjectionMatrix();
133 | scope.dispatchEvent( changeEvent );
134 |
135 | scope.update();
136 |
137 | state = STATE.NONE;
138 |
139 | };
140 |
141 | // this method is exposed, but perhaps it would be better if we can make it private...
142 | this.update = function () {
143 |
144 | var offset = new Vector3();
145 |
146 | // so camera.up is the orbit axis
147 | var quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
148 | var quatInverse = quat.clone().inverse();
149 |
150 | var lastPosition = new Vector3();
151 | var lastQuaternion = new Quaternion();
152 |
153 | return function update() {
154 |
155 | this.lastViewMatrixInverse = scope.object.matrixWorldInverse.clone();
156 |
157 |
158 | var position = scope.object.position;
159 |
160 | offset.copy( position ).sub( scope.target );
161 |
162 | // rotate offset to "y-axis-is-up" space
163 | offset.applyQuaternion( quat );
164 |
165 | // angle from z-axis around y-axis
166 | spherical.setFromVector3( offset );
167 |
168 | if ( scope.autoRotate && state === STATE.NONE ) {
169 |
170 | rotateLeft( getAutoRotationAngle() );
171 |
172 | }
173 |
174 | if ( scope.enableDamping ) {
175 |
176 | spherical.theta += sphericalDelta.theta * scope.dampingFactor;
177 | spherical.phi += sphericalDelta.phi * scope.dampingFactor;
178 |
179 | } else {
180 |
181 | spherical.theta += sphericalDelta.theta;
182 | spherical.phi += sphericalDelta.phi;
183 |
184 | }
185 |
186 | // restrict theta to be between desired limits
187 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
188 |
189 | // restrict phi to be between desired limits
190 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
191 |
192 | spherical.makeSafe();
193 |
194 |
195 | if( scope.enableDamping ) {
196 | scale += scaleDelta * 4 * scope.dampingFactor;
197 | }
198 |
199 |
200 | spherical.radius *= scale;
201 |
202 | // restrict radius to be between desired limits
203 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
204 |
205 | // move target to panned location
206 |
207 | if ( scope.enableDamping === true ) {
208 |
209 | scope.target.addScaledVector( panOffset, scope.dampingFactor );
210 |
211 | } else {
212 |
213 | scope.target.add( panOffset );
214 |
215 | }
216 |
217 | offset.setFromSpherical( spherical );
218 |
219 | // rotate offset back to "camera-up-vector-is-up" space
220 | offset.applyQuaternion( quatInverse );
221 |
222 | position.copy( scope.target ).add( offset );
223 |
224 | scope.object.lookAt( scope.target );
225 |
226 | if ( scope.enableDamping === true ) {
227 |
228 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
229 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
230 |
231 | panOffset.multiplyScalar( 1 - scope.dampingFactor );
232 |
233 | scaleDelta *= ( 1 - scope.dampingFactor );
234 |
235 | } else {
236 |
237 | sphericalDelta.set( 0, 0, 0 );
238 |
239 | panOffset.set( 0, 0, 0 );
240 |
241 | scaleDelta = 0;
242 |
243 | }
244 |
245 | scale = 1;
246 |
247 | // update condition is:
248 | // min(camera displacement, camera rotation in radians)^2 > EPS
249 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
250 |
251 | if ( zoomChanged ||
252 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
253 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
254 |
255 | scope.dispatchEvent( changeEvent );
256 |
257 | lastPosition.copy( scope.object.position );
258 | lastQuaternion.copy( scope.object.quaternion );
259 | zoomChanged = false;
260 |
261 | return true;
262 |
263 | }
264 |
265 | return false;
266 |
267 | };
268 |
269 | }();
270 |
271 | this.dispose = function () {
272 |
273 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
274 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
275 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
276 |
277 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
278 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
279 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
280 |
281 | document.removeEventListener( 'mousemove', onMouseMove, false );
282 | document.removeEventListener( 'mouseup', onMouseUp, false );
283 |
284 | window.removeEventListener( 'keydown', onKeyDown, false );
285 |
286 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
287 |
288 | };
289 |
290 | //
291 | // internals
292 | //
293 |
294 | var scope = this;
295 |
296 | var changeEvent = { type: 'change' };
297 | var startEvent = { type: 'start' };
298 | var endEvent = { type: 'end' };
299 | var zoomEvent = { type: 'zoom' };
300 |
301 | var STATE = {
302 | NONE: - 1,
303 | ROTATE: 0,
304 | DOLLY: 1,
305 | PAN: 2,
306 | TOUCH_ROTATE: 3,
307 | TOUCH_PAN: 4,
308 | TOUCH_DOLLY_PAN: 5,
309 | TOUCH_DOLLY_ROTATE: 6
310 | };
311 |
312 | var state = STATE.NONE;
313 |
314 | var EPS = 0.000001;
315 |
316 | // current position in spherical coordinates
317 | var spherical = new Spherical();
318 | var sphericalDelta = new Spherical();
319 |
320 | var scale = 1;
321 | var scaleDelta = 1;
322 | var panOffset = new Vector3();
323 | var zoomChanged = false;
324 |
325 | var rotateStart = new Vector2();
326 | var rotateEnd = new Vector2();
327 | var rotateDelta = new Vector2();
328 |
329 | var panStart = new Vector2();
330 | var panEnd = new Vector2();
331 | var panDelta = new Vector2();
332 |
333 | var dollyStart = new Vector2();
334 | var dollyEnd = new Vector2();
335 | var dollyDelta = new Vector2();
336 |
337 | function getAutoRotationAngle() {
338 |
339 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
340 |
341 | }
342 |
343 | function getZoomScale() {
344 |
345 | return Math.pow( 0.95, scope.zoomSpeed );
346 |
347 | }
348 |
349 | function rotateLeft( angle ) {
350 |
351 | sphericalDelta.theta -= angle;
352 |
353 | }
354 |
355 | function rotateUp( angle ) {
356 |
357 | sphericalDelta.phi -= angle;
358 |
359 | }
360 |
361 | var panLeft = function () {
362 |
363 | var v = new Vector3();
364 |
365 | return function panLeft( distance, objectMatrix ) {
366 |
367 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
368 | v.multiplyScalar( - distance );
369 |
370 | panOffset.add( v );
371 |
372 | };
373 |
374 | }();
375 |
376 | var panUp = function () {
377 |
378 | var v = new Vector3();
379 |
380 | return function panUp( distance, objectMatrix ) {
381 |
382 | if ( scope.screenSpacePanning === true ) {
383 |
384 | v.setFromMatrixColumn( objectMatrix, 1 );
385 |
386 | } else {
387 |
388 | v.setFromMatrixColumn( objectMatrix, 0 );
389 | v.crossVectors( scope.object.up, v );
390 |
391 | }
392 |
393 | v.multiplyScalar( distance );
394 |
395 | panOffset.add( v );
396 |
397 | };
398 |
399 | }();
400 |
401 | // deltaX and deltaY are in pixels; right and down are positive
402 | var pan = function () {
403 |
404 | var offset = new Vector3();
405 |
406 | return function pan( deltaX, deltaY ) {
407 |
408 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
409 |
410 | if ( scope.object.isPerspectiveCamera ) {
411 |
412 | // perspective
413 | var position = scope.object.position;
414 | offset.copy( position ).sub( scope.target );
415 | var targetDistance = offset.length();
416 |
417 | // half of the fov is center to top of screen
418 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
419 |
420 | // we use only clientHeight here so aspect ratio does not distort speed
421 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
422 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
423 |
424 | } else if ( scope.object.isOrthographicCamera ) {
425 |
426 | // orthographic
427 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
428 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
429 |
430 | } else {
431 |
432 | // camera neither orthographic nor perspective
433 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
434 | scope.enablePan = false;
435 |
436 | }
437 |
438 | };
439 |
440 | }();
441 |
442 | function dollyIn( dollyScale ) {
443 |
444 | if ( scope.object.isPerspectiveCamera ) {
445 |
446 | // scale /= dollyScale;
447 | scaleDelta = scale - scale * dollyScale;
448 |
449 | } else if ( scope.object.isOrthographicCamera ) {
450 |
451 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
452 | scope.object.updateProjectionMatrix();
453 | zoomChanged = true;
454 |
455 | } else {
456 |
457 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
458 | scope.enableZoom = false;
459 |
460 | }
461 |
462 | }
463 |
464 | function dollyOut( dollyScale ) {
465 |
466 | if ( scope.object.isPerspectiveCamera ) {
467 |
468 | // scale *= dollyScale;
469 | scaleDelta = scale - scale / dollyScale;
470 |
471 | } else if ( scope.object.isOrthographicCamera ) {
472 |
473 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
474 | scope.object.updateProjectionMatrix();
475 | zoomChanged = true;
476 |
477 | } else {
478 |
479 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
480 | scope.enableZoom = false;
481 |
482 | }
483 |
484 | }
485 |
486 | //
487 | // event callbacks - update the object state
488 | //
489 |
490 | function handleMouseDownRotate( event ) {
491 |
492 | rotateStart.set( event.clientX, event.clientY );
493 |
494 | }
495 |
496 | function handleMouseDownDolly( event ) {
497 |
498 | dollyStart.set( event.clientX, event.clientY );
499 |
500 | }
501 |
502 | function handleMouseDownPan( event ) {
503 |
504 | panStart.set( event.clientX, event.clientY );
505 |
506 | }
507 |
508 | function handleMouseMoveRotate( event ) {
509 |
510 | rotateEnd.set( event.clientX, event.clientY );
511 |
512 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
513 |
514 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
515 |
516 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
517 |
518 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
519 |
520 | rotateStart.copy( rotateEnd );
521 |
522 | // scope.update();
523 |
524 | }
525 |
526 | function handleMouseMoveDolly( event ) {
527 |
528 | dollyEnd.set( event.clientX, event.clientY );
529 |
530 | dollyDelta.subVectors( dollyEnd, dollyStart );
531 |
532 | if ( dollyDelta.y > 0 ) {
533 |
534 | dollyIn( getZoomScale() );
535 |
536 | } else if ( dollyDelta.y < 0 ) {
537 |
538 | dollyOut( getZoomScale() );
539 |
540 | }
541 |
542 | dollyStart.copy( dollyEnd );
543 |
544 | // scope.update();
545 |
546 | }
547 |
548 | function handleMouseMovePan( event ) {
549 |
550 | panEnd.set( event.clientX, event.clientY );
551 |
552 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
553 |
554 | pan( panDelta.x, panDelta.y );
555 |
556 | panStart.copy( panEnd );
557 |
558 | // scope.update();
559 |
560 | }
561 |
562 | function handleMouseUp( /*event*/ ) {
563 |
564 | // no-op
565 |
566 | }
567 |
568 | function handleMouseWheel( event ) {
569 |
570 | if ( event.deltaY < 0 ) {
571 |
572 | dollyOut( getZoomScale() );
573 |
574 | } else if ( event.deltaY > 0 ) {
575 |
576 | dollyIn( getZoomScale() );
577 |
578 | }
579 |
580 | // scope.update();
581 |
582 | }
583 |
584 | function handleKeyDown( event ) {
585 |
586 | var needsUpdate = false;
587 |
588 | switch ( event.keyCode ) {
589 |
590 | case scope.keys.UP:
591 | pan( 0, scope.keyPanSpeed );
592 | needsUpdate = true;
593 | break;
594 |
595 | case scope.keys.BOTTOM:
596 | pan( 0, - scope.keyPanSpeed );
597 | needsUpdate = true;
598 | break;
599 |
600 | case scope.keys.LEFT:
601 | pan( scope.keyPanSpeed, 0 );
602 | needsUpdate = true;
603 | break;
604 |
605 | case scope.keys.RIGHT:
606 | pan( - scope.keyPanSpeed, 0 );
607 | needsUpdate = true;
608 | break;
609 |
610 | }
611 |
612 | if ( needsUpdate ) {
613 |
614 | // prevent the browser from scrolling on cursor keys
615 | event.preventDefault();
616 |
617 | scope.update();
618 |
619 | }
620 |
621 |
622 | }
623 |
624 | function handleTouchStartRotate( event ) {
625 |
626 | if ( event.touches.length == 1 ) {
627 |
628 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
629 |
630 | } else {
631 |
632 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
633 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
634 |
635 | rotateStart.set( x, y );
636 |
637 | }
638 |
639 | }
640 |
641 | function handleTouchStartPan( event ) {
642 |
643 | if ( event.touches.length == 1 ) {
644 |
645 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
646 |
647 | } else {
648 |
649 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
650 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
651 |
652 | panStart.set( x, y );
653 |
654 | }
655 |
656 | }
657 |
658 | function handleTouchStartDolly( event ) {
659 |
660 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
661 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
662 |
663 | var distance = Math.sqrt( dx * dx + dy * dy );
664 |
665 | dollyStart.set( 0, distance );
666 |
667 | }
668 |
669 | function handleTouchStartDollyPan( event ) {
670 |
671 | if ( scope.enableZoom ) handleTouchStartDolly( event );
672 |
673 | if ( scope.enablePan ) handleTouchStartPan( event );
674 |
675 | }
676 |
677 | function handleTouchStartDollyRotate( event ) {
678 |
679 | if ( scope.enableZoom ) handleTouchStartDolly( event );
680 |
681 | if ( scope.enableRotate ) handleTouchStartRotate( event );
682 |
683 | }
684 |
685 | function handleTouchMoveRotate( event ) {
686 |
687 | if ( event.touches.length == 1 ) {
688 |
689 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
690 |
691 | } else {
692 |
693 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
694 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
695 |
696 | rotateEnd.set( x, y );
697 |
698 | }
699 |
700 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
701 |
702 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
703 |
704 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
705 |
706 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
707 |
708 | rotateStart.copy( rotateEnd );
709 |
710 | }
711 |
712 | function handleTouchMovePan( event ) {
713 |
714 | if ( event.touches.length == 1 ) {
715 |
716 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
717 |
718 | } else {
719 |
720 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
721 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
722 |
723 | panEnd.set( x, y );
724 |
725 | }
726 |
727 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
728 |
729 | pan( panDelta.x, panDelta.y );
730 |
731 | panStart.copy( panEnd );
732 |
733 | }
734 |
735 | function handleTouchMoveDolly( event ) {
736 |
737 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
738 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
739 |
740 | var distance = Math.sqrt( dx * dx + dy * dy );
741 |
742 | dollyEnd.set( 0, distance );
743 |
744 | dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
745 |
746 | dollyIn( dollyDelta.y );
747 |
748 | dollyStart.copy( dollyEnd );
749 |
750 | }
751 |
752 | function handleTouchMoveDollyPan( event ) {
753 |
754 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
755 |
756 | if ( scope.enablePan ) handleTouchMovePan( event );
757 |
758 | }
759 |
760 | function handleTouchMoveDollyRotate( event ) {
761 |
762 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
763 |
764 | if ( scope.enableRotate ) handleTouchMoveRotate( event );
765 |
766 | }
767 |
768 | function handleTouchEnd( /*event*/ ) {
769 |
770 | // no-op
771 |
772 | }
773 |
774 | //
775 | // event handlers - FSM: listen for events and reset state
776 | //
777 |
778 | function onMouseDown( event ) {
779 |
780 | if ( scope.enabled === false ) return;
781 |
782 | // Prevent the browser from scrolling.
783 |
784 | event.preventDefault();
785 |
786 | // Manually set the focus since calling preventDefault above
787 | // prevents the browser from setting it automatically.
788 |
789 | scope.domElement.focus ? scope.domElement.focus() : window.focus();
790 |
791 | switch ( event.button ) {
792 |
793 | case 0:
794 |
795 | switch ( scope.mouseButtons.LEFT ) {
796 |
797 | case MOUSE.ROTATE:
798 |
799 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
800 |
801 | if ( scope.enablePan === false ) return;
802 |
803 | handleMouseDownPan( event );
804 |
805 | state = STATE.PAN;
806 |
807 | } else {
808 |
809 | if ( scope.enableRotate === false ) return;
810 |
811 | handleMouseDownRotate( event );
812 |
813 | state = STATE.ROTATE;
814 |
815 | }
816 |
817 | break;
818 |
819 | case MOUSE.PAN:
820 |
821 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
822 |
823 | if ( scope.enableRotate === false ) return;
824 |
825 | handleMouseDownRotate( event );
826 |
827 | state = STATE.ROTATE;
828 |
829 | } else {
830 |
831 | if ( scope.enablePan === false ) return;
832 |
833 | handleMouseDownPan( event );
834 |
835 | state = STATE.PAN;
836 |
837 | }
838 |
839 | break;
840 |
841 | default:
842 |
843 | state = STATE.NONE;
844 |
845 | }
846 |
847 | break;
848 |
849 |
850 | case 1:
851 |
852 | switch ( scope.mouseButtons.MIDDLE ) {
853 |
854 | case MOUSE.DOLLY:
855 |
856 | if ( scope.enableZoom === false ) return;
857 |
858 | handleMouseDownDolly( event );
859 |
860 | state = STATE.DOLLY;
861 |
862 | break;
863 |
864 |
865 | default:
866 |
867 | state = STATE.NONE;
868 |
869 | }
870 |
871 | break;
872 |
873 | case 2:
874 |
875 | switch ( scope.mouseButtons.RIGHT ) {
876 |
877 | case MOUSE.ROTATE:
878 |
879 | if ( scope.enableRotate === false ) return;
880 |
881 | handleMouseDownRotate( event );
882 |
883 | state = STATE.ROTATE;
884 |
885 | break;
886 |
887 | case MOUSE.PAN:
888 |
889 | if ( scope.enablePan === false ) return;
890 |
891 | handleMouseDownPan( event );
892 |
893 | state = STATE.PAN;
894 |
895 | break;
896 |
897 | default:
898 |
899 | state = STATE.NONE;
900 |
901 | }
902 |
903 | break;
904 |
905 | }
906 |
907 | if ( state !== STATE.NONE ) {
908 |
909 | document.addEventListener( 'mousemove', onMouseMove, false );
910 | document.addEventListener( 'mouseup', onMouseUp, false );
911 |
912 | scope.dispatchEvent( startEvent );
913 |
914 | }
915 |
916 | }
917 |
918 | function onMouseMove( event ) {
919 |
920 | if ( scope.enabled === false ) return;
921 |
922 | event.preventDefault();
923 |
924 | switch ( state ) {
925 |
926 | case STATE.ROTATE:
927 |
928 | if ( scope.enableRotate === false ) return;
929 |
930 | handleMouseMoveRotate( event );
931 |
932 | break;
933 |
934 | case STATE.DOLLY:
935 |
936 | if ( scope.enableZoom === false ) return;
937 |
938 | handleMouseMoveDolly( event );
939 |
940 | break;
941 |
942 | case STATE.PAN:
943 |
944 | if ( scope.enablePan === false ) return;
945 |
946 | handleMouseMovePan( event );
947 |
948 | break;
949 |
950 | }
951 |
952 | }
953 |
954 | function onMouseUp( event ) {
955 |
956 | if ( scope.enabled === false ) return;
957 |
958 | handleMouseUp( event );
959 |
960 | document.removeEventListener( 'mousemove', onMouseMove, false );
961 | document.removeEventListener( 'mouseup', onMouseUp, false );
962 |
963 | scope.dispatchEvent( endEvent );
964 |
965 | state = STATE.NONE;
966 |
967 | }
968 |
969 | function onMouseWheel( event ) {
970 |
971 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
972 |
973 | event.preventDefault();
974 | event.stopPropagation();
975 |
976 | scope.dispatchEvent( startEvent );
977 | scope.dispatchEvent( zoomEvent );
978 |
979 | handleMouseWheel( event );
980 |
981 | scope.dispatchEvent( endEvent );
982 |
983 | }
984 |
985 | function onKeyDown( event ) {
986 |
987 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
988 |
989 | handleKeyDown( event );
990 |
991 | }
992 |
993 | function onTouchStart( event ) {
994 |
995 | if ( scope.enabled === false ) return;
996 |
997 | event.preventDefault();
998 |
999 | switch ( event.touches.length ) {
1000 |
1001 | case 1:
1002 |
1003 | switch ( scope.touches.ONE ) {
1004 |
1005 | case TOUCH.ROTATE:
1006 |
1007 | if ( scope.enableRotate === false ) return;
1008 |
1009 | handleTouchStartRotate( event );
1010 |
1011 | state = STATE.TOUCH_ROTATE;
1012 |
1013 | break;
1014 |
1015 | case TOUCH.PAN:
1016 |
1017 | if ( scope.enablePan === false ) return;
1018 |
1019 | handleTouchStartPan( event );
1020 |
1021 | state = STATE.TOUCH_PAN;
1022 |
1023 | break;
1024 |
1025 | default:
1026 |
1027 | state = STATE.NONE;
1028 |
1029 | }
1030 |
1031 | break;
1032 |
1033 | case 2:
1034 |
1035 | switch ( scope.touches.TWO ) {
1036 |
1037 | case TOUCH.DOLLY_PAN:
1038 |
1039 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1040 |
1041 | handleTouchStartDollyPan( event );
1042 |
1043 | state = STATE.TOUCH_DOLLY_PAN;
1044 |
1045 | break;
1046 |
1047 | case TOUCH.DOLLY_ROTATE:
1048 |
1049 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1050 |
1051 | handleTouchStartDollyRotate( event );
1052 |
1053 | state = STATE.TOUCH_DOLLY_ROTATE;
1054 |
1055 | break;
1056 |
1057 | default:
1058 |
1059 | state = STATE.NONE;
1060 |
1061 | }
1062 |
1063 | break;
1064 |
1065 | default:
1066 |
1067 | state = STATE.NONE;
1068 |
1069 | }
1070 |
1071 | if ( state !== STATE.NONE ) {
1072 |
1073 | scope.dispatchEvent( startEvent );
1074 |
1075 | }
1076 |
1077 | }
1078 |
1079 | function onTouchMove( event ) {
1080 |
1081 | if ( scope.enabled === false ) return;
1082 |
1083 | event.preventDefault();
1084 | event.stopPropagation();
1085 |
1086 | switch ( state ) {
1087 |
1088 | case STATE.TOUCH_ROTATE:
1089 |
1090 | if ( scope.enableRotate === false ) return;
1091 |
1092 | handleTouchMoveRotate( event );
1093 |
1094 | // scope.update();
1095 |
1096 | break;
1097 |
1098 | case STATE.TOUCH_PAN:
1099 |
1100 | if ( scope.enablePan === false ) return;
1101 |
1102 | handleTouchMovePan( event );
1103 |
1104 | // scope.update();
1105 |
1106 | break;
1107 |
1108 | case STATE.TOUCH_DOLLY_PAN:
1109 |
1110 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1111 |
1112 | handleTouchMoveDollyPan( event );
1113 |
1114 | // scope.update();
1115 |
1116 | break;
1117 |
1118 | case STATE.TOUCH_DOLLY_ROTATE:
1119 |
1120 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1121 |
1122 | handleTouchMoveDollyRotate( event );
1123 |
1124 | // scope.update();
1125 |
1126 | break;
1127 |
1128 | default:
1129 |
1130 | state = STATE.NONE;
1131 |
1132 | }
1133 |
1134 | }
1135 |
1136 | function onTouchEnd( event ) {
1137 |
1138 | if ( scope.enabled === false ) return;
1139 |
1140 | handleTouchEnd( event );
1141 |
1142 | scope.dispatchEvent( endEvent );
1143 |
1144 | state = STATE.NONE;
1145 |
1146 | }
1147 |
1148 | function onContextMenu( event ) {
1149 |
1150 | if ( scope.enabled === false ) return;
1151 |
1152 | event.preventDefault();
1153 |
1154 | }
1155 |
1156 | //
1157 |
1158 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
1159 |
1160 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
1161 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
1162 |
1163 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
1164 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
1165 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
1166 |
1167 | window.addEventListener( 'keydown', onKeyDown, false );
1168 |
1169 | // force an update at start
1170 |
1171 | this.update();
1172 |
1173 | };
1174 |
1175 | OrbitControls.prototype = Object.create( EventDispatcher.prototype );
1176 | OrbitControls.prototype.constructor = OrbitControls;
1177 |
1178 |
1179 | // This set of controls performs orbiting, dollying (zooming), and panning.
1180 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
1181 | // This is very similar to OrbitControls, another set of touch behavior
1182 | //
1183 | // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
1184 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
1185 | // Pan - left mouse, or arrow keys / touch: one-finger move
1186 |
1187 | var MapControls = function ( object, domElement ) {
1188 |
1189 | OrbitControls.call( this, object, domElement );
1190 |
1191 | this.mouseButtons.LEFT = MOUSE.PAN;
1192 | this.mouseButtons.RIGHT = MOUSE.ROTATE;
1193 |
1194 | this.touches.ONE = TOUCH.PAN;
1195 | this.touches.TWO = TOUCH.DOLLY_ROTATE;
1196 |
1197 | };
1198 |
1199 | MapControls.prototype = Object.create( EventDispatcher.prototype );
1200 | MapControls.prototype.constructor = MapControls;
1201 |
1202 | export { OrbitControls, MapControls };
1203 |
--------------------------------------------------------------------------------
/Cornell box/main.js:
--------------------------------------------------------------------------------
1 | import * as THREE from "./dependencies/three.module.js";
2 | import { OrbitControls } from "./dependencies/orbitControls.js";
3 | import { position_fs, position_vs } from "./shaders/position.js";
4 | import { material_fs, material_vs } from "./shaders/material.js";
5 | import { normal_fs, normal_vs } from "./shaders/normals.js";
6 | import { display_fs, display_vs } from "./shaders/display.js";
7 | import { makeSceneShaders } from "./shaders/radiance.js";
8 | import { atrous_fs, atrous_vs } from "./shaders/atrous.js";
9 | import { momentMove_fs, momentMove_vs } from "./shaders/momentMove.js";
10 | import { historyTest_fs, historyTest_vs, historyAccum_fs, historyAccum_vs } from "./shaders/history.js";
11 | import { radianceAccum_fs, radianceAccum_vs } from "./shaders/radianceAccum.js";
12 | import * as dat from './dependencies/dat.gui.js';
13 |
14 |
15 | window.addEventListener("load", init);
16 |
17 | let scene;
18 | let displayScene;
19 | let camera;
20 | let controls;
21 | let renderer;
22 | let pmremGenerator;
23 | let hdrCubeRenderTarget;
24 | let HDRtexture;
25 |
26 | let positionRT;
27 | let normalRT;
28 | let radianceRT;
29 | let atrousRT;
30 | let momentMoveRT;
31 | let historyRT;
32 | let materialRT;
33 |
34 | let positionBufferMaterial;
35 | let materialBufferMaterial;
36 | let normalBufferMaterial;
37 | let radianceBufferMaterial;
38 | let momentBufferMaterial;
39 | let historyTestMaterial;
40 | let historyAccumMaterial;
41 | let radianceAccumMaterial;
42 | let atrousMaterial;
43 |
44 | let displayQuadMesh;
45 | let mesh;
46 |
47 | let kpress;
48 | let lpress;
49 | let opress;
50 | let ppress;
51 | let npress;
52 | let mpress;
53 |
54 |
55 | function init() {
56 | // let w = new THREE.Vector3(0, 0, -1);
57 | // let u = w.clone().cross(new THREE.Vector3(0, 1, 0));
58 | // let v = u.clone().cross(w);
59 | // console.log(u);
60 | // console.log(v);
61 | // return;
62 |
63 | renderer = new THREE.WebGLRenderer({ antialias: false });
64 | renderer.setSize( window.innerWidth, window.innerHeight );
65 | renderer.toneMapping = THREE.ACESFilmicToneMapping;
66 | renderer.toneMappingExposure = 0.8;
67 | renderer.outputEncoding = THREE.sRGBEncoding;
68 | renderer.autoClear = false;
69 | document.body.appendChild( renderer.domElement );
70 |
71 | scene = new THREE.Scene();
72 | displayScene = new THREE.Scene();
73 | camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
74 |
75 | controls = new OrbitControls( camera, renderer.domElement );
76 | controls.enableDamping = true;
77 | controls.dampingFactor = 0.0875;
78 | controls.enablePan = true;
79 | controls.panSpeed = 1.0;
80 | controls.screenSpacePanning = true;
81 |
82 | //controls.update() must be called after any manual changes to the camera's transform
83 | camera.position.set( 0, 1, 18 );
84 | controls.target.set( 0, 0, 0 );
85 | controls.update();
86 |
87 |
88 |
89 | let geom = createGeometry(0);
90 |
91 |
92 |
93 | positionRT = new THREE.WebGLRenderTarget(innerWidth, innerHeight, {
94 | magFilter: THREE.NearestFilter,
95 | minFilter: THREE.NearestFilter,
96 | type: THREE.FloatType,
97 | stencilBuffer: false,
98 | });
99 |
100 | normalRT = new THREE.WebGLRenderTarget(innerWidth, innerHeight, {
101 | magFilter: THREE.NearestFilter,
102 | minFilter: THREE.NearestFilter,
103 | type: THREE.FloatType,
104 | stencilBuffer: false,
105 | });
106 |
107 | momentMoveRT = new THREE.WebGLRenderTarget(innerWidth, innerHeight, {
108 | magFilter: THREE.NearestFilter,
109 | minFilter: THREE.NearestFilter,
110 | type: THREE.FloatType,
111 | stencilBuffer: false,
112 | });
113 |
114 | materialRT = new THREE.WebGLRenderTarget(innerWidth, innerHeight, {
115 | magFilter: THREE.NearestFilter,
116 | minFilter: THREE.NearestFilter,
117 | type: THREE.FloatType,
118 | stencilBuffer: false,
119 | });
120 |
121 |
122 | atrousRT = createDoubleFBO(innerWidth, innerHeight, THREE.NearestFilter);
123 | historyRT = createTripleFBO(innerWidth, innerHeight, THREE.NearestFilter);
124 | radianceRT = createTripleFBO(innerWidth, innerHeight, THREE.NearestFilter);
125 |
126 |
127 |
128 | positionBufferMaterial = new THREE.ShaderMaterial({
129 | fragmentShader: position_fs,
130 | vertexShader: position_vs,
131 | side: THREE.DoubleSide,
132 | });
133 |
134 | normalBufferMaterial = new THREE.ShaderMaterial({
135 | uniforms: {
136 | "uCameraPos": { value: camera.position },
137 | },
138 | fragmentShader: normal_fs,
139 | vertexShader: normal_vs,
140 | side: THREE.DoubleSide,
141 | });
142 |
143 | materialBufferMaterial = new THREE.ShaderMaterial({
144 | fragmentShader: material_fs,
145 | vertexShader: material_vs,
146 | side: THREE.DoubleSide,
147 | });
148 |
149 | momentBufferMaterial = new THREE.ShaderMaterial({
150 | uniforms: {
151 | "uOldModelViewMatrix": { value: new THREE.Matrix4() },
152 | },
153 | fragmentShader: momentMove_fs,
154 | vertexShader: momentMove_vs,
155 | side: THREE.DoubleSide,
156 | });
157 |
158 | radianceBufferMaterial = new THREE.ShaderMaterial({
159 | uniforms: {
160 | "uScene": { value: geom.uniform },
161 |
162 | "uRadMult": { value: 1 },
163 | "uCameraPos": { value: camera.position },
164 | "uCameraTarget": { value: controls.target },
165 | "uAspectRatio": { value: innerWidth / innerHeight },
166 | "uRandom": { value: new THREE.Vector4(0, 0, 0, 0) },
167 | "uTime": { value: 0 },
168 |
169 | "uMirrorIndex": { value: 1 },
170 |
171 | "uPositionBuffer": { type: "t", value: positionRT.texture },
172 | },
173 | transparent: true,
174 | blending: THREE.CustomBlending,
175 | blendEquation: THREE.AddEquation,
176 | blendSrc: THREE.OneFactor,
177 | blendDst: THREE.OneFactor,
178 |
179 | fragmentShader: radiance_fs,
180 | vertexShader: radiance_vs,
181 | side: THREE.DoubleSide,
182 | });
183 |
184 | atrousMaterial = new THREE.ShaderMaterial({
185 | defines: {
186 | "atrous3x3": true,
187 | },
188 | uniforms: {
189 | "uRadiance": { type: "t", value: radianceRT.rt3.texture },
190 | "uNormal": { type: "t", value: normalRT.texture },
191 | "uPosition": { type: "t", value: positionRT.texture },
192 | "uMaterial": { type: "t", value: materialRT.texture },
193 | "uHistoryAccum": { type: "t", value: historyRT.rt3.texture },
194 | "uFilterHistoryModulation": { value: 0 },
195 | "uMaxFramesHistory": { value: 0 },
196 | "uStep": { value: 1.0 },
197 | "uScreenSize": { value: new THREE.Vector2(innerWidth, innerHeight) },
198 | "uC_phi": { value: 0.0 },
199 | "uN_phi": { value: 0.0 },
200 | "uP_phi": { value: 0.0 },
201 | },
202 | fragmentShader: atrous_fs,
203 | vertexShader: atrous_vs,
204 | side: THREE.DoubleSide,
205 | });
206 |
207 | historyTestMaterial = new THREE.ShaderMaterial({
208 | uniforms: {
209 | "uNormalBuffer": { type: "t", value: normalRT.texture },
210 | "uPositionBuffer": { type: "t", value: positionRT.texture },
211 | "uMomentMove": { type: "t", value: momentMoveRT.texture },
212 | "uCameraPos": { type: "t", value: camera.position },
213 | "uInvScreenSize": { value: new THREE.Vector2(1 / innerWidth, 1 / innerHeight) },
214 | },
215 | fragmentShader: historyTest_fs,
216 | vertexShader: historyTest_vs,
217 | side: THREE.DoubleSide,
218 | });
219 |
220 |
221 | historyAccumMaterial = new THREE.ShaderMaterial({
222 | uniforms: {
223 | "uHistoryTest": { type: "t", value: null },
224 | "uHistoryAccum": { type: "t", value: null },
225 | "uMomentMove": { type: "t", value: momentMoveRT.texture },
226 | },
227 | fragmentShader: historyAccum_fs,
228 | vertexShader: historyAccum_vs,
229 | side: THREE.DoubleSide,
230 | });
231 |
232 | radianceAccumMaterial = new THREE.ShaderMaterial({
233 | uniforms: {
234 | "uCurrentRadiance": { type: "t", value: null },
235 | "uAccumulatedRadiance": { type: "t", value: null },
236 | "uHistoryBuffer": { type: "t", value: null },
237 | "uMomentMoveBuffer": { type: "t", value: null },
238 | "uMaxFramesHistory": { type: "t", value: null },
239 | },
240 | fragmentShader: radianceAccum_fs,
241 | vertexShader: radianceAccum_vs,
242 | side: THREE.DoubleSide,
243 | });
244 |
245 | window.displayMaterial = new THREE.ShaderMaterial({
246 | uniforms: {
247 | "uTexture": { type: "t", value: radianceRT.rt3.texture },
248 | },
249 | fragmentShader: display_fs,
250 | vertexShader: display_vs,
251 | side: THREE.DoubleSide,
252 | });
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 | // pmremGenerator = new THREE.PMREMGenerator( renderer, "1.44" );
261 | // // pmremGenerator.compileEquirectangularShader();
262 |
263 |
264 | // let hdrEquiTexture;
265 | // new RGBELoader()
266 | // .setDataType( THREE.UnsignedByteType ) // alt: FloatType, HalfFloatType
267 | // .load( "envmaps/env.hdr", function ( texture, textureData ) {
268 |
269 | // HDRtexture = texture;
270 | // // hdrEquiTexture = texture;
271 | // hdrCubeRenderTarget = pmremGenerator.fromEquirectangular( texture );
272 |
273 | // onDownload();
274 | // });
275 |
276 | var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
277 | mesh = new THREE.Mesh( geom.geometry, material );
278 | mesh.geometryCULL = geom.geometryCULL;
279 |
280 | scene.add(mesh);
281 |
282 |
283 |
284 | window.addEventListener("keydown", (e) => {
285 | if(e.key == "k") kpress = true;
286 | if(e.key == "l") lpress = true;
287 | if(e.key == "p") ppress = true;
288 | if(e.key == "o") opress = true;
289 | if(e.key == "m") mpress = true;
290 | if(e.key == "n") npress = true;
291 |
292 | });
293 | window.addEventListener("keyup", (e) => {
294 | if(e.key == "k") kpress = false;
295 | if(e.key == "l") lpress = false;
296 | if(e.key == "p") ppress = false;
297 | if(e.key == "o") opress = false;
298 | if(e.key == "m") mpress = false;
299 | if(e.key == "n") npress = false;
300 | });
301 |
302 |
303 |
304 | displayQuadMesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2,2), displayMaterial);
305 | displayScene.add(displayQuadMesh);
306 |
307 |
308 | initGUI();
309 | animate(0);
310 | }
311 |
312 | function createDoubleFBO(w, h, filtering) {
313 | let rt1 = new THREE.WebGLRenderTarget(w, h, {
314 | type: THREE.FloatType,
315 | minFilter: filtering || THREE.LinearFilter,
316 | magFilter: filtering || THREE.LinearFilter,
317 | wrapS: THREE.ClampToEdgeWrapping,
318 | wrapT: THREE.ClampToEdgeWrapping,
319 | format: THREE.RGBAFormat,
320 | stencilBuffer: false,
321 | anisotropy: 1,
322 | });
323 |
324 | let rt2 = new THREE.WebGLRenderTarget(w, h, {
325 | type: THREE.FloatType,
326 | minFilter: filtering || THREE.LinearFilter,
327 | magFilter: filtering || THREE.LinearFilter,
328 | wrapS: THREE.ClampToEdgeWrapping,
329 | wrapT: THREE.ClampToEdgeWrapping,
330 | format: THREE.RGBAFormat,
331 | stencilBuffer: false,
332 | anisotropy: 1,
333 | });
334 |
335 | return {
336 | read: rt1,
337 | write: rt2,
338 | swap: function() {
339 | let temp = this.read;
340 | this.read = this.write;
341 | this.write = temp;
342 | }
343 | };
344 | }
345 |
346 | function createTripleFBO(w, h, filtering) {
347 | let rt1 = new THREE.WebGLRenderTarget(w, h, {
348 | type: THREE.FloatType,
349 | minFilter: filtering || THREE.LinearFilter,
350 | magFilter: filtering || THREE.LinearFilter,
351 | wrapS: THREE.ClampToEdgeWrapping,
352 | wrapT: THREE.ClampToEdgeWrapping,
353 | format: THREE.RGBAFormat,
354 | stencilBuffer: false,
355 | anisotropy: 1,
356 | });
357 |
358 | let rt2 = new THREE.WebGLRenderTarget(w, h, {
359 | type: THREE.FloatType,
360 | minFilter: filtering || THREE.LinearFilter,
361 | magFilter: filtering || THREE.LinearFilter,
362 | wrapS: THREE.ClampToEdgeWrapping,
363 | wrapT: THREE.ClampToEdgeWrapping,
364 | format: THREE.RGBAFormat,
365 | stencilBuffer: false,
366 | anisotropy: 1,
367 | });
368 |
369 | let rt3 = new THREE.WebGLRenderTarget(w, h, {
370 | type: THREE.FloatType,
371 | minFilter: filtering || THREE.LinearFilter,
372 | magFilter: filtering || THREE.LinearFilter,
373 | wrapS: THREE.ClampToEdgeWrapping,
374 | wrapT: THREE.ClampToEdgeWrapping,
375 | format: THREE.RGBAFormat,
376 | stencilBuffer: false,
377 | anisotropy: 1,
378 | });
379 |
380 | return {
381 | rt1: rt1,
382 | rt2: rt2,
383 | rt3: rt3,
384 | swap_rt2_rt3: function() {
385 | let temp = this.rt2;
386 | this.rt2 = this.rt3;
387 | this.rt3 = temp;
388 | }
389 | };
390 | }
391 |
392 | let prevMeshGeometryCull;
393 | function animate(now) {
394 | requestAnimationFrame( animate );
395 |
396 | now *= 0.001;
397 |
398 |
399 | // HAI DOVUTO DISABILITARE SCOPE.UPDATE() DURANTE IL MOUSEMOVE/MOUSEDOWN ETC
400 | // DENTRO LO SCRIPT ORBITCONTROLS.js,
401 | // ALTRIMENTI controls.lastViewMatrixInverse SAREBBE STATA UGUALE ALLA
402 | // CURRENT MATRIX (mentre cliccavi e facevi drag), FACENDO SBALLARE I CALCOLI
403 | // DELL'HISTORYTEST.
404 | // NON HAI DISABILITATO SCOPE.UPDATE() NELL'HANDLING DEI TOUCH-EVENT, QUINDI
405 | // QUESTO PROGETTO
406 | // NON FUNZIONERA' SUI MOBILES FINCHE' NON RIMUOVI SCOPE.UPDATE() ANCHE DA LI'
407 | controls.update();
408 |
409 |
410 |
411 | let newgeo = createGeometry(now);
412 | let oldgeometryCULL = mesh.geometryCULL;
413 | let oldgeometry = mesh.geometry;
414 | mesh.geometry = newgeo.geometry;
415 | mesh.geometryCULL = newgeo.geometryCULL;
416 | radianceBufferMaterial.uniforms.uScene.value = newgeo.uniform;
417 | radianceBufferMaterial.uniforms.uScene.needsUpdate = true;
418 | radianceBufferMaterial.uniforms.needsUpdate = true;
419 | radianceBufferMaterial.needsUpdate = true;
420 |
421 |
422 |
423 | // we need to create moment buffers BEFORE we update normal/position RTs
424 | // **************** create moment buffers
425 | // are you surprised I'm using the matrixWorldInverse? then think more..
426 | // are you surprised I'm using the matrixWorldInverse? then think more..
427 | let oldCameraMatrix = controls.lastViewMatrixInverse;
428 | let mgCULL = createMomentGeometry(newgeo.geometryCULL, oldgeometryCULL);
429 | let mg = createMomentGeometry(newgeo.geometry, oldgeometry);
430 |
431 | momentBufferMaterial.uniforms.uOldModelViewMatrix.value = oldCameraMatrix;
432 | momentBufferMaterial.uniforms.uOldModelViewMatrix.needsUpdate = true;
433 | momentBufferMaterial.uniforms.needsUpdate = true;
434 | momentBufferMaterial.needsUpdate = true;
435 | momentBufferMaterial.side = THREE.FrontSide;
436 | renderer.setRenderTarget(momentMoveRT);
437 | mesh.material = momentBufferMaterial;
438 | renderer.clear();
439 | mesh.geometry = mgCULL;
440 | renderer.render( scene, camera );
441 |
442 | renderer.setRenderTarget(momentMoveRT);
443 | mesh.geometry = mg;
444 | momentBufferMaterial.side = THREE.DoubleSide;
445 | renderer.render( scene, camera );
446 | // reassign the new geometry after we're done here...
447 | mesh.geometry = newgeo.geometry;
448 | // **************** create moment buffers - END
449 |
450 |
451 |
452 | // ************** create history buffer
453 | // on rt1 we add the success vs unsuccess buffer (either +1 or -1)
454 | renderer.setRenderTarget(historyRT.rt1);
455 | renderer.clear();
456 | mesh.material = historyTestMaterial;
457 | mesh.geometry = newgeo.geometryCULL;
458 | historyTestMaterial.uniforms.uCameraPos.value = camera.position;
459 | historyTestMaterial.side = THREE.FrontSide;
460 | renderer.render( scene, camera );
461 |
462 | renderer.setRenderTarget(historyRT.rt1);
463 | historyTestMaterial.side = THREE.DoubleSide;
464 | mesh.geometry = newgeo.geometry;
465 | renderer.render( scene, camera );
466 |
467 |
468 |
469 | historyRT.swap_rt2_rt3();
470 | // rt2 now holds the previously accumulated values
471 | // rt3 updates the old accumulated values with the new buffer on rt1
472 | renderer.setRenderTarget(historyRT.rt3);
473 | renderer.clear();
474 | displayQuadMesh.material = historyAccumMaterial;
475 | historyAccumMaterial.uniforms.uHistoryTest.value = historyRT.rt1.texture;
476 | historyAccumMaterial.uniforms.uHistoryAccum.value = historyRT.rt2.texture;
477 | renderer.render( displayScene, camera );
478 | // ************** create history buffer - END
479 |
480 |
481 |
482 |
483 | // **************** creating buffers
484 | renderer.setRenderTarget(positionRT);
485 | mesh.material = positionBufferMaterial;
486 | positionBufferMaterial.side = THREE.FrontSide;
487 | mesh.geometry = newgeo.geometryCULL;
488 | renderer.clear();
489 | renderer.render( scene, camera );
490 |
491 | renderer.setRenderTarget(positionRT);
492 | positionBufferMaterial.side = THREE.DoubleSide;
493 | mesh.geometry = newgeo.geometry;
494 | renderer.render( scene, camera );
495 |
496 |
497 |
498 | renderer.setRenderTarget(materialRT);
499 | mesh.material = materialBufferMaterial;
500 | mesh.material.side = THREE.FrontSide;
501 | mesh.geometry = newgeo.geometryCULL;
502 | renderer.clear();
503 | renderer.render( scene, camera );
504 |
505 | renderer.setRenderTarget(materialRT);
506 | mesh.material.side = THREE.DoubleSide;
507 | mesh.geometry = newgeo.geometry;
508 | renderer.render( scene, camera );
509 |
510 |
511 |
512 | renderer.setRenderTarget(normalRT);
513 | mesh.material = normalBufferMaterial;
514 | mesh.material.side = THREE.FrontSide;
515 | mesh.geometry = newgeo.geometryCULL;
516 | normalBufferMaterial.uniforms.uCameraPos.value = camera.position;
517 | renderer.clear();
518 | renderer.render( scene, camera );
519 |
520 | renderer.setRenderTarget(normalRT);
521 | mesh.geometry = newgeo.geometry;
522 | mesh.material.side = THREE.DoubleSide;
523 | renderer.render( scene, camera );
524 |
525 |
526 | renderer.setRenderTarget(radianceRT.rt1);
527 | renderer.clear();
528 | for(let i = 0; i < controller.spp; i++) {
529 | renderer.setRenderTarget(radianceRT.rt1);
530 | radianceBufferMaterial.uniforms.uRadMult.value = 1 / (controller.spp);
531 | radianceBufferMaterial.uniforms.uCameraPos.value = camera.position;
532 | radianceBufferMaterial.uniforms.uCameraTarget.value = controls.target;
533 | radianceBufferMaterial.uniforms.uRandom.value = new THREE.Vector4(Math.random(), Math.random(), Math.random(), Math.random());
534 | radianceBufferMaterial.uniforms.uTime.value = now;
535 | radianceBufferMaterial.uniforms.uMirrorIndex.value = controller.mirrorIndex;
536 | displayQuadMesh.material = radianceBufferMaterial;
537 | renderer.render(displayScene, camera );
538 | }
539 | // ************** accumulating radiance
540 | radianceRT.swap_rt2_rt3();
541 |
542 | renderer.setRenderTarget(radianceRT.rt3);
543 | renderer.clear();
544 | displayQuadMesh.material = radianceAccumMaterial;
545 | radianceAccumMaterial.uniforms.uCurrentRadiance.value = radianceRT.rt1.texture;
546 | radianceAccumMaterial.uniforms.uAccumulatedRadiance.value = radianceRT.rt2.texture;
547 | radianceAccumMaterial.uniforms.uHistoryBuffer.value = historyRT.rt3.texture;
548 | radianceAccumMaterial.uniforms.uMomentMoveBuffer.value = momentMoveRT.texture;
549 | radianceAccumMaterial.uniforms.uMaxFramesHistory.value = controller.maxFramesHistory;
550 | renderer.render(displayScene, camera );
551 | // ************** accumulating radiance - END
552 |
553 |
554 | // **************** creating buffers - END
555 |
556 |
557 |
558 |
559 | // **************** atrous
560 | atrousMaterial.uniforms.uN_phi.value = controller.n_phi;
561 | atrousMaterial.uniforms.uP_phi.value = controller.p_phi;
562 | atrousMaterial.uniforms.uC_phi.value = controller.c_phi;
563 |
564 | renderer.setRenderTarget(atrousRT.write);
565 | atrousMaterial.uniforms.uRadiance.value = radianceRT.rt3.texture;
566 | atrousMaterial.uniforms.uHistoryAccum.value = historyRT.rt3.texture;
567 | atrousMaterial.uniforms.uMaxFramesHistory.value = controller.maxFramesHistory;
568 | atrousMaterial.uniforms.uFilterHistoryModulation.value = controller.filterHistoryModulation;
569 | atrousMaterial.uniforms.uStep.value = 1.0;
570 | displayQuadMesh.material = atrousMaterial;
571 | renderer.clear();
572 | renderer.render(displayScene, camera );
573 |
574 | for(let i = 0; i < Math.floor(controller.iterations); i++) {
575 | atrousRT.swap();
576 |
577 | renderer.setRenderTarget(atrousRT.write);
578 | atrousMaterial.uniforms.uRadiance.value = atrousRT.read.texture;
579 | atrousMaterial.uniforms.uStep.value *= controller.stepMultiplier;
580 | atrousMaterial.uniforms.uC_phi.value *= controller.c_phiMultPerIt;
581 | displayQuadMesh.material = atrousMaterial;
582 | renderer.clear();
583 | renderer.render(displayScene, camera );
584 | }
585 | atrousRT.swap();
586 | // **************** atrous - END
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 | renderer.setRenderTarget(null);
595 | displayQuadMesh.material = displayMaterial;
596 | // displayQuadMesh.material.uniforms.uTexture.value = radianceRT.rt3.texture;
597 | displayQuadMesh.material.uniforms.uTexture.value = atrousRT.write.texture;
598 | // if(kpress) {
599 | // // displayQuadMesh.material.uniforms.uTexture.value = momentMoveRT.texture;
600 | // // displayQuadMesh.material.uniforms.uTexture.value = normalRT.texture;
601 | // // displayQuadMesh.material.uniforms.uTexture.value = historyRT.rt1.texture;
602 | // // displayQuadMesh.material.uniforms.uTexture.value = historyRT.rt3.texture;
603 | // displayQuadMesh.material.uniforms.uTexture.value = radianceRT.rt3.texture;
604 | // }
605 |
606 | if(kpress) displayQuadMesh.material.uniforms.uTexture.value = radianceRT.rt3.texture;
607 | if(lpress) displayQuadMesh.material.uniforms.uTexture.value = normalRT.texture;
608 | if(opress) displayQuadMesh.material.uniforms.uTexture.value = positionRT.texture;
609 | if(ppress) displayQuadMesh.material.uniforms.uTexture.value = historyRT.rt3.texture;
610 | if(npress) displayQuadMesh.material.uniforms.uTexture.value = momentMoveRT.texture;
611 | if(mpress) displayQuadMesh.material.uniforms.uTexture.value = radianceRT.rt1.texture;
612 |
613 | renderer.clear();
614 | renderer.render(displayScene, camera);
615 | }
616 |
617 | let controller;
618 | function initGUI() {
619 |
620 | var gui = new dat.GUI();
621 |
622 | var GUIcontroller = function() {
623 | this.c_phi = 105;
624 | this.n_phi = 0.01;
625 | this.p_phi = 1;
626 |
627 | this.c_phiMultPerIt = 0.34;
628 |
629 | this.stepMultiplier = 1.5;
630 | this.iterations = 10;
631 |
632 | this.atrous5x5 = false;
633 |
634 | this.maxFramesHistory = 10;
635 | this.filterHistoryModulation = 0.54;
636 | this.spp = 3;
637 | this.mirrorIndex = 1;
638 |
639 | this.lowQuality = function() {
640 | this.spp = 1;
641 | this.iterations = 7;
642 | this.filterHistoryModulation = 0.37;
643 | this.stepMultiplier = 1.7;
644 | this.atrous5x5 = false;
645 | this.maxFramesHistory = 10;
646 | this.c_phiMultPerIt = 0.34;
647 | this.c_phi = 105;
648 | this.n_phi = 0.01;
649 | this.p_phi = 1;
650 |
651 | this.updateGUI();
652 | };
653 |
654 | this.mediumQuality = function() {
655 | this.spp = 2;
656 | this.iterations = 8;
657 | this.c_phiMultPerIt = 0.36;
658 | this.filterHistoryModulation = 0.42;
659 | this.stepMultiplier = 1.6;
660 | this.c_phi = 105;
661 | this.n_phi = 0.01;
662 | this.p_phi = 1;
663 | this.maxFramesHistory = 10;
664 | this.atrous5x5 = false;
665 |
666 | this.updateGUI();
667 | };
668 |
669 | this.highQuality = function() {
670 | this.c_phi = 105;
671 | this.n_phi = 0.01;
672 | this.p_phi = 1;
673 |
674 | this.c_phiMultPerIt = 0.34;
675 |
676 | this.stepMultiplier = 1.5;
677 | this.iterations = 10;
678 |
679 | this.atrous5x5 = false;
680 |
681 | this.maxFramesHistory = 10;
682 | this.filterHistoryModulation = 0.54;
683 | this.spp = 3;
684 |
685 | this.updateGUI();
686 | }
687 |
688 | this.veryHighQuality = function() {
689 | this.c_phi = 105;
690 | this.n_phi = 0.01;
691 | this.p_phi = 1;
692 |
693 | this.c_phiMultPerIt = 0.34;
694 |
695 | this.stepMultiplier = 1.47;
696 | this.iterations = 10;
697 |
698 | this.atrous5x5 = false;
699 |
700 | this.maxFramesHistory = 7;
701 | this.filterHistoryModulation = 0.35;
702 | this.spp = 5;
703 |
704 | this.updateGUI();
705 | }
706 |
707 | this.updateGUI = function() {
708 | for(let folder in gui.__folders) {
709 | if(!gui.__folders.hasOwnProperty(folder)) continue;
710 |
711 | for(let j = 0; j < gui.__folders[folder].__controllers.length; j++) {
712 | let property = gui.__folders[folder].__controllers[j].property;
713 |
714 | if(controller.hasOwnProperty(property)) {
715 | gui.__folders[folder].__controllers[j].setValue(controller[property]);
716 | }
717 | }
718 | }
719 | };
720 |
721 | };
722 |
723 | controller = new GUIcontroller();
724 |
725 |
726 | var wff = gui.addFolder('Wavelet Filter');
727 | var ptf = gui.addFolder('Path Tracer');
728 | var rpf = gui.addFolder('Reprojection Params');
729 | var qpf = gui.addFolder('Quality Presets');
730 |
731 | wff.add(controller, 'c_phi', 0, 200).onChange(function(value) {
732 | atrousMaterial.uniforms.uC_phi.value = value;
733 | });
734 | wff.add(controller, 'n_phi', 0.01, 30).onChange(function(value) {
735 | atrousMaterial.uniforms.uN_phi.value = value;
736 | });
737 | wff.add(controller, 'p_phi', 0, 30).onChange(function(value) {
738 | atrousMaterial.uniforms.uP_phi.value = value;
739 | });
740 | wff.add(controller, 'c_phiMultPerIt', 0, 1);
741 | wff.add(controller, 'stepMultiplier', 0, 5);
742 | wff.add(controller, 'iterations', 0, 10).step(1);
743 | wff.add(controller, 'atrous5x5').onChange(function(value) {
744 | if(value) {
745 | atrousMaterial.defines = {
746 | "atrous5x5": true,
747 | };
748 | } else {
749 | atrousMaterial.defines = {
750 | "atrous3x3": true,
751 | };
752 | }
753 |
754 | atrousMaterial.needsUpdate = true;
755 | });
756 |
757 | ptf.add(controller, 'spp', 1, 10).step(1);
758 | ptf.add(controller, 'mirrorIndex', 1, 4).step(1);
759 |
760 | rpf.add(controller, 'maxFramesHistory', 0, 100).step(1);
761 | rpf.add(controller, 'filterHistoryModulation', 0, 1);
762 |
763 | qpf.add(controller, 'lowQuality');
764 | qpf.add(controller, 'mediumQuality');
765 | qpf.add(controller, 'highQuality');
766 | qpf.add(controller, 'veryHighQuality');
767 |
768 |
769 |
770 | wff.open();
771 | ptf.open();
772 | rpf.open();
773 | qpf.open();
774 | }
775 |
776 |
777 | let addt = 18;
778 | let tot_triangles = 14 + addt;
779 | makeSceneShaders(tot_triangles);
780 | // let addt = 0;
781 | let randBuffer = [];
782 | let randBufferTransl = [];
783 | for(let i = 0; i < addt; i++) {
784 | let index = 0; //Math.floor(Math.random() * 4);
785 |
786 | randBuffer.push(Math.random() * 2 - 1, Math.random(), Math.random() * 2 - 1, index);
787 | randBuffer.push(Math.random() * 2 - 1, Math.random(), Math.random() * 2 - 1, index);
788 | randBuffer.push(Math.random() * 2 - 1, Math.random(), Math.random() * 2 - 1, index);
789 | }
790 | for(let i = 0; i < addt; i++) {
791 | randBufferTransl.push(
792 | Math.random() * 2 - 1,
793 | Math.random() * 2 - 1,
794 | Math.random() * 2 - 1
795 | );
796 | }
797 | function createGeometry(time) {
798 | var geometryCULL = new THREE.BufferGeometry();
799 | // create a simple square shape. We duplicate the top left and bottom right
800 | // vertices because each vertex needs to appear once per triangle.
801 | var verticesCULL = [
802 | -5, -5, -5, 1,
803 | -5, +5, -5, 1,
804 | -5, -5, +5, 1,
805 |
806 | -5, +5, +5, 1,
807 | -5, -5, +5, 1,
808 | -5, +5, -5, 1,
809 |
810 | +5, -5, +5, 2,
811 | +5, +5, -5, 2,
812 | +5, -5, -5, 2,
813 |
814 | +5, +5, +5, 2,
815 | +5, +5, -5, 2,
816 | +5, -5, +5, 2,
817 |
818 |
819 | +5, -5, -5, 0,
820 | -5, -5, -5, 0,
821 | -5, -5, +5, 0,
822 |
823 | +5, -5, +5, 0,
824 | +5, -5, -5, 0,
825 | -5, -5, +5, 0,
826 |
827 | +5, +5, -5, 0,
828 | -5, +5, +5, 0,
829 | -5, +5, -5, 0,
830 |
831 | +5, +5, +5, 0,
832 | -5, +5, +5, 0,
833 | +5, +5, -5, 0,
834 |
835 | +5, -5, -5, 3,
836 | -5, +5, -5, 3,
837 | -5, -5, -5, 3,
838 |
839 | -5, +5, -5, 3,
840 | +5, -5, -5, 3,
841 | +5, +5, -5, 3,
842 |
843 | +5, -5, +5, 0,
844 | -5, -5, +5, 0,
845 | -5, +5, +5, 0,
846 |
847 | -5, +5, +5, 0,
848 | +5, +5, +5, 0,
849 | +5, -5, +5, 0,
850 |
851 | // light source, will be back-culled
852 | +3.85, +4.9, -3.85, 15,
853 | -3.85, +4.9, +3.85, 15,
854 | -3.85, +4.9, -3.85, 15,
855 |
856 | +3.85, +4.9, +3.85, 15,
857 | -3.85, +4.9, +3.85, 15,
858 | +3.85, +4.9, -3.85, 15,
859 | ];
860 |
861 |
862 | verticesCULL = new Float32Array(verticesCULL);
863 | // threejs's vertices wont need the "index" property at the 4th position
864 | let threeVerticesCULL = [];
865 | for(let i = 0; i < verticesCULL.length; i+=4) {
866 | threeVerticesCULL.push(verticesCULL[i+0]);
867 | threeVerticesCULL.push(verticesCULL[i+1]);
868 | threeVerticesCULL.push(verticesCULL[i+2]);
869 | }
870 | threeVerticesCULL = new Float32Array(threeVerticesCULL);
871 |
872 | var normalsCULL = [];
873 | for(let i = 0; i < threeVerticesCULL.length; i+=9) {
874 | let v1 = new THREE.Vector3(threeVerticesCULL[i], threeVerticesCULL[i+1], threeVerticesCULL[i+2]);
875 | let v2 = new THREE.Vector3(threeVerticesCULL[i+3], threeVerticesCULL[i+4], threeVerticesCULL[i+5]);
876 | let v3 = new THREE.Vector3(threeVerticesCULL[i+6], threeVerticesCULL[i+7], threeVerticesCULL[i+8]);
877 |
878 | let v2mv1 = v2.clone().sub(v1);
879 | let v3mv1 = v3.clone().sub(v1);
880 |
881 | let n = v2mv1.cross(v3mv1).normalize();
882 | normalsCULL.push(n.x, n.y, n.z);
883 | normalsCULL.push(n.x, n.y, n.z);
884 | normalsCULL.push(n.x, n.y, n.z);
885 | }
886 | normalsCULL = new Float32Array(normalsCULL);
887 |
888 | // itemSize = 3 because there are 3 values (components) per vertex
889 | geometryCULL.setAttribute( 'position', new THREE.BufferAttribute( threeVerticesCULL, 3 ) );
890 | geometryCULL.setAttribute( 'aMaterial', new THREE.BufferAttribute( verticesCULL, 4 ) );
891 | geometryCULL.setAttribute( 'normal', new THREE.BufferAttribute( normalsCULL, 3 ) );
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 | // NON-culled geometry
901 | var geometry = new THREE.BufferGeometry();
902 | // create a simple square shape. We duplicate the top left and bottom right
903 | // vertices because each vertex needs to appear once per triangle.
904 | var vertices = [
905 |
906 | ];
907 | for(let i = 0; i < addt; i++) {
908 | let scale = 4;
909 | let transScale = 1;
910 | let yOffs = -3;
911 |
912 |
913 | vertices.push(randBuffer[i*12 + 0] * scale + randBufferTransl[i * 3 + 0] * transScale);
914 | vertices.push(randBuffer[i*12 + 1] * scale + randBufferTransl[i * 3 + 1] * transScale + yOffs + Math.sin(time + randBuffer[i*12 + 1] * 149.8776));
915 | vertices.push(randBuffer[i*12 + 2] * scale + randBufferTransl[i * 3 + 2] * transScale);
916 | vertices.push(randBuffer[i*12 + 3]);
917 |
918 | vertices.push(randBuffer[i*12 + 4] * scale + randBufferTransl[i * 3 + 0] * transScale);
919 | vertices.push(randBuffer[i*12 + 5] * scale + randBufferTransl[i * 3 + 1] * transScale + yOffs + Math.sin(time + randBuffer[i*12 + 5] * 149.8776));
920 | vertices.push(randBuffer[i*12 + 6] * scale + randBufferTransl[i * 3 + 2] * transScale);
921 | vertices.push(randBuffer[i*12 + 7]);
922 |
923 | vertices.push(randBuffer[i*12 + 8] * scale + randBufferTransl[i * 3 + 0] * transScale);
924 | vertices.push(randBuffer[i*12 + 9] * scale + randBufferTransl[i * 3 + 1] * transScale + yOffs + Math.sin(time + randBuffer[i*12 + 9] * 149.8776));
925 | vertices.push(randBuffer[i*12 + 10] * scale + randBufferTransl[i * 3 + 2] * transScale);
926 | vertices.push(randBuffer[i*12 + 11]);
927 |
928 | // vertices[(i+2) * 9 + 1] = vertices[(i+2)*9 + 1] + Math.sin(time + randBuffer[(i+2)*9] + 1.2);
929 | // vertices[(i+2) * 9 + 4] = vertices[(i+2)*9 + 4] + Math.sin(time + randBuffer[(i+2)*9] + 3.3);
930 | // vertices[(i+2) * 9 + 7] = vertices[(i+2)*9 + 7] + Math.sin(time + randBuffer[(i+2)*9] + 23.3);
931 | }
932 | vertices = new Float32Array(vertices);
933 | // threejs's vertices wont need the "index" property at the 4th position
934 | let threeVertices = [];
935 | for(let i = 0; i < vertices.length; i+=4) {
936 | threeVertices.push(vertices[i+0]);
937 | threeVertices.push(vertices[i+1]);
938 | threeVertices.push(vertices[i+2]);
939 | }
940 | threeVertices = new Float32Array(threeVertices);
941 |
942 | var normals = [];
943 | for(let i = 0; i < threeVertices.length; i+=9) {
944 | let v1 = new THREE.Vector3(threeVertices[i], threeVertices[i+1], threeVertices[i+2]);
945 | let v2 = new THREE.Vector3(threeVertices[i+3], threeVertices[i+4], threeVertices[i+5]);
946 | let v3 = new THREE.Vector3(threeVertices[i+6], threeVertices[i+7], threeVertices[i+8]);
947 |
948 | let v2mv1 = v2.clone().sub(v1);
949 | let v3mv1 = v3.clone().sub(v1);
950 |
951 | let n = v2mv1.cross(v3mv1).normalize();
952 | normals.push(n.x, n.y, n.z);
953 | normals.push(n.x, n.y, n.z);
954 | normals.push(n.x, n.y, n.z);
955 | }
956 | normals = new Float32Array(normals);
957 |
958 | // itemSize = 3 because there are 3 values (components) per vertex
959 | geometry.setAttribute( 'position', new THREE.BufferAttribute( threeVertices, 3 ) );
960 | geometry.setAttribute( 'aMaterial', new THREE.BufferAttribute( vertices, 4 ) );
961 | geometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 | let uniformGeometry = [];
977 | for(let i = 0; i < verticesCULL.length; i+=4) {
978 | uniformGeometry.push(new THREE.Vector4(verticesCULL[i], verticesCULL[i+1], verticesCULL[i+2], verticesCULL[i+3]));
979 | }
980 | for(let i = 0; i < vertices.length; i+=4) {
981 | uniformGeometry.push(new THREE.Vector4(vertices[i], vertices[i+1], vertices[i+2], vertices[i+3]));
982 | }
983 |
984 | return {
985 | uniform: uniformGeometry,
986 | geometryCULL: geometryCULL,
987 | geometry: geometry
988 | };
989 | }
990 |
991 | function createMomentGeometry(newgeo, oldgeo) {
992 | var geometry = new THREE.BufferGeometry();
993 | geometry.setAttribute( 'position', new THREE.BufferAttribute( newgeo.attributes.position.array, 3 ));
994 | geometry.setAttribute( 'oldPosition', new THREE.BufferAttribute( oldgeo.attributes.position.array, 3 ));
995 |
996 | return geometry;
997 | }
--------------------------------------------------------------------------------