Mirror.js 6.1 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
		mirrorCamera.updateProjectionMatrix();
		mirrorCamera.updateMatrixWorld();
M
Mr.doob 已提交
146

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

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

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

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

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

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

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

181
	}
M
Mr.doob 已提交
182

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

185
		updateTextureMatrix( camera );
M
Mr.doob 已提交
186

187
		scope.visible = false;
M
Mr.doob 已提交
188

189
		var currentRenderTarget = renderer.getRenderTarget();
M
Mr.doob 已提交
190

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

194
		scope.visible = true;
M
Mr.doob 已提交
195

196
	};
M
Mr.doob 已提交
197

198
};
M
Mr.doob 已提交
199

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