Mirror.js 7.0 KB
Newer Older
M
Mr.doob 已提交
1 2 3 4
/**
 * @author Slayvin / http://slayvin.net
 */

5
THREE.Mirror = function ( width, height, options ) {
M
Mr.doob 已提交
6

7
	THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
M
Mr.doob 已提交
8

9
	var scope = this;
M
Mr.doob 已提交
10

11 12
	scope.name = 'mirror_' + scope.id;
	scope.matrixNeedsUpdate = true;
M
Mr.doob 已提交
13

14
	options = options || {};
M
Mr.doob 已提交
15

M
Mr.doob 已提交
16 17
	var viewport = new THREE.Vector4();

18 19
	var textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
	var textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
M
Mr.doob 已提交
20

21
	var clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
G
gero3 已提交
22
	var mirrorColor = options.color !== undefined ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
M
Mr.doob 已提交
23

24 25 26 27 28 29 30
	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();
31

M
Mr.doob 已提交
32 33 34 35
	var view = new THREE.Vector3();
	var target = new THREE.Vector3();
	var q = new THREE.Vector4();

36
	var textureMatrix = new THREE.Matrix4();
M
Mr.doob 已提交
37

38
	var mirrorCamera = new THREE.PerspectiveCamera();
M
Mr.doob 已提交
39

40 41 42 43 44 45
	var parameters = {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.LinearFilter,
		format: THREE.RGBFormat,
		stencilBuffer: false
	};
M
Mr.doob 已提交
46

47
	var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
M
Mr.doob 已提交
48

49
	if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
M
Mr.doob 已提交
50

51
		renderTarget.texture.generateMipmaps = false;
G
Gregg Tavares 已提交
52

53
	}
M
Mr.doob 已提交
54

M
Mr.doob 已提交
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
	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' )

	};

96
	var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms );
M
Mr.doob 已提交
97

98
	var material = new THREE.ShaderMaterial( {
M
Mr.doob 已提交
99

M
Mr.doob 已提交
100 101 102
		fragmentShader: mirrorShader.fragmentShader,
		vertexShader: mirrorShader.vertexShader,
		uniforms: mirrorUniforms
M
Mr.doob 已提交
103

M
Mr.doob 已提交
104 105
	} );

106 107 108
	material.uniforms.mirrorSampler.value = renderTarget.texture;
	material.uniforms.mirrorColor.value = mirrorColor;
	material.uniforms.textureMatrix.value = textureMatrix;
M
Mr.doob 已提交
109

110
	scope.material = material;
G
gero3 已提交
111

112
	function updateTextureMatrix( camera ) {
M
Mr.doob 已提交
113

114
		scope.updateMatrixWorld();
M
Mr.doob 已提交
115

116 117
		mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
		cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
M
Mr.doob 已提交
118

119
		rotationMatrix.extractRotation( scope.matrixWorld );
M
Mr.doob 已提交
120

121 122
		normal.set( 0, 0, 1 );
		normal.applyMatrix4( rotationMatrix );
M
Mr.doob 已提交
123

M
Mr.doob 已提交
124
		view.subVectors( mirrorWorldPosition, cameraWorldPosition );
125 126
		view.reflect( normal ).negate();
		view.add( mirrorWorldPosition );
M
Mr.doob 已提交
127

128
		rotationMatrix.extractRotation( camera.matrixWorld );
M
Mr.doob 已提交
129

130 131 132
		lookAtPosition.set( 0, 0, - 1 );
		lookAtPosition.applyMatrix4( rotationMatrix );
		lookAtPosition.add( cameraWorldPosition );
M
Mr.doob 已提交
133

M
Mr.doob 已提交
134
		target.subVectors( mirrorWorldPosition, lookAtPosition );
135 136
		target.reflect( normal ).negate();
		target.add( mirrorWorldPosition );
M
Mr.doob 已提交
137

138 139 140 141 142
		mirrorCamera.position.copy( view );
		mirrorCamera.up.set( 0, - 1, 0 );
		mirrorCamera.up.applyMatrix4( rotationMatrix );
		mirrorCamera.up.reflect( normal ).negate();
		mirrorCamera.lookAt( target );
M
Mr.doob 已提交
143

144
		mirrorCamera.aspect = camera.aspect;
M
Mr.doob 已提交
145 146 147
		mirrorCamera.near = camera.near;
		mirrorCamera.far = camera.far;

148
		mirrorCamera.updateMatrixWorld();
M
Mr.doob 已提交
149
		mirrorCamera.updateProjectionMatrix();
M
Mr.doob 已提交
150

M
Mr.doob 已提交
151
		// Update the texture matrix
152
		textureMatrix.set(
M
Mr.doob 已提交
153 154 155 156 157
			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
		);
158 159
		textureMatrix.multiply( mirrorCamera.projectionMatrix );
		textureMatrix.multiply( mirrorCamera.matrixWorldInverse );
M
Mr.doob 已提交
160

M
Mr.doob 已提交
161 162
		// 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
163 164
		mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition );
		mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse );
M
Mr.doob 已提交
165

166
		clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant );
M
Mr.doob 已提交
167

168
		var projectionMatrix = mirrorCamera.projectionMatrix;
M
Mr.doob 已提交
169

170 171
		q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
		q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
M
Mr.doob 已提交
172 173
		q.z = - 1.0;
		q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
M
Mr.doob 已提交
174

M
Mr.doob 已提交
175
		// Calculate the scaled plane vector
M
Mr.doob 已提交
176
		clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
M
Mr.doob 已提交
177

M
Mr.doob 已提交
178
		// Replacing the third row of the projection matrix
M
Mr.doob 已提交
179 180 181 182
		projectionMatrix.elements[ 2 ] = clipPlane.x;
		projectionMatrix.elements[ 6 ] = clipPlane.y;
		projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
		projectionMatrix.elements[ 14 ] = clipPlane.w;
M
Mr.doob 已提交
183

184
	}
M
Mr.doob 已提交
185

186
	scope.onBeforeRender = function ( renderer, scene, camera ) {
M
Mr.doob 已提交
187

188
		updateTextureMatrix( camera );
M
Mr.doob 已提交
189

190
		scope.visible = false;
M
Mr.doob 已提交
191

192
		var currentRenderTarget = renderer.getRenderTarget();
M
Mr.doob 已提交
193

M
Mr.doob 已提交
194 195 196
		var currentVrEnabled = renderer.vr.enabled;
		var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;

M
Mr.doob 已提交
197
		renderer.vr.enabled = false; // Avoid camera modification and recursion
M
Mr.doob 已提交
198 199
		renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows

200
		renderer.render( scene, mirrorCamera, renderTarget, true );
M
Mr.doob 已提交
201 202

		renderer.vr.enabled = currentVrEnabled;
M
Mr.doob 已提交
203 204
		renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;

205
		renderer.setRenderTarget( currentRenderTarget );
M
Mr.doob 已提交
206

M
Mr.doob 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
		// Restore viewport

		var bounds = camera.bounds;

		if ( bounds !== undefined ) {

			var size = renderer.getSize();
			var pixelRatio = renderer.getPixelRatio();

			viewport.x = bounds.x * size.width * pixelRatio;
			viewport.y = bounds.y * size.height * pixelRatio;
			viewport.z = bounds.z * size.width * pixelRatio;
			viewport.w = bounds.w * size.height * pixelRatio;

			renderer.state.viewport( viewport );

		}

225
		scope.visible = true;
M
Mr.doob 已提交
226

227
	};
M
Mr.doob 已提交
228

229
};
M
Mr.doob 已提交
230

231
THREE.Mirror.prototype = Object.create( THREE.Mesh.prototype );