├── img
├── 1.jpg
├── map.png
├── t1.png
└── t2.png
├── index.html
└── js
├── app.js
├── app2.js
└── vendor
├── OrbitControls.js
├── postprocessing
├── BloomPass.js
├── EffectComposer.js
├── RenderPass.js
├── ShaderPass.js
└── UnrealBloomPass.js
├── shader
├── ConvolutionShader.js
└── CopyShader.js
└── three.min.js
/img/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imokya/threejs-tunnel-effect/8e53428cebf87c98f635db59e678fc86c8142c35/img/1.jpg
--------------------------------------------------------------------------------
/img/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imokya/threejs-tunnel-effect/8e53428cebf87c98f635db59e678fc86c8142c35/img/map.png
--------------------------------------------------------------------------------
/img/t1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imokya/threejs-tunnel-effect/8e53428cebf87c98f635db59e678fc86c8142c35/img/t1.png
--------------------------------------------------------------------------------
/img/t2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imokya/threejs-tunnel-effect/8e53428cebf87c98f635db59e678fc86c8142c35/img/t2.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Three.js Tunnel
8 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | class App {
2 |
3 | constructor() {
4 | this.init()
5 | }
6 |
7 | init() {
8 | this._initScene()
9 | this._initTube()
10 | this._initPlanes()
11 | this._bindEvent()
12 | this._initVars()
13 | this._render()
14 | }
15 |
16 | _bindEvent() {
17 | window.addEventListener('resize', this._onResize.bind(this))
18 | document.addEventListener('wheel', e => {
19 | this.speed += e.deltaY * 0.002
20 | })
21 | }
22 |
23 | _onResize() {
24 | this.ww = window.innerWidth
25 | this.wh = window.innerHeight
26 | this.renderer.setSize(this.ww, this.wh)
27 | this.camera.aspect = this.ww / this.wh
28 | this.camera.updateProjectionMatrix()
29 | }
30 |
31 | _initTube() {
32 | function CustomSinCurve(scale) {
33 | THREE.Curve.call( this )
34 | this.scale = ( scale === undefined ) ? 1 : scale
35 | }
36 | CustomSinCurve.prototype = Object.create(THREE.Curve.prototype)
37 | CustomSinCurve.prototype.constructor = CustomSinCurve
38 | CustomSinCurve.prototype.getPoint = function (t) {
39 | const tx = Math.cos(2 * Math.PI * t)
40 | const ty = Math.sin(2 * Math.PI * t)
41 | const tz = 0.1 * Math.sin(8 * Math.PI * t)
42 |
43 | // t = t * 4 * Math.PI
44 | // const a = this.radius / 2
45 | // const tx = a * (1 + Math.cos(t))
46 | // const ty = a * Math.sin(t)
47 | // const tz = 2 * a * Math.sin(t / 2)
48 |
49 | return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale)
50 | }
51 | const path = new CustomSinCurve(40)
52 | this.tubeGeometry = new THREE.TubeGeometry(path, 200, 1, 8, false)
53 | const material = new THREE.MeshBasicMaterial({
54 | side: THREE.DoubleSide,
55 | map: new THREE.TextureLoader().load('img/map.png')
56 | })
57 | material.map.wrapS = THREE.RepeatWrapping;
58 | material.map.wrapT= THREE.RepeatWrapping;
59 | material.map.repeat.set(10, 1)
60 | const mesh = new THREE.Mesh(this.tubeGeometry, material)
61 | this.scene.add(mesh)
62 | }
63 |
64 | _initScene() {
65 | this.ww = window.innerWidth
66 | this.wh = window.innerHeight
67 | this.camera = new THREE.PerspectiveCamera(45, this.ww/this.wh, 0.1, 1000)
68 | this.scene = new THREE.Scene()
69 | this.renderer = new THREE.WebGLRenderer({
70 | antialias: true
71 | })
72 | this.renderer.setSize(this.ww, this.wh)
73 | document.body.appendChild(this.renderer.domElement)
74 |
75 | this.camera.position.z = 150
76 | }
77 |
78 | _initPlanes() {
79 | this.planes = []
80 | const tex1 = new THREE.TextureLoader().load('img/t1.png')
81 | const tex2 = new THREE.TextureLoader().load('img/t2.png')
82 | const geo = new THREE.PlaneBufferGeometry(0.5, 0.5)
83 | const mat1 = new THREE.MeshBasicMaterial({
84 | transparent: true,
85 | map: tex1
86 | })
87 | const mat2 = new THREE.MeshBasicMaterial({
88 | transparent: true,
89 | map: tex2
90 | })
91 | geo.center()
92 | const mesh = new THREE.Mesh(geo, mat1)
93 | for(let i = 0; i < 20; i++) {
94 | const plane = mesh.clone()
95 | if(i % 2 == 0) {
96 | plane.material = mat1
97 | } else {
98 | plane.material = mat2
99 | }
100 | plane.position.copy(this.tubeGeometry.parameters.path.getPointAt(i * 0.04))
101 | this.planes.push(plane)
102 | this.scene.add(plane)
103 | }
104 |
105 |
106 | }
107 |
108 |
109 | _initVars() {
110 | //this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement)
111 | this.binormal = new THREE.Vector3()
112 | this.normal = new THREE.Vector3()
113 | this.time = 0
114 | this.position = 0
115 | this.speed = 0
116 | }
117 |
118 | _render() {
119 | //this.time = this.position * 100
120 | this.time += 10
121 | let looptime = 20 * 1000
122 | let t = ( this.time % looptime ) / looptime
123 | let pos = this.tubeGeometry.parameters.path.getPointAt(t)
124 |
125 | let segments = this.tubeGeometry.tangents.length
126 | let pickt = t * segments
127 | let pick = Math.floor(pickt)
128 | let pickNext = (pick + 1) % segments
129 | let offset = 0
130 |
131 | this.binormal.subVectors(this.tubeGeometry.binormals[pickNext], this.tubeGeometry.binormals[pick])
132 | this.binormal.multiplyScalar(pickt - pick).add(this.tubeGeometry.binormals[pick])
133 | let dir = this.tubeGeometry.parameters.path.getTangentAt(t)
134 |
135 | this.normal.copy(this.binormal).cross(dir)
136 | pos.add(this.normal.clone().multiplyScalar(offset))
137 | this.camera.position.copy(pos)
138 | let lookAt = this.tubeGeometry.parameters.path.getPointAt((t+1/this.tubeGeometry.parameters.path.getLength())%1)
139 | this.camera.matrix.lookAt(this.camera.position, lookAt, this.normal)
140 | this.camera.rotation.setFromRotationMatrix(this.camera.matrix, this.camera.rotation.order)
141 |
142 | this.position += this.speed
143 | this.position = this.position < 0 ? 0 : this.position
144 | this.speed *= 0.9
145 |
146 | this.planes.forEach(plane => {
147 | plane.quaternion.copy(this.camera.quaternion)
148 | })
149 |
150 |
151 | this.renderer.render(this.scene, this.camera)
152 | requestAnimationFrame(this._render.bind(this))
153 | }
154 |
155 | }
156 |
157 | const app = new App()
--------------------------------------------------------------------------------
/js/app2.js:
--------------------------------------------------------------------------------
1 | class App {
2 |
3 | constructor() {
4 | this.init()
5 | }
6 |
7 | init() {
8 | this._initScene()
9 | this._initTube()
10 | this._initPlanes()
11 | this._bindEvent()
12 | this._initVars()
13 | this._render()
14 | }
15 |
16 | _bindEvent() {
17 | window.addEventListener('resize', this._onResize.bind(this))
18 | document.addEventListener('wheel', e => {
19 | this.speed += e.deltaY * 0.002
20 | })
21 | }
22 |
23 | _onResize() {
24 | this.ww = window.innerWidth
25 | this.wh = window.innerHeight
26 | this.renderer.setSize(this.ww, this.wh)
27 | this.camera.aspect = this.ww / this.wh
28 | this.camera.updateProjectionMatrix()
29 | }
30 |
31 | _initTube() {
32 | function CustomSinCurve(scale) {
33 | THREE.Curve.call( this )
34 | this.scale = ( scale === undefined ) ? 1 : scale
35 | }
36 | CustomSinCurve.prototype = Object.create(THREE.Curve.prototype)
37 | CustomSinCurve.prototype.constructor = CustomSinCurve
38 | CustomSinCurve.prototype.getPoint = function (t) {
39 | const tx = Math.cos(2 * Math.PI * t)
40 | const ty = Math.sin(2 * Math.PI * t)
41 | const tz = 0.1 * Math.sin(8 * Math.PI * t)
42 |
43 | // t = t * 4 * Math.PI
44 | // const a = this.radius / 2
45 | // const tx = a * (1 + Math.cos(t))
46 | // const ty = a * Math.sin(t)
47 | // const tz = 2 * a * Math.sin(t / 2)
48 |
49 | return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale)
50 | }
51 | const path = new CustomSinCurve(40)
52 | this.tubeGeometry = new THREE.TubeGeometry(path, 200, 5, 8, false)
53 | const material = new THREE.MeshBasicMaterial({
54 | side: THREE.DoubleSide,
55 | map: new THREE.TextureLoader().load('img/map.png')
56 | })
57 | material.map.wrapS = THREE.RepeatWrapping;
58 | material.map.wrapT= THREE.RepeatWrapping;
59 | material.map.repeat.set(10, 1)
60 | const mesh = new THREE.Mesh(this.tubeGeometry, material)
61 | this.scene.add(mesh)
62 | }
63 |
64 | _initScene() {
65 | this.ww = window.innerWidth
66 | this.wh = window.innerHeight
67 | this.camera = new THREE.PerspectiveCamera(45, this.ww/this.wh, 0.1, 1000)
68 | this.scene = new THREE.Scene()
69 | this.renderer = new THREE.WebGLRenderer({
70 | antialias: true
71 | })
72 | this.renderer.setSize(this.ww, this.wh)
73 | document.body.appendChild(this.renderer.domElement)
74 |
75 | this.camera.position.z = 150
76 | }
77 |
78 | _initPlanes() {
79 | this.planes = []
80 | const tex1 = new THREE.TextureLoader().load('img/t1.png')
81 | const tex2 = new THREE.TextureLoader().load('img/t2.png')
82 | const geo = new THREE.PlaneBufferGeometry(0.5, 0.5)
83 | const mat1 = new THREE.MeshBasicMaterial({
84 | transparent: true,
85 | map: tex1
86 | })
87 | const mat2 = new THREE.MeshBasicMaterial({
88 | transparent: true,
89 | map: tex2
90 | })
91 | geo.center()
92 | const mesh = new THREE.Mesh(geo, mat1)
93 | for(let i = 0; i < 20; i++) {
94 | const plane = mesh.clone()
95 | if(i % 2 == 0) {
96 | plane.material = mat1
97 | } else {
98 | plane.material = mat2
99 | }
100 | plane.position.copy(this.tubeGeometry.parameters.path.getPointAt(i * 0.04))
101 | this.planes.push(plane)
102 | this.scene.add(plane)
103 | }
104 |
105 |
106 | }
107 |
108 |
109 | _initVars() {
110 | this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement)
111 | this.binormal = new THREE.Vector3()
112 | this.normal = new THREE.Vector3()
113 | this.time = 0
114 | this.position = 0
115 | this.speed = 0
116 | }
117 |
118 | _render() {
119 | //this.time = this.position * 100
120 | // this.time += 10
121 | // let looptime = 20 * 1000
122 | // let t = ( this.time % looptime ) / looptime
123 | // let pos = this.tubeGeometry.parameters.path.getPointAt(t)
124 |
125 | // let segments = this.tubeGeometry.tangents.length
126 | // let pickt = t * segments
127 | // let pick = Math.floor(pickt)
128 | // let pickNext = (pick + 1) % segments
129 |
130 | // this.binormal.subVectors(this.tubeGeometry.binormals[pickNext], this.tubeGeometry.binormals[pick])
131 | // this.binormal.multiplyScalar(pickt - pick).add(this.tubeGeometry.binormals[pick])
132 | // let dir = this.tubeGeometry.parameters.path.getTangentAt(t)
133 |
134 | // let offset = 0
135 |
136 | // this.normal.copy(this.binormal).cross(dir)
137 | // pos.add(this.normal.clone().multiplyScalar(offset))
138 | // this.camera.position.copy(pos)
139 |
140 | // let lookAt = this.tubeGeometry.parameters.path.getPointAt((t+1/this.tubeGeometry.parameters.path.getLength())%1)
141 | // this.camera.matrix.lookAt(this.camera.position, lookAt, this.normal)
142 | // this.camera.rotation.setFromRotationMatrix(this.camera.matrix, this.camera.rotation.order)
143 |
144 | // this.position += this.speed
145 | // this.position = this.position < 0 ? 0 : this.position
146 | // this.speed *= 0.9
147 |
148 | // this.planes.forEach(plane => {
149 | // plane.quaternion.copy(this.camera.quaternion)
150 | // })
151 |
152 |
153 | this.renderer.render(this.scene, this.camera)
154 | requestAnimationFrame(this._render.bind(this))
155 | }
156 |
157 | }
158 |
159 | const app = new App()
--------------------------------------------------------------------------------
/js/vendor/OrbitControls.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.OrbitControls: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author qiao / https://github.com/qiao
4 | * @author mrdoob / http://mrdoob.com
5 | * @author alteredq / http://alteredqualia.com/
6 | * @author WestLangley / http://github.com/WestLangley
7 | * @author erich666 / http://erichaines.com
8 | * @author ScieCode / http://github.com/sciecode
9 | */
10 |
11 | // This set of controls performs orbiting, dollying (zooming), and panning.
12 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
13 | //
14 | // Orbit - left mouse / touch: one-finger move
15 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
16 | // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
17 |
18 | THREE.OrbitControls = function ( object, domElement ) {
19 |
20 | if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
21 | if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
22 |
23 | this.object = object;
24 | this.domElement = domElement;
25 |
26 | // Set to false to disable this control
27 | this.enabled = true;
28 |
29 | // "target" sets the location of focus, where the object orbits around
30 | this.target = new THREE.Vector3();
31 |
32 | // How far you can dolly in and out ( PerspectiveCamera only )
33 | this.minDistance = 0;
34 | this.maxDistance = Infinity;
35 |
36 | // How far you can zoom in and out ( OrthographicCamera only )
37 | this.minZoom = 0;
38 | this.maxZoom = Infinity;
39 |
40 | // How far you can orbit vertically, upper and lower limits.
41 | // Range is 0 to Math.PI radians.
42 | this.minPolarAngle = 0; // radians
43 | this.maxPolarAngle = Math.PI; // radians
44 |
45 | // How far you can orbit horizontally, upper and lower limits.
46 | // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
47 | this.minAzimuthAngle = - Infinity; // radians
48 | this.maxAzimuthAngle = Infinity; // radians
49 |
50 | // Set to true to enable damping (inertia)
51 | // If damping is enabled, you must call controls.update() in your animation loop
52 | this.enableDamping = false;
53 | this.dampingFactor = 0.05;
54 |
55 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
56 | // Set to false to disable zooming
57 | this.enableZoom = true;
58 | this.zoomSpeed = 1.0;
59 |
60 | // Set to false to disable rotating
61 | this.enableRotate = true;
62 | this.rotateSpeed = 1.0;
63 |
64 | // Set to false to disable panning
65 | this.enablePan = true;
66 | this.panSpeed = 1.0;
67 | this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
68 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
69 |
70 | // Set to true to automatically rotate around the target
71 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
72 | this.autoRotate = false;
73 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
74 |
75 | // Set to false to disable use of the keys
76 | this.enableKeys = true;
77 |
78 | // The four arrow keys
79 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
80 |
81 | // Mouse buttons
82 | this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };
83 |
84 | // Touch fingers
85 | this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
86 |
87 | // for reset
88 | this.target0 = this.target.clone();
89 | this.position0 = this.object.position.clone();
90 | this.zoom0 = this.object.zoom;
91 |
92 | //
93 | // public methods
94 | //
95 |
96 | this.getPolarAngle = function () {
97 |
98 | return spherical.phi;
99 |
100 | };
101 |
102 | this.getAzimuthalAngle = function () {
103 |
104 | return spherical.theta;
105 |
106 | };
107 |
108 | this.saveState = function () {
109 |
110 | scope.target0.copy( scope.target );
111 | scope.position0.copy( scope.object.position );
112 | scope.zoom0 = scope.object.zoom;
113 |
114 | };
115 |
116 | this.reset = function () {
117 |
118 | scope.target.copy( scope.target0 );
119 | scope.object.position.copy( scope.position0 );
120 | scope.object.zoom = scope.zoom0;
121 |
122 | scope.object.updateProjectionMatrix();
123 | scope.dispatchEvent( changeEvent );
124 |
125 | scope.update();
126 |
127 | state = STATE.NONE;
128 |
129 | };
130 |
131 | // this method is exposed, but perhaps it would be better if we can make it private...
132 | this.update = function () {
133 |
134 | var offset = new THREE.Vector3();
135 |
136 | // so camera.up is the orbit axis
137 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
138 | var quatInverse = quat.clone().inverse();
139 |
140 | var lastPosition = new THREE.Vector3();
141 | var lastQuaternion = new THREE.Quaternion();
142 |
143 | var twoPI = 2 * Math.PI;
144 |
145 | return function update() {
146 |
147 | var position = scope.object.position;
148 |
149 | offset.copy( position ).sub( scope.target );
150 |
151 | // rotate offset to "y-axis-is-up" space
152 | offset.applyQuaternion( quat );
153 |
154 | // angle from z-axis around y-axis
155 | spherical.setFromVector3( offset );
156 |
157 | if ( scope.autoRotate && state === STATE.NONE ) {
158 |
159 | rotateLeft( getAutoRotationAngle() );
160 |
161 | }
162 |
163 | if ( scope.enableDamping ) {
164 |
165 | spherical.theta += sphericalDelta.theta * scope.dampingFactor;
166 | spherical.phi += sphericalDelta.phi * scope.dampingFactor;
167 |
168 | } else {
169 |
170 | spherical.theta += sphericalDelta.theta;
171 | spherical.phi += sphericalDelta.phi;
172 |
173 | }
174 |
175 | // restrict theta to be between desired limits
176 |
177 | var min = scope.minAzimuthAngle;
178 | var max = scope.maxAzimuthAngle;
179 |
180 | if ( isFinite( min ) && isFinite( max ) ) {
181 |
182 | if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;
183 |
184 | if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;
185 |
186 | if ( min < max ) {
187 |
188 | spherical.theta = Math.max( min, Math.min( max, spherical.theta ) );
189 |
190 | } else {
191 |
192 | spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ?
193 | Math.max( min, spherical.theta ) :
194 | Math.min( max, spherical.theta );
195 |
196 | }
197 |
198 | }
199 |
200 | // restrict phi to be between desired limits
201 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
202 |
203 | spherical.makeSafe();
204 |
205 |
206 | spherical.radius *= scale;
207 |
208 | // restrict radius to be between desired limits
209 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
210 |
211 | // move target to panned location
212 |
213 | if ( scope.enableDamping === true ) {
214 |
215 | scope.target.addScaledVector( panOffset, scope.dampingFactor );
216 |
217 | } else {
218 |
219 | scope.target.add( panOffset );
220 |
221 | }
222 |
223 | offset.setFromSpherical( spherical );
224 |
225 | // rotate offset back to "camera-up-vector-is-up" space
226 | offset.applyQuaternion( quatInverse );
227 |
228 | position.copy( scope.target ).add( offset );
229 |
230 | scope.object.lookAt( scope.target );
231 |
232 | if ( scope.enableDamping === true ) {
233 |
234 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
235 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
236 |
237 | panOffset.multiplyScalar( 1 - scope.dampingFactor );
238 |
239 | } else {
240 |
241 | sphericalDelta.set( 0, 0, 0 );
242 |
243 | panOffset.set( 0, 0, 0 );
244 |
245 | }
246 |
247 | scale = 1;
248 |
249 | // update condition is:
250 | // min(camera displacement, camera rotation in radians)^2 > EPS
251 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
252 |
253 | if ( zoomChanged ||
254 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
255 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
256 |
257 | scope.dispatchEvent( changeEvent );
258 |
259 | lastPosition.copy( scope.object.position );
260 | lastQuaternion.copy( scope.object.quaternion );
261 | zoomChanged = false;
262 |
263 | return true;
264 |
265 | }
266 |
267 | return false;
268 |
269 | };
270 |
271 | }();
272 |
273 | this.dispose = function () {
274 |
275 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
276 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
277 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
278 |
279 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
280 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
281 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
282 |
283 | scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove, false );
284 | scope.domElement.ownerDocument.removeEventListener( 'mouseup', onMouseUp, false );
285 |
286 | scope.domElement.removeEventListener( 'keydown', onKeyDown, false );
287 |
288 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
289 |
290 | };
291 |
292 | //
293 | // internals
294 | //
295 |
296 | var scope = this;
297 |
298 | var changeEvent = { type: 'change' };
299 | var startEvent = { type: 'start' };
300 | var endEvent = { type: 'end' };
301 |
302 | var STATE = {
303 | NONE: - 1,
304 | ROTATE: 0,
305 | DOLLY: 1,
306 | PAN: 2,
307 | TOUCH_ROTATE: 3,
308 | TOUCH_PAN: 4,
309 | TOUCH_DOLLY_PAN: 5,
310 | TOUCH_DOLLY_ROTATE: 6
311 | };
312 |
313 | var state = STATE.NONE;
314 |
315 | var EPS = 0.000001;
316 |
317 | // current position in spherical coordinates
318 | var spherical = new THREE.Spherical();
319 | var sphericalDelta = new THREE.Spherical();
320 |
321 | var scale = 1;
322 | var panOffset = new THREE.Vector3();
323 | var zoomChanged = false;
324 |
325 | var rotateStart = new THREE.Vector2();
326 | var rotateEnd = new THREE.Vector2();
327 | var rotateDelta = new THREE.Vector2();
328 |
329 | var panStart = new THREE.Vector2();
330 | var panEnd = new THREE.Vector2();
331 | var panDelta = new THREE.Vector2();
332 |
333 | var dollyStart = new THREE.Vector2();
334 | var dollyEnd = new THREE.Vector2();
335 | var dollyDelta = new THREE.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 THREE.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 THREE.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 THREE.Vector3();
405 |
406 | return function pan( deltaX, deltaY ) {
407 |
408 | var element = 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 dollyOut( dollyScale ) {
443 |
444 | if ( scope.object.isPerspectiveCamera ) {
445 |
446 | scale /= dollyScale;
447 |
448 | } else if ( scope.object.isOrthographicCamera ) {
449 |
450 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
451 | scope.object.updateProjectionMatrix();
452 | zoomChanged = true;
453 |
454 | } else {
455 |
456 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
457 | scope.enableZoom = false;
458 |
459 | }
460 |
461 | }
462 |
463 | function dollyIn( dollyScale ) {
464 |
465 | if ( scope.object.isPerspectiveCamera ) {
466 |
467 | scale *= dollyScale;
468 |
469 | } else if ( scope.object.isOrthographicCamera ) {
470 |
471 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
472 | scope.object.updateProjectionMatrix();
473 | zoomChanged = true;
474 |
475 | } else {
476 |
477 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
478 | scope.enableZoom = false;
479 |
480 | }
481 |
482 | }
483 |
484 | //
485 | // event callbacks - update the object state
486 | //
487 |
488 | function handleMouseDownRotate( event ) {
489 |
490 | rotateStart.set( event.clientX, event.clientY );
491 |
492 | }
493 |
494 | function handleMouseDownDolly( event ) {
495 |
496 | dollyStart.set( event.clientX, event.clientY );
497 |
498 | }
499 |
500 | function handleMouseDownPan( event ) {
501 |
502 | panStart.set( event.clientX, event.clientY );
503 |
504 | }
505 |
506 | function handleMouseMoveRotate( event ) {
507 |
508 | rotateEnd.set( event.clientX, event.clientY );
509 |
510 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
511 |
512 | var element = scope.domElement;
513 |
514 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
515 |
516 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
517 |
518 | rotateStart.copy( rotateEnd );
519 |
520 | scope.update();
521 |
522 | }
523 |
524 | function handleMouseMoveDolly( event ) {
525 |
526 | dollyEnd.set( event.clientX, event.clientY );
527 |
528 | dollyDelta.subVectors( dollyEnd, dollyStart );
529 |
530 | if ( dollyDelta.y > 0 ) {
531 |
532 | dollyOut( getZoomScale() );
533 |
534 | } else if ( dollyDelta.y < 0 ) {
535 |
536 | dollyIn( getZoomScale() );
537 |
538 | }
539 |
540 | dollyStart.copy( dollyEnd );
541 |
542 | scope.update();
543 |
544 | }
545 |
546 | function handleMouseMovePan( event ) {
547 |
548 | panEnd.set( event.clientX, event.clientY );
549 |
550 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
551 |
552 | pan( panDelta.x, panDelta.y );
553 |
554 | panStart.copy( panEnd );
555 |
556 | scope.update();
557 |
558 | }
559 |
560 | function handleMouseUp( /*event*/ ) {
561 |
562 | // no-op
563 |
564 | }
565 |
566 | function handleMouseWheel( event ) {
567 |
568 | if ( event.deltaY < 0 ) {
569 |
570 | dollyIn( getZoomScale() );
571 |
572 | } else if ( event.deltaY > 0 ) {
573 |
574 | dollyOut( getZoomScale() );
575 |
576 | }
577 |
578 | scope.update();
579 |
580 | }
581 |
582 | function handleKeyDown( event ) {
583 |
584 | var needsUpdate = false;
585 |
586 | switch ( event.keyCode ) {
587 |
588 | case scope.keys.UP:
589 | pan( 0, scope.keyPanSpeed );
590 | needsUpdate = true;
591 | break;
592 |
593 | case scope.keys.BOTTOM:
594 | pan( 0, - scope.keyPanSpeed );
595 | needsUpdate = true;
596 | break;
597 |
598 | case scope.keys.LEFT:
599 | pan( scope.keyPanSpeed, 0 );
600 | needsUpdate = true;
601 | break;
602 |
603 | case scope.keys.RIGHT:
604 | pan( - scope.keyPanSpeed, 0 );
605 | needsUpdate = true;
606 | break;
607 |
608 | }
609 |
610 | if ( needsUpdate ) {
611 |
612 | // prevent the browser from scrolling on cursor keys
613 | event.preventDefault();
614 |
615 | scope.update();
616 |
617 | }
618 |
619 |
620 | }
621 |
622 | function handleTouchStartRotate( event ) {
623 |
624 | if ( event.touches.length == 1 ) {
625 |
626 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
627 |
628 | } else {
629 |
630 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
631 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
632 |
633 | rotateStart.set( x, y );
634 |
635 | }
636 |
637 | }
638 |
639 | function handleTouchStartPan( event ) {
640 |
641 | if ( event.touches.length == 1 ) {
642 |
643 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
644 |
645 | } else {
646 |
647 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
648 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
649 |
650 | panStart.set( x, y );
651 |
652 | }
653 |
654 | }
655 |
656 | function handleTouchStartDolly( event ) {
657 |
658 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
659 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
660 |
661 | var distance = Math.sqrt( dx * dx + dy * dy );
662 |
663 | dollyStart.set( 0, distance );
664 |
665 | }
666 |
667 | function handleTouchStartDollyPan( event ) {
668 |
669 | if ( scope.enableZoom ) handleTouchStartDolly( event );
670 |
671 | if ( scope.enablePan ) handleTouchStartPan( event );
672 |
673 | }
674 |
675 | function handleTouchStartDollyRotate( event ) {
676 |
677 | if ( scope.enableZoom ) handleTouchStartDolly( event );
678 |
679 | if ( scope.enableRotate ) handleTouchStartRotate( event );
680 |
681 | }
682 |
683 | function handleTouchMoveRotate( event ) {
684 |
685 | if ( event.touches.length == 1 ) {
686 |
687 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
688 |
689 | } else {
690 |
691 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
692 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
693 |
694 | rotateEnd.set( x, y );
695 |
696 | }
697 |
698 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
699 |
700 | var element = scope.domElement;
701 |
702 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
703 |
704 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
705 |
706 | rotateStart.copy( rotateEnd );
707 |
708 | }
709 |
710 | function handleTouchMovePan( event ) {
711 |
712 | if ( event.touches.length == 1 ) {
713 |
714 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
715 |
716 | } else {
717 |
718 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
719 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
720 |
721 | panEnd.set( x, y );
722 |
723 | }
724 |
725 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
726 |
727 | pan( panDelta.x, panDelta.y );
728 |
729 | panStart.copy( panEnd );
730 |
731 | }
732 |
733 | function handleTouchMoveDolly( event ) {
734 |
735 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
736 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
737 |
738 | var distance = Math.sqrt( dx * dx + dy * dy );
739 |
740 | dollyEnd.set( 0, distance );
741 |
742 | dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
743 |
744 | dollyOut( dollyDelta.y );
745 |
746 | dollyStart.copy( dollyEnd );
747 |
748 | }
749 |
750 | function handleTouchMoveDollyPan( event ) {
751 |
752 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
753 |
754 | if ( scope.enablePan ) handleTouchMovePan( event );
755 |
756 | }
757 |
758 | function handleTouchMoveDollyRotate( event ) {
759 |
760 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
761 |
762 | if ( scope.enableRotate ) handleTouchMoveRotate( event );
763 |
764 | }
765 |
766 | function handleTouchEnd( /*event*/ ) {
767 |
768 | // no-op
769 |
770 | }
771 |
772 | //
773 | // event handlers - FSM: listen for events and reset state
774 | //
775 |
776 | function onMouseDown( event ) {
777 |
778 | if ( scope.enabled === false ) return;
779 |
780 | // Prevent the browser from scrolling.
781 | event.preventDefault();
782 |
783 | // Manually set the focus since calling preventDefault above
784 | // prevents the browser from setting it automatically.
785 |
786 | scope.domElement.focus ? scope.domElement.focus() : window.focus();
787 |
788 | var mouseAction;
789 |
790 | switch ( event.button ) {
791 |
792 | case 0:
793 |
794 | mouseAction = scope.mouseButtons.LEFT;
795 | break;
796 |
797 | case 1:
798 |
799 | mouseAction = scope.mouseButtons.MIDDLE;
800 | break;
801 |
802 | case 2:
803 |
804 | mouseAction = scope.mouseButtons.RIGHT;
805 | break;
806 |
807 | default:
808 |
809 | mouseAction = - 1;
810 |
811 | }
812 |
813 | switch ( mouseAction ) {
814 |
815 | case THREE.MOUSE.DOLLY:
816 |
817 | if ( scope.enableZoom === false ) return;
818 |
819 | handleMouseDownDolly( event );
820 |
821 | state = STATE.DOLLY;
822 |
823 | break;
824 |
825 | case THREE.MOUSE.ROTATE:
826 |
827 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
828 |
829 | if ( scope.enablePan === false ) return;
830 |
831 | handleMouseDownPan( event );
832 |
833 | state = STATE.PAN;
834 |
835 | } else {
836 |
837 | if ( scope.enableRotate === false ) return;
838 |
839 | handleMouseDownRotate( event );
840 |
841 | state = STATE.ROTATE;
842 |
843 | }
844 |
845 | break;
846 |
847 | case THREE.MOUSE.PAN:
848 |
849 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
850 |
851 | if ( scope.enableRotate === false ) return;
852 |
853 | handleMouseDownRotate( event );
854 |
855 | state = STATE.ROTATE;
856 |
857 | } else {
858 |
859 | if ( scope.enablePan === false ) return;
860 |
861 | handleMouseDownPan( event );
862 |
863 | state = STATE.PAN;
864 |
865 | }
866 |
867 | break;
868 |
869 | default:
870 |
871 | state = STATE.NONE;
872 |
873 | }
874 |
875 | if ( state !== STATE.NONE ) {
876 |
877 | scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove, false );
878 | scope.domElement.ownerDocument.addEventListener( 'mouseup', onMouseUp, false );
879 |
880 | scope.dispatchEvent( startEvent );
881 |
882 | }
883 |
884 | }
885 |
886 | function onMouseMove( event ) {
887 |
888 | if ( scope.enabled === false ) return;
889 |
890 | event.preventDefault();
891 |
892 | switch ( state ) {
893 |
894 | case STATE.ROTATE:
895 |
896 | if ( scope.enableRotate === false ) return;
897 |
898 | handleMouseMoveRotate( event );
899 |
900 | break;
901 |
902 | case STATE.DOLLY:
903 |
904 | if ( scope.enableZoom === false ) return;
905 |
906 | handleMouseMoveDolly( event );
907 |
908 | break;
909 |
910 | case STATE.PAN:
911 |
912 | if ( scope.enablePan === false ) return;
913 |
914 | handleMouseMovePan( event );
915 |
916 | break;
917 |
918 | }
919 |
920 | }
921 |
922 | function onMouseUp( event ) {
923 |
924 | if ( scope.enabled === false ) return;
925 |
926 | handleMouseUp( event );
927 |
928 | scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove, false );
929 | scope.domElement.ownerDocument.removeEventListener( 'mouseup', onMouseUp, false );
930 |
931 | scope.dispatchEvent( endEvent );
932 |
933 | state = STATE.NONE;
934 |
935 | }
936 |
937 | function onMouseWheel( event ) {
938 |
939 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
940 |
941 | event.preventDefault();
942 | event.stopPropagation();
943 |
944 | scope.dispatchEvent( startEvent );
945 |
946 | handleMouseWheel( event );
947 |
948 | scope.dispatchEvent( endEvent );
949 |
950 | }
951 |
952 | function onKeyDown( event ) {
953 |
954 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
955 |
956 | handleKeyDown( event );
957 |
958 | }
959 |
960 | function onTouchStart( event ) {
961 |
962 | if ( scope.enabled === false ) return;
963 |
964 | event.preventDefault(); // prevent scrolling
965 |
966 | switch ( event.touches.length ) {
967 |
968 | case 1:
969 |
970 | switch ( scope.touches.ONE ) {
971 |
972 | case THREE.TOUCH.ROTATE:
973 |
974 | if ( scope.enableRotate === false ) return;
975 |
976 | handleTouchStartRotate( event );
977 |
978 | state = STATE.TOUCH_ROTATE;
979 |
980 | break;
981 |
982 | case THREE.TOUCH.PAN:
983 |
984 | if ( scope.enablePan === false ) return;
985 |
986 | handleTouchStartPan( event );
987 |
988 | state = STATE.TOUCH_PAN;
989 |
990 | break;
991 |
992 | default:
993 |
994 | state = STATE.NONE;
995 |
996 | }
997 |
998 | break;
999 |
1000 | case 2:
1001 |
1002 | switch ( scope.touches.TWO ) {
1003 |
1004 | case THREE.TOUCH.DOLLY_PAN:
1005 |
1006 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1007 |
1008 | handleTouchStartDollyPan( event );
1009 |
1010 | state = STATE.TOUCH_DOLLY_PAN;
1011 |
1012 | break;
1013 |
1014 | case THREE.TOUCH.DOLLY_ROTATE:
1015 |
1016 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1017 |
1018 | handleTouchStartDollyRotate( event );
1019 |
1020 | state = STATE.TOUCH_DOLLY_ROTATE;
1021 |
1022 | break;
1023 |
1024 | default:
1025 |
1026 | state = STATE.NONE;
1027 |
1028 | }
1029 |
1030 | break;
1031 |
1032 | default:
1033 |
1034 | state = STATE.NONE;
1035 |
1036 | }
1037 |
1038 | if ( state !== STATE.NONE ) {
1039 |
1040 | scope.dispatchEvent( startEvent );
1041 |
1042 | }
1043 |
1044 | }
1045 |
1046 | function onTouchMove( event ) {
1047 |
1048 | if ( scope.enabled === false ) return;
1049 |
1050 | event.preventDefault(); // prevent scrolling
1051 | event.stopPropagation();
1052 |
1053 | switch ( state ) {
1054 |
1055 | case STATE.TOUCH_ROTATE:
1056 |
1057 | if ( scope.enableRotate === false ) return;
1058 |
1059 | handleTouchMoveRotate( event );
1060 |
1061 | scope.update();
1062 |
1063 | break;
1064 |
1065 | case STATE.TOUCH_PAN:
1066 |
1067 | if ( scope.enablePan === false ) return;
1068 |
1069 | handleTouchMovePan( event );
1070 |
1071 | scope.update();
1072 |
1073 | break;
1074 |
1075 | case STATE.TOUCH_DOLLY_PAN:
1076 |
1077 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1078 |
1079 | handleTouchMoveDollyPan( event );
1080 |
1081 | scope.update();
1082 |
1083 | break;
1084 |
1085 | case STATE.TOUCH_DOLLY_ROTATE:
1086 |
1087 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1088 |
1089 | handleTouchMoveDollyRotate( event );
1090 |
1091 | scope.update();
1092 |
1093 | break;
1094 |
1095 | default:
1096 |
1097 | state = STATE.NONE;
1098 |
1099 | }
1100 |
1101 | }
1102 |
1103 | function onTouchEnd( event ) {
1104 |
1105 | if ( scope.enabled === false ) return;
1106 |
1107 | handleTouchEnd( event );
1108 |
1109 | scope.dispatchEvent( endEvent );
1110 |
1111 | state = STATE.NONE;
1112 |
1113 | }
1114 |
1115 | function onContextMenu( event ) {
1116 |
1117 | if ( scope.enabled === false ) return;
1118 |
1119 | event.preventDefault();
1120 |
1121 | }
1122 |
1123 | //
1124 |
1125 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
1126 |
1127 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
1128 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
1129 |
1130 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
1131 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
1132 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
1133 |
1134 | scope.domElement.addEventListener( 'keydown', onKeyDown, false );
1135 |
1136 | // make sure element can receive keys.
1137 |
1138 | if ( scope.domElement.tabIndex === - 1 ) {
1139 |
1140 | scope.domElement.tabIndex = 0;
1141 |
1142 | }
1143 |
1144 | // force an update at start
1145 |
1146 | this.update();
1147 |
1148 | };
1149 |
1150 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1151 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
1152 |
1153 |
1154 | // This set of controls performs orbiting, dollying (zooming), and panning.
1155 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
1156 | // This is very similar to OrbitControls, another set of touch behavior
1157 | //
1158 | // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
1159 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
1160 | // Pan - left mouse, or arrow keys / touch: one-finger move
1161 |
1162 | THREE.MapControls = function ( object, domElement ) {
1163 |
1164 | THREE.OrbitControls.call( this, object, domElement );
1165 |
1166 | this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
1167 |
1168 | this.mouseButtons.LEFT = THREE.MOUSE.PAN;
1169 | this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
1170 |
1171 | this.touches.ONE = THREE.TOUCH.PAN;
1172 | this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
1173 |
1174 | };
1175 |
1176 | THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1177 | THREE.MapControls.prototype.constructor = THREE.MapControls;
1178 |
--------------------------------------------------------------------------------
/js/vendor/postprocessing/BloomPass.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.BloomPass: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author alteredq / http://alteredqualia.com/
4 | */
5 |
6 | THREE.BloomPass = function ( strength, kernelSize, sigma, resolution ) {
7 |
8 | THREE.Pass.call( this );
9 |
10 | strength = ( strength !== undefined ) ? strength : 1;
11 | kernelSize = ( kernelSize !== undefined ) ? kernelSize : 25;
12 | sigma = ( sigma !== undefined ) ? sigma : 4.0;
13 | resolution = ( resolution !== undefined ) ? resolution : 256;
14 |
15 | // render targets
16 |
17 | var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat };
18 |
19 | this.renderTargetX = new THREE.WebGLRenderTarget( resolution, resolution, pars );
20 | this.renderTargetX.texture.name = "BloomPass.x";
21 | this.renderTargetY = new THREE.WebGLRenderTarget( resolution, resolution, pars );
22 | this.renderTargetY.texture.name = "BloomPass.y";
23 |
24 | // copy material
25 |
26 | if ( THREE.CopyShader === undefined )
27 | console.error( "THREE.BloomPass relies on THREE.CopyShader" );
28 |
29 | var copyShader = THREE.CopyShader;
30 |
31 | this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
32 |
33 | this.copyUniforms[ "opacity" ].value = strength;
34 |
35 | this.materialCopy = new THREE.ShaderMaterial( {
36 |
37 | uniforms: this.copyUniforms,
38 | vertexShader: copyShader.vertexShader,
39 | fragmentShader: copyShader.fragmentShader,
40 | blending: THREE.AdditiveBlending,
41 | transparent: true
42 |
43 | } );
44 |
45 | // convolution material
46 |
47 | if ( THREE.ConvolutionShader === undefined )
48 | console.error( "THREE.BloomPass relies on THREE.ConvolutionShader" );
49 |
50 | var convolutionShader = THREE.ConvolutionShader;
51 |
52 | this.convolutionUniforms = THREE.UniformsUtils.clone( convolutionShader.uniforms );
53 |
54 | this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurX;
55 | this.convolutionUniforms[ "cKernel" ].value = THREE.ConvolutionShader.buildKernel( sigma );
56 |
57 | this.materialConvolution = new THREE.ShaderMaterial( {
58 |
59 | uniforms: this.convolutionUniforms,
60 | vertexShader: convolutionShader.vertexShader,
61 | fragmentShader: convolutionShader.fragmentShader,
62 | defines: {
63 | "KERNEL_SIZE_FLOAT": kernelSize.toFixed( 1 ),
64 | "KERNEL_SIZE_INT": kernelSize.toFixed( 0 )
65 | }
66 |
67 | } );
68 |
69 | this.needsSwap = false;
70 |
71 | this.fsQuad = new THREE.Pass.FullScreenQuad( null );
72 |
73 | };
74 |
75 | THREE.BloomPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
76 |
77 | constructor: THREE.BloomPass,
78 |
79 | render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
80 |
81 | if ( maskActive ) renderer.state.buffers.stencil.setTest( false );
82 |
83 | // Render quad with blured scene into texture (convolution pass 1)
84 |
85 | this.fsQuad.material = this.materialConvolution;
86 |
87 | this.convolutionUniforms[ "tDiffuse" ].value = readBuffer.texture;
88 | this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurX;
89 |
90 | renderer.setRenderTarget( this.renderTargetX );
91 | renderer.clear();
92 | this.fsQuad.render( renderer );
93 |
94 |
95 | // Render quad with blured scene into texture (convolution pass 2)
96 |
97 | this.convolutionUniforms[ "tDiffuse" ].value = this.renderTargetX.texture;
98 | this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurY;
99 |
100 | renderer.setRenderTarget( this.renderTargetY );
101 | renderer.clear();
102 | this.fsQuad.render( renderer );
103 |
104 | // Render original scene with superimposed blur to texture
105 |
106 | this.fsQuad.material = this.materialCopy;
107 |
108 | this.copyUniforms[ "tDiffuse" ].value = this.renderTargetY.texture;
109 |
110 | if ( maskActive ) renderer.state.buffers.stencil.setTest( true );
111 |
112 | renderer.setRenderTarget( readBuffer );
113 | if ( this.clear ) renderer.clear();
114 | this.fsQuad.render( renderer );
115 |
116 | }
117 |
118 | } );
119 |
120 | THREE.BloomPass.blurX = new THREE.Vector2( 0.001953125, 0.0 );
121 | THREE.BloomPass.blurY = new THREE.Vector2( 0.0, 0.001953125 );
122 |
--------------------------------------------------------------------------------
/js/vendor/postprocessing/EffectComposer.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.EffectComposer: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author alteredq / http://alteredqualia.com/
4 | */
5 |
6 | THREE.EffectComposer = function ( renderer, renderTarget ) {
7 |
8 | this.renderer = renderer;
9 |
10 | if ( renderTarget === undefined ) {
11 |
12 | var parameters = {
13 | minFilter: THREE.LinearFilter,
14 | magFilter: THREE.LinearFilter,
15 | format: THREE.RGBAFormat,
16 | stencilBuffer: false
17 | };
18 |
19 | var size = renderer.getSize( new THREE.Vector2() );
20 | this._pixelRatio = renderer.getPixelRatio();
21 | this._width = size.width;
22 | this._height = size.height;
23 |
24 | renderTarget = new THREE.WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, parameters );
25 | renderTarget.texture.name = 'EffectComposer.rt1';
26 |
27 | } else {
28 |
29 | this._pixelRatio = 1;
30 | this._width = renderTarget.width;
31 | this._height = renderTarget.height;
32 |
33 | }
34 |
35 | this.renderTarget1 = renderTarget;
36 | this.renderTarget2 = renderTarget.clone();
37 | this.renderTarget2.texture.name = 'EffectComposer.rt2';
38 |
39 | this.writeBuffer = this.renderTarget1;
40 | this.readBuffer = this.renderTarget2;
41 |
42 | this.renderToScreen = true;
43 |
44 | this.passes = [];
45 |
46 | // dependencies
47 |
48 | if ( THREE.CopyShader === undefined ) {
49 |
50 | console.error( 'THREE.EffectComposer relies on THREE.CopyShader' );
51 |
52 | }
53 |
54 | if ( THREE.ShaderPass === undefined ) {
55 |
56 | console.error( 'THREE.EffectComposer relies on THREE.ShaderPass' );
57 |
58 | }
59 |
60 | this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
61 |
62 | this.clock = new THREE.Clock();
63 |
64 | };
65 |
66 | Object.assign( THREE.EffectComposer.prototype, {
67 |
68 | swapBuffers: function () {
69 |
70 | var tmp = this.readBuffer;
71 | this.readBuffer = this.writeBuffer;
72 | this.writeBuffer = tmp;
73 |
74 | },
75 |
76 | addPass: function ( pass ) {
77 |
78 | this.passes.push( pass );
79 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
80 |
81 | },
82 |
83 | insertPass: function ( pass, index ) {
84 |
85 | this.passes.splice( index, 0, pass );
86 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
87 |
88 | },
89 |
90 | isLastEnabledPass: function ( passIndex ) {
91 |
92 | for ( var i = passIndex + 1; i < this.passes.length; i ++ ) {
93 |
94 | if ( this.passes[ i ].enabled ) {
95 |
96 | return false;
97 |
98 | }
99 |
100 | }
101 |
102 | return true;
103 |
104 | },
105 |
106 | render: function ( deltaTime ) {
107 |
108 | // deltaTime value is in seconds
109 |
110 | if ( deltaTime === undefined ) {
111 |
112 | deltaTime = this.clock.getDelta();
113 |
114 | }
115 |
116 | var currentRenderTarget = this.renderer.getRenderTarget();
117 |
118 | var maskActive = false;
119 |
120 | var pass, i, il = this.passes.length;
121 |
122 | for ( i = 0; i < il; i ++ ) {
123 |
124 | pass = this.passes[ i ];
125 |
126 | if ( pass.enabled === false ) continue;
127 |
128 | pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) );
129 | pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive );
130 |
131 | if ( pass.needsSwap ) {
132 |
133 | if ( maskActive ) {
134 |
135 | var context = this.renderer.getContext();
136 | var stencil = this.renderer.state.buffers.stencil;
137 |
138 | //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
139 | stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff );
140 |
141 | this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime );
142 |
143 | //context.stencilFunc( context.EQUAL, 1, 0xffffffff );
144 | stencil.setFunc( context.EQUAL, 1, 0xffffffff );
145 |
146 | }
147 |
148 | this.swapBuffers();
149 |
150 | }
151 |
152 | if ( THREE.MaskPass !== undefined ) {
153 |
154 | if ( pass instanceof THREE.MaskPass ) {
155 |
156 | maskActive = true;
157 |
158 | } else if ( pass instanceof THREE.ClearMaskPass ) {
159 |
160 | maskActive = false;
161 |
162 | }
163 |
164 | }
165 |
166 | }
167 |
168 | this.renderer.setRenderTarget( currentRenderTarget );
169 |
170 | },
171 |
172 | reset: function ( renderTarget ) {
173 |
174 | if ( renderTarget === undefined ) {
175 |
176 | var size = this.renderer.getSize( new THREE.Vector2() );
177 | this._pixelRatio = this.renderer.getPixelRatio();
178 | this._width = size.width;
179 | this._height = size.height;
180 |
181 | renderTarget = this.renderTarget1.clone();
182 | renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
183 |
184 | }
185 |
186 | this.renderTarget1.dispose();
187 | this.renderTarget2.dispose();
188 | this.renderTarget1 = renderTarget;
189 | this.renderTarget2 = renderTarget.clone();
190 |
191 | this.writeBuffer = this.renderTarget1;
192 | this.readBuffer = this.renderTarget2;
193 |
194 | },
195 |
196 | setSize: function ( width, height ) {
197 |
198 | this._width = width;
199 | this._height = height;
200 |
201 | var effectiveWidth = this._width * this._pixelRatio;
202 | var effectiveHeight = this._height * this._pixelRatio;
203 |
204 | this.renderTarget1.setSize( effectiveWidth, effectiveHeight );
205 | this.renderTarget2.setSize( effectiveWidth, effectiveHeight );
206 |
207 | for ( var i = 0; i < this.passes.length; i ++ ) {
208 |
209 | this.passes[ i ].setSize( effectiveWidth, effectiveHeight );
210 |
211 | }
212 |
213 | },
214 |
215 | setPixelRatio: function ( pixelRatio ) {
216 |
217 | this._pixelRatio = pixelRatio;
218 |
219 | this.setSize( this._width, this._height );
220 |
221 | }
222 |
223 | } );
224 |
225 |
226 | THREE.Pass = function () {
227 |
228 | // if set to true, the pass is processed by the composer
229 | this.enabled = true;
230 |
231 | // if set to true, the pass indicates to swap read and write buffer after rendering
232 | this.needsSwap = true;
233 |
234 | // if set to true, the pass clears its buffer before rendering
235 | this.clear = false;
236 |
237 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
238 | this.renderToScreen = false;
239 |
240 | };
241 |
242 | Object.assign( THREE.Pass.prototype, {
243 |
244 | setSize: function ( /* width, height */ ) {},
245 |
246 | render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
247 |
248 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
249 |
250 | }
251 |
252 | } );
253 |
254 | // Helper for passes that need to fill the viewport with a single quad.
255 | THREE.Pass.FullScreenQuad = ( function () {
256 |
257 | var camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
258 | var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
259 |
260 | var FullScreenQuad = function ( material ) {
261 |
262 | this._mesh = new THREE.Mesh( geometry, material );
263 |
264 | };
265 |
266 | Object.defineProperty( FullScreenQuad.prototype, 'material', {
267 |
268 | get: function () {
269 |
270 | return this._mesh.material;
271 |
272 | },
273 |
274 | set: function ( value ) {
275 |
276 | this._mesh.material = value;
277 |
278 | }
279 |
280 | } );
281 |
282 | Object.assign( FullScreenQuad.prototype, {
283 |
284 | dispose: function () {
285 |
286 | this._mesh.geometry.dispose();
287 |
288 | },
289 |
290 | render: function ( renderer ) {
291 |
292 | renderer.render( this._mesh, camera );
293 |
294 | }
295 |
296 | } );
297 |
298 | return FullScreenQuad;
299 |
300 | } )();
301 |
--------------------------------------------------------------------------------
/js/vendor/postprocessing/RenderPass.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.RenderPass: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author alteredq / http://alteredqualia.com/
4 | */
5 |
6 |
7 | THREE.RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
8 |
9 | THREE.Pass.call( this );
10 |
11 | this.scene = scene;
12 | this.camera = camera;
13 |
14 | this.overrideMaterial = overrideMaterial;
15 |
16 | this.clearColor = clearColor;
17 | this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
18 |
19 | this.clear = true;
20 | this.clearDepth = false;
21 | this.needsSwap = false;
22 |
23 | };
24 |
25 | THREE.RenderPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
26 |
27 | constructor: THREE.RenderPass,
28 |
29 | render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
30 |
31 | var oldAutoClear = renderer.autoClear;
32 | renderer.autoClear = false;
33 |
34 | var oldClearColor, oldClearAlpha, oldOverrideMaterial;
35 |
36 | if ( this.overrideMaterial !== undefined ) {
37 |
38 | oldOverrideMaterial = this.scene.overrideMaterial;
39 |
40 | this.scene.overrideMaterial = this.overrideMaterial;
41 |
42 | }
43 |
44 | if ( this.clearColor ) {
45 |
46 | oldClearColor = renderer.getClearColor().getHex();
47 | oldClearAlpha = renderer.getClearAlpha();
48 |
49 | renderer.setClearColor( this.clearColor, this.clearAlpha );
50 |
51 | }
52 |
53 | if ( this.clearDepth ) {
54 |
55 | renderer.clearDepth();
56 |
57 | }
58 |
59 | renderer.setRenderTarget( this.renderToScreen ? null : readBuffer );
60 |
61 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
62 | if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
63 | renderer.render( this.scene, this.camera );
64 |
65 | if ( this.clearColor ) {
66 |
67 | renderer.setClearColor( oldClearColor, oldClearAlpha );
68 |
69 | }
70 |
71 | if ( this.overrideMaterial !== undefined ) {
72 |
73 | this.scene.overrideMaterial = oldOverrideMaterial;
74 |
75 | }
76 |
77 | renderer.autoClear = oldAutoClear;
78 |
79 | }
80 |
81 | } );
82 |
--------------------------------------------------------------------------------
/js/vendor/postprocessing/ShaderPass.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.ShaderPass: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author alteredq / http://alteredqualia.com/
4 | */
5 |
6 | THREE.ShaderPass = function ( shader, textureID ) {
7 |
8 | THREE.Pass.call( this );
9 |
10 | this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
11 |
12 | if ( shader instanceof THREE.ShaderMaterial ) {
13 |
14 | this.uniforms = shader.uniforms;
15 |
16 | this.material = shader;
17 |
18 | } else if ( shader ) {
19 |
20 | this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
21 |
22 | this.material = new THREE.ShaderMaterial( {
23 |
24 | defines: Object.assign( {}, shader.defines ),
25 | uniforms: this.uniforms,
26 | vertexShader: shader.vertexShader,
27 | fragmentShader: shader.fragmentShader
28 |
29 | } );
30 |
31 | }
32 |
33 | this.fsQuad = new THREE.Pass.FullScreenQuad( this.material );
34 |
35 | };
36 |
37 | THREE.ShaderPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
38 |
39 | constructor: THREE.ShaderPass,
40 |
41 | render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
42 |
43 | if ( this.uniforms[ this.textureID ] ) {
44 |
45 | this.uniforms[ this.textureID ].value = readBuffer.texture;
46 |
47 | }
48 |
49 | this.fsQuad.material = this.material;
50 |
51 | if ( this.renderToScreen ) {
52 |
53 | renderer.setRenderTarget( null );
54 | this.fsQuad.render( renderer );
55 |
56 | } else {
57 |
58 | renderer.setRenderTarget( writeBuffer );
59 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
60 | if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
61 | this.fsQuad.render( renderer );
62 |
63 | }
64 |
65 | }
66 |
67 | } );
68 |
--------------------------------------------------------------------------------
/js/vendor/postprocessing/UnrealBloomPass.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.UnrealBloomPass: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author spidersharma / http://eduperiment.com/
4 | */
5 |
6 | /**
7 | * UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a
8 | * mip map chain of bloom textures and blurs them with different radii. Because
9 | * of the weighted combination of mips, and because larger blurs are done on
10 | * higher mips, this effect provides good quality and performance.
11 | *
12 | * Reference:
13 | * - https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
14 | */
15 | THREE.UnrealBloomPass = function ( resolution, strength, radius, threshold ) {
16 |
17 | THREE.Pass.call( this );
18 |
19 | this.strength = ( strength !== undefined ) ? strength : 1;
20 | this.radius = radius;
21 | this.threshold = threshold;
22 | this.resolution = ( resolution !== undefined ) ? new THREE.Vector2( resolution.x, resolution.y ) : new THREE.Vector2( 256, 256 );
23 |
24 | // create color only once here, reuse it later inside the render function
25 | this.clearColor = new THREE.Color( 0, 0, 0 );
26 |
27 | // render targets
28 | var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat };
29 | this.renderTargetsHorizontal = [];
30 | this.renderTargetsVertical = [];
31 | this.nMips = 5;
32 | var resx = Math.round( this.resolution.x / 2 );
33 | var resy = Math.round( this.resolution.y / 2 );
34 |
35 | this.renderTargetBright = new THREE.WebGLRenderTarget( resx, resy, pars );
36 | this.renderTargetBright.texture.name = "UnrealBloomPass.bright";
37 | this.renderTargetBright.texture.generateMipmaps = false;
38 |
39 | for ( var i = 0; i < this.nMips; i ++ ) {
40 |
41 | var renderTargetHorizonal = new THREE.WebGLRenderTarget( resx, resy, pars );
42 |
43 | renderTargetHorizonal.texture.name = "UnrealBloomPass.h" + i;
44 | renderTargetHorizonal.texture.generateMipmaps = false;
45 |
46 | this.renderTargetsHorizontal.push( renderTargetHorizonal );
47 |
48 | var renderTargetVertical = new THREE.WebGLRenderTarget( resx, resy, pars );
49 |
50 | renderTargetVertical.texture.name = "UnrealBloomPass.v" + i;
51 | renderTargetVertical.texture.generateMipmaps = false;
52 |
53 | this.renderTargetsVertical.push( renderTargetVertical );
54 |
55 | resx = Math.round( resx / 2 );
56 |
57 | resy = Math.round( resy / 2 );
58 |
59 | }
60 |
61 | // luminosity high pass material
62 |
63 | if ( THREE.LuminosityHighPassShader === undefined )
64 | console.error( "THREE.UnrealBloomPass relies on THREE.LuminosityHighPassShader" );
65 |
66 | var highPassShader = THREE.LuminosityHighPassShader;
67 | this.highPassUniforms = THREE.UniformsUtils.clone( highPassShader.uniforms );
68 |
69 | this.highPassUniforms[ "luminosityThreshold" ].value = threshold;
70 | this.highPassUniforms[ "smoothWidth" ].value = 0.01;
71 |
72 | this.materialHighPassFilter = new THREE.ShaderMaterial( {
73 | uniforms: this.highPassUniforms,
74 | vertexShader: highPassShader.vertexShader,
75 | fragmentShader: highPassShader.fragmentShader,
76 | defines: {}
77 | } );
78 |
79 | // Gaussian Blur Materials
80 | this.separableBlurMaterials = [];
81 | var kernelSizeArray = [ 3, 5, 7, 9, 11 ];
82 | var resx = Math.round( this.resolution.x / 2 );
83 | var resy = Math.round( this.resolution.y / 2 );
84 |
85 | for ( var i = 0; i < this.nMips; i ++ ) {
86 |
87 | this.separableBlurMaterials.push( this.getSeperableBlurMaterial( kernelSizeArray[ i ] ) );
88 |
89 | this.separableBlurMaterials[ i ].uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy );
90 |
91 | resx = Math.round( resx / 2 );
92 |
93 | resy = Math.round( resy / 2 );
94 |
95 | }
96 |
97 | // Composite material
98 | this.compositeMaterial = this.getCompositeMaterial( this.nMips );
99 | this.compositeMaterial.uniforms[ "blurTexture1" ].value = this.renderTargetsVertical[ 0 ].texture;
100 | this.compositeMaterial.uniforms[ "blurTexture2" ].value = this.renderTargetsVertical[ 1 ].texture;
101 | this.compositeMaterial.uniforms[ "blurTexture3" ].value = this.renderTargetsVertical[ 2 ].texture;
102 | this.compositeMaterial.uniforms[ "blurTexture4" ].value = this.renderTargetsVertical[ 3 ].texture;
103 | this.compositeMaterial.uniforms[ "blurTexture5" ].value = this.renderTargetsVertical[ 4 ].texture;
104 | this.compositeMaterial.uniforms[ "bloomStrength" ].value = strength;
105 | this.compositeMaterial.uniforms[ "bloomRadius" ].value = 0.1;
106 | this.compositeMaterial.needsUpdate = true;
107 |
108 | var bloomFactors = [ 1.0, 0.8, 0.6, 0.4, 0.2 ];
109 | this.compositeMaterial.uniforms[ "bloomFactors" ].value = bloomFactors;
110 | this.bloomTintColors = [ new THREE.Vector3( 1, 1, 1 ), new THREE.Vector3( 1, 1, 1 ), new THREE.Vector3( 1, 1, 1 ),
111 | new THREE.Vector3( 1, 1, 1 ), new THREE.Vector3( 1, 1, 1 ) ];
112 | this.compositeMaterial.uniforms[ "bloomTintColors" ].value = this.bloomTintColors;
113 |
114 | // copy material
115 | if ( THREE.CopyShader === undefined ) {
116 |
117 | console.error( "THREE.UnrealBloomPass relies on THREE.CopyShader" );
118 |
119 | }
120 |
121 | var copyShader = THREE.CopyShader;
122 |
123 | this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
124 | this.copyUniforms[ "opacity" ].value = 1.0;
125 |
126 | this.materialCopy = new THREE.ShaderMaterial( {
127 | uniforms: this.copyUniforms,
128 | vertexShader: copyShader.vertexShader,
129 | fragmentShader: copyShader.fragmentShader,
130 | blending: THREE.AdditiveBlending,
131 | depthTest: false,
132 | depthWrite: false,
133 | transparent: true
134 | } );
135 |
136 | this.enabled = true;
137 | this.needsSwap = false;
138 |
139 | this.oldClearColor = new THREE.Color();
140 | this.oldClearAlpha = 1;
141 |
142 | this.basic = new THREE.MeshBasicMaterial();
143 |
144 | this.fsQuad = new THREE.Pass.FullScreenQuad( null );
145 |
146 | };
147 |
148 | THREE.UnrealBloomPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
149 |
150 | constructor: THREE.UnrealBloomPass,
151 |
152 | dispose: function () {
153 |
154 | for ( var i = 0; i < this.renderTargetsHorizontal.length; i ++ ) {
155 |
156 | this.renderTargetsHorizontal[ i ].dispose();
157 |
158 | }
159 |
160 | for ( var i = 0; i < this.renderTargetsVertical.length; i ++ ) {
161 |
162 | this.renderTargetsVertical[ i ].dispose();
163 |
164 | }
165 |
166 | this.renderTargetBright.dispose();
167 |
168 | },
169 |
170 | setSize: function ( width, height ) {
171 |
172 | var resx = Math.round( width / 2 );
173 | var resy = Math.round( height / 2 );
174 |
175 | this.renderTargetBright.setSize( resx, resy );
176 |
177 | for ( var i = 0; i < this.nMips; i ++ ) {
178 |
179 | this.renderTargetsHorizontal[ i ].setSize( resx, resy );
180 | this.renderTargetsVertical[ i ].setSize( resx, resy );
181 |
182 | this.separableBlurMaterials[ i ].uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy );
183 |
184 | resx = Math.round( resx / 2 );
185 | resy = Math.round( resy / 2 );
186 |
187 | }
188 |
189 | },
190 |
191 | render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
192 |
193 | this.oldClearColor.copy( renderer.getClearColor() );
194 | this.oldClearAlpha = renderer.getClearAlpha();
195 | var oldAutoClear = renderer.autoClear;
196 | renderer.autoClear = false;
197 |
198 | renderer.setClearColor( this.clearColor, 0 );
199 |
200 | if ( maskActive ) renderer.state.buffers.stencil.setTest( false );
201 |
202 | // Render input to screen
203 |
204 | if ( this.renderToScreen ) {
205 |
206 | this.fsQuad.material = this.basic;
207 | this.basic.map = readBuffer.texture;
208 |
209 | renderer.setRenderTarget( null );
210 | renderer.clear();
211 | this.fsQuad.render( renderer );
212 |
213 | }
214 |
215 | // 1. Extract Bright Areas
216 |
217 | this.highPassUniforms[ "tDiffuse" ].value = readBuffer.texture;
218 | this.highPassUniforms[ "luminosityThreshold" ].value = this.threshold;
219 | this.fsQuad.material = this.materialHighPassFilter;
220 |
221 | renderer.setRenderTarget( this.renderTargetBright );
222 | renderer.clear();
223 | this.fsQuad.render( renderer );
224 |
225 | // 2. Blur All the mips progressively
226 |
227 | var inputRenderTarget = this.renderTargetBright;
228 |
229 | for ( var i = 0; i < this.nMips; i ++ ) {
230 |
231 | this.fsQuad.material = this.separableBlurMaterials[ i ];
232 |
233 | this.separableBlurMaterials[ i ].uniforms[ "colorTexture" ].value = inputRenderTarget.texture;
234 | this.separableBlurMaterials[ i ].uniforms[ "direction" ].value = THREE.UnrealBloomPass.BlurDirectionX;
235 | renderer.setRenderTarget( this.renderTargetsHorizontal[ i ] );
236 | renderer.clear();
237 | this.fsQuad.render( renderer );
238 |
239 | this.separableBlurMaterials[ i ].uniforms[ "colorTexture" ].value = this.renderTargetsHorizontal[ i ].texture;
240 | this.separableBlurMaterials[ i ].uniforms[ "direction" ].value = THREE.UnrealBloomPass.BlurDirectionY;
241 | renderer.setRenderTarget( this.renderTargetsVertical[ i ] );
242 | renderer.clear();
243 | this.fsQuad.render( renderer );
244 |
245 | inputRenderTarget = this.renderTargetsVertical[ i ];
246 |
247 | }
248 |
249 | // Composite All the mips
250 |
251 | this.fsQuad.material = this.compositeMaterial;
252 | this.compositeMaterial.uniforms[ "bloomStrength" ].value = this.strength;
253 | this.compositeMaterial.uniforms[ "bloomRadius" ].value = this.radius;
254 | this.compositeMaterial.uniforms[ "bloomTintColors" ].value = this.bloomTintColors;
255 |
256 | renderer.setRenderTarget( this.renderTargetsHorizontal[ 0 ] );
257 | renderer.clear();
258 | this.fsQuad.render( renderer );
259 |
260 | // Blend it additively over the input texture
261 |
262 | this.fsQuad.material = this.materialCopy;
263 | this.copyUniforms[ "tDiffuse" ].value = this.renderTargetsHorizontal[ 0 ].texture;
264 |
265 | if ( maskActive ) renderer.state.buffers.stencil.setTest( true );
266 |
267 | if ( this.renderToScreen ) {
268 |
269 | renderer.setRenderTarget( null );
270 | this.fsQuad.render( renderer );
271 |
272 | } else {
273 |
274 | renderer.setRenderTarget( readBuffer );
275 | this.fsQuad.render( renderer );
276 |
277 | }
278 |
279 | // Restore renderer settings
280 |
281 | renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
282 | renderer.autoClear = oldAutoClear;
283 |
284 | },
285 |
286 | getSeperableBlurMaterial: function ( kernelRadius ) {
287 |
288 | return new THREE.ShaderMaterial( {
289 |
290 | defines: {
291 | "KERNEL_RADIUS": kernelRadius,
292 | "SIGMA": kernelRadius
293 | },
294 |
295 | uniforms: {
296 | "colorTexture": { value: null },
297 | "texSize": { value: new THREE.Vector2( 0.5, 0.5 ) },
298 | "direction": { value: new THREE.Vector2( 0.5, 0.5 ) }
299 | },
300 |
301 | vertexShader:
302 | "varying vec2 vUv;\n\
303 | void main() {\n\
304 | vUv = uv;\n\
305 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
306 | }",
307 |
308 | fragmentShader:
309 | "#include \
310 | varying vec2 vUv;\n\
311 | uniform sampler2D colorTexture;\n\
312 | uniform vec2 texSize;\
313 | uniform vec2 direction;\
314 | \
315 | float gaussianPdf(in float x, in float sigma) {\
316 | return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\
317 | }\
318 | void main() {\n\
319 | vec2 invSize = 1.0 / texSize;\
320 | float fSigma = float(SIGMA);\
321 | float weightSum = gaussianPdf(0.0, fSigma);\
322 | vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\
323 | for( int i = 1; i < KERNEL_RADIUS; i ++ ) {\
324 | float x = float(i);\
325 | float w = gaussianPdf(x, fSigma);\
326 | vec2 uvOffset = direction * invSize * x;\
327 | vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\
328 | vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\
329 | diffuseSum += (sample1 + sample2) * w;\
330 | weightSum += 2.0 * w;\
331 | }\
332 | gl_FragColor = vec4(diffuseSum/weightSum, 1.0);\n\
333 | }"
334 | } );
335 |
336 | },
337 |
338 | getCompositeMaterial: function ( nMips ) {
339 |
340 | return new THREE.ShaderMaterial( {
341 |
342 | defines: {
343 | "NUM_MIPS": nMips
344 | },
345 |
346 | uniforms: {
347 | "blurTexture1": { value: null },
348 | "blurTexture2": { value: null },
349 | "blurTexture3": { value: null },
350 | "blurTexture4": { value: null },
351 | "blurTexture5": { value: null },
352 | "dirtTexture": { value: null },
353 | "bloomStrength": { value: 1.0 },
354 | "bloomFactors": { value: null },
355 | "bloomTintColors": { value: null },
356 | "bloomRadius": { value: 0.0 }
357 | },
358 |
359 | vertexShader:
360 | "varying vec2 vUv;\n\
361 | void main() {\n\
362 | vUv = uv;\n\
363 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
364 | }",
365 |
366 | fragmentShader:
367 | "varying vec2 vUv;\
368 | uniform sampler2D blurTexture1;\
369 | uniform sampler2D blurTexture2;\
370 | uniform sampler2D blurTexture3;\
371 | uniform sampler2D blurTexture4;\
372 | uniform sampler2D blurTexture5;\
373 | uniform sampler2D dirtTexture;\
374 | uniform float bloomStrength;\
375 | uniform float bloomRadius;\
376 | uniform float bloomFactors[NUM_MIPS];\
377 | uniform vec3 bloomTintColors[NUM_MIPS];\
378 | \
379 | float lerpBloomFactor(const in float factor) { \
380 | float mirrorFactor = 1.2 - factor;\
381 | return mix(factor, mirrorFactor, bloomRadius);\
382 | }\
383 | \
384 | void main() {\
385 | gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + \
386 | lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + \
387 | lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + \
388 | lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + \
389 | lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\
390 | }"
391 | } );
392 |
393 | }
394 |
395 | } );
396 |
397 | THREE.UnrealBloomPass.BlurDirectionX = new THREE.Vector2( 1.0, 0.0 );
398 | THREE.UnrealBloomPass.BlurDirectionY = new THREE.Vector2( 0.0, 1.0 );
399 |
--------------------------------------------------------------------------------
/js/vendor/shader/ConvolutionShader.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.ConvolutionShader: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author alteredq / http://alteredqualia.com/
4 | *
5 | * Convolution shader
6 | * ported from o3d sample to WebGL / GLSL
7 | * http://o3d.googlecode.com/svn/trunk/samples/convolution.html
8 | */
9 |
10 | THREE.ConvolutionShader = {
11 |
12 | defines: {
13 |
14 | "KERNEL_SIZE_FLOAT": "25.0",
15 | "KERNEL_SIZE_INT": "25"
16 |
17 | },
18 |
19 | uniforms: {
20 |
21 | "tDiffuse": { value: null },
22 | "uImageIncrement": { value: new THREE.Vector2( 0.001953125, 0.0 ) },
23 | "cKernel": { value: [] }
24 |
25 | },
26 |
27 | vertexShader: [
28 |
29 | "uniform vec2 uImageIncrement;",
30 |
31 | "varying vec2 vUv;",
32 |
33 | "void main() {",
34 |
35 | " vUv = uv - ( ( KERNEL_SIZE_FLOAT - 1.0 ) / 2.0 ) * uImageIncrement;",
36 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
37 |
38 | "}"
39 |
40 | ].join( "\n" ),
41 |
42 | fragmentShader: [
43 |
44 | "uniform float cKernel[ KERNEL_SIZE_INT ];",
45 |
46 | "uniform sampler2D tDiffuse;",
47 | "uniform vec2 uImageIncrement;",
48 |
49 | "varying vec2 vUv;",
50 |
51 | "void main() {",
52 |
53 | " vec2 imageCoord = vUv;",
54 | " vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );",
55 |
56 | " for( int i = 0; i < KERNEL_SIZE_INT; i ++ ) {",
57 |
58 | " sum += texture2D( tDiffuse, imageCoord ) * cKernel[ i ];",
59 | " imageCoord += uImageIncrement;",
60 |
61 | " }",
62 |
63 | " gl_FragColor = sum;",
64 |
65 | "}"
66 |
67 |
68 | ].join( "\n" ),
69 |
70 | buildKernel: function ( sigma ) {
71 |
72 | // We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize anyway.
73 |
74 | function gauss( x, sigma ) {
75 |
76 | return Math.exp( - ( x * x ) / ( 2.0 * sigma * sigma ) );
77 |
78 | }
79 |
80 | var i, values, sum, halfWidth, kMaxKernelSize = 25, kernelSize = 2 * Math.ceil( sigma * 3.0 ) + 1;
81 |
82 | if ( kernelSize > kMaxKernelSize ) kernelSize = kMaxKernelSize;
83 | halfWidth = ( kernelSize - 1 ) * 0.5;
84 |
85 | values = new Array( kernelSize );
86 | sum = 0.0;
87 | for ( i = 0; i < kernelSize; ++ i ) {
88 |
89 | values[ i ] = gauss( i - halfWidth, sigma );
90 | sum += values[ i ];
91 |
92 | }
93 |
94 | // normalize the kernel
95 |
96 | for ( i = 0; i < kernelSize; ++ i ) values[ i ] /= sum;
97 |
98 | return values;
99 |
100 | }
101 |
102 | };
103 |
--------------------------------------------------------------------------------
/js/vendor/shader/CopyShader.js:
--------------------------------------------------------------------------------
1 | console.warn( "THREE.CopyShader: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation." );
2 | /**
3 | * @author alteredq / http://alteredqualia.com/
4 | *
5 | * Full-screen textured quad shader
6 | */
7 |
8 | THREE.CopyShader = {
9 |
10 | uniforms: {
11 |
12 | "tDiffuse": { value: null },
13 | "opacity": { value: 1.0 }
14 |
15 | },
16 |
17 | vertexShader: [
18 |
19 | "varying vec2 vUv;",
20 |
21 | "void main() {",
22 |
23 | " vUv = uv;",
24 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
25 |
26 | "}"
27 |
28 | ].join( "\n" ),
29 |
30 | fragmentShader: [
31 |
32 | "uniform float opacity;",
33 |
34 | "uniform sampler2D tDiffuse;",
35 |
36 | "varying vec2 vUv;",
37 |
38 | "void main() {",
39 |
40 | " vec4 texel = texture2D( tDiffuse, vUv );",
41 | " gl_FragColor = opacity * texel;",
42 |
43 | "}"
44 |
45 | ].join( "\n" )
46 |
47 | };
48 |
--------------------------------------------------------------------------------