forked from mrdoob/three.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMirror.js
202 lines (140 loc) · 6.25 KB
/
Mirror.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/**
* @author Slayvin / http://slayvin.net
*/
THREE.Mirror = function ( width, height, options ) {
THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
var scope = this;
scope.name = 'mirror_' + scope.id;
scope.matrixNeedsUpdate = true;
options = options || {};
var textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
var textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
var clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
var mirrorColor = options.color !== undefined ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
var mirrorPlane = new THREE.Plane();
var normal = new THREE.Vector3();
var mirrorWorldPosition = new THREE.Vector3();
var cameraWorldPosition = new THREE.Vector3();
var rotationMatrix = new THREE.Matrix4();
var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
var clipPlane = new THREE.Vector4();
var textureMatrix = new THREE.Matrix4();
var mirrorCamera = new THREE.PerspectiveCamera();
mirrorCamera.matrixAutoUpdate = true;
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: false
};
var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
renderTarget.texture.generateMipmaps = false;
}
var mirrorShader = {
uniforms: {
mirrorColor: { value: new THREE.Color( 0x7F7F7F ) },
mirrorSampler: { value: null },
textureMatrix: { value: new THREE.Matrix4() }
},
vertexShader: [
'uniform mat4 textureMatrix;',
'varying vec4 mirrorCoord;',
'void main() {',
' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
' mirrorCoord = textureMatrix * worldPosition;',
' gl_Position = projectionMatrix * mvPosition;',
'}'
].join( '\n' ),
fragmentShader: [
'uniform vec3 mirrorColor;',
'uniform sampler2D mirrorSampler;',
'varying vec4 mirrorCoord;',
'float blendOverlay(float base, float blend) {',
' return( base < 0.5 ? ( 2.0 * base * blend ) : (1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
'}',
'void main() {',
' vec4 color = texture2DProj(mirrorSampler, mirrorCoord);',
' color = vec4(blendOverlay(mirrorColor.r, color.r), blendOverlay(mirrorColor.g, color.g), blendOverlay(mirrorColor.b, color.b), 1.0);',
' gl_FragColor = color;',
'}'
].join( '\n' )
};
var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms );
var material = new THREE.ShaderMaterial( {
fragmentShader: mirrorShader.fragmentShader,
vertexShader: mirrorShader.vertexShader,
uniforms: mirrorUniforms
} );
material.uniforms.mirrorSampler.value = renderTarget.texture;
material.uniforms.mirrorColor.value = mirrorColor;
material.uniforms.textureMatrix.value = textureMatrix;
scope.material = material;
function updateTextureMatrix( camera ) {
camera.updateMatrixWorld();
mirrorCamera.copy( camera );
mirrorCamera.updateProjectionMatrix();
scope.updateMatrixWorld();
mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
rotationMatrix.extractRotation( scope.matrixWorld );
normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );
var view = mirrorWorldPosition.clone().sub( cameraWorldPosition );
view.reflect( normal ).negate();
view.add( mirrorWorldPosition );
rotationMatrix.extractRotation( camera.matrixWorld );
lookAtPosition.set( 0, 0, - 1 );
lookAtPosition.applyMatrix4( rotationMatrix );
lookAtPosition.add( cameraWorldPosition );
var target = mirrorWorldPosition.clone().sub( lookAtPosition );
target.reflect( normal ).negate();
target.add( mirrorWorldPosition );
mirrorCamera.position.copy( view );
mirrorCamera.up.set( 0, - 1, 0 );
mirrorCamera.up.applyMatrix4( rotationMatrix );
mirrorCamera.up.reflect( normal ).negate();
mirrorCamera.lookAt( target );
mirrorCamera.updateProjectionMatrix();
mirrorCamera.updateMatrixWorld();
mirrorCamera.matrixWorldInverse.getInverse( mirrorCamera.matrixWorld );
// Update the texture matrix
textureMatrix.set(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
);
textureMatrix.multiply( mirrorCamera.projectionMatrix );
textureMatrix.multiply( mirrorCamera.matrixWorldInverse );
// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition );
mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse );
clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant );
var q = new THREE.Vector4();
var projectionMatrix = mirrorCamera.projectionMatrix;
q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
q.z = - 1.0;
q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
// Calculate the scaled plane vector
var c = new THREE.Vector4();
c = clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
// Replacing the third row of the projection matrix
projectionMatrix.elements[ 2 ] = c.x;
projectionMatrix.elements[ 6 ] = c.y;
projectionMatrix.elements[ 10 ] = c.z + 1.0 - clipBias;
projectionMatrix.elements[ 14 ] = c.w;
}
scope.onBeforeRender = function ( renderer, scene, camera ) {
updateTextureMatrix( camera );
scope.visible = false;
var currentRenderTarget = renderer.getRenderTarget();
renderer.render( scene, mirrorCamera, renderTarget, true );
renderer.setRenderTarget( currentRenderTarget );
scope.visible = true;
};
};
THREE.Mirror.prototype = Object.create( THREE.Mesh.prototype );