Mirror.js 6.2 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

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

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

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

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

32 33
	var mirrorCamera = new THREE.PerspectiveCamera();
	mirrorCamera.matrixAutoUpdate = true;
M
Mr.doob 已提交
34

35 36 37 38 39 40
	var parameters = {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.LinearFilter,
		format: THREE.RGBFormat,
		stencilBuffer: false
	};
M
Mr.doob 已提交
41

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

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

46
		renderTarget.texture.generateMipmaps = false;
G
Gregg Tavares 已提交
47

48
	}
M
Mr.doob 已提交
49

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

	};

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

93
	var material = new THREE.ShaderMaterial( {
M
Mr.doob 已提交
94

M
Mr.doob 已提交
95 96 97
		fragmentShader: mirrorShader.fragmentShader,
		vertexShader: mirrorShader.vertexShader,
		uniforms: mirrorUniforms
M
Mr.doob 已提交
98

M
Mr.doob 已提交
99 100
	} );

101 102 103
	material.uniforms.mirrorSampler.value = renderTarget.texture;
	material.uniforms.mirrorColor.value = mirrorColor;
	material.uniforms.textureMatrix.value = textureMatrix;
M
Mr.doob 已提交
104

105
	scope.material = material;
G
gero3 已提交
106

107
	function updateTextureMatrix( camera ) {
M
Mr.doob 已提交
108

109
		camera.updateMatrixWorld();
M
Mr.doob 已提交
110

111 112
		mirrorCamera.copy( camera );
		mirrorCamera.updateProjectionMatrix();
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

124 125 126
		var view = mirrorWorldPosition.clone().sub( cameraWorldPosition );
		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

134 135 136
		var target = mirrorWorldPosition.clone().sub( lookAtPosition );
		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 145 146
		mirrorCamera.updateProjectionMatrix();
		mirrorCamera.updateMatrixWorld();
		mirrorCamera.matrixWorldInverse.getInverse( mirrorCamera.matrixWorld );
M
Mr.doob 已提交
147

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

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

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

M
Mr.doob 已提交
165
		var q = new THREE.Vector4();
166
		var projectionMatrix = mirrorCamera.projectionMatrix;
M
Mr.doob 已提交
167

168 169
		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 已提交
170 171
		q.z = - 1.0;
		q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
M
Mr.doob 已提交
172

M
Mr.doob 已提交
173 174
		// Calculate the scaled plane vector
		var c = new THREE.Vector4();
175
		c = clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
M
Mr.doob 已提交
176

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

183
	}
M
Mr.doob 已提交
184

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

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

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

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

193 194
		renderer.render( scene, mirrorCamera, renderTarget, true );
		renderer.setRenderTarget( currentRenderTarget );
M
Mr.doob 已提交
195

196
		scope.visible = true;
M
Mr.doob 已提交
197

198
	};
M
Mr.doob 已提交
199

200
};
M
Mr.doob 已提交
201

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