Просмотр исходного кода

JSM: Added module and TS file for Refractor.

Mugen87 6 лет назад
Родитель
Сommit
eb4f276113

+ 2 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -146,6 +146,7 @@
 				<li>objects
 				<li>objects
 					<ul>
 					<ul>
 						<li>Reflector</li>
 						<li>Reflector</li>
+						<li>Refractor</li>
 					</ul>
 					</ul>
 				</li>
 				</li>
 				<li>pmrem
 				<li>pmrem
@@ -168,6 +169,7 @@
 					<ul>
 					<ul>
 						<li>BokehShader2</li>
 						<li>BokehShader2</li>
 						<li>UnpackDepthRGBAShader</li>
 						<li>UnpackDepthRGBAShader</li>
+						<li>WaterRefractionShader</li>
 					</ul>
 					</ul>
 				</li>
 				</li>
 				<li>utils
 				<li>utils

+ 19 - 29
examples/js/objects/Refractor.js

@@ -184,43 +184,36 @@ THREE.Refractor = function ( geometry, options ) {
 
 
 	//
 	//
 
 
-	var render = ( function () {
+	function render( renderer, scene, camera ) {
 
 
-		var viewport = new THREE.Vector4();
-		var size = new THREE.Vector2();
+		scope.visible = false;
 
 
-		return function render( renderer, scene, camera ) {
+		var currentRenderTarget = renderer.getRenderTarget();
+		var currentVrEnabled = renderer.vr.enabled;
+		var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
 
 
-			scope.visible = false;
+		renderer.vr.enabled = false; // avoid camera modification
+		renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows
 
 
-			var currentRenderTarget = renderer.getRenderTarget();
-			var currentVrEnabled = renderer.vr.enabled;
-			var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
+		renderer.setRenderTarget( renderTarget );
+		renderer.clear();
+		renderer.render( scene, virtualCamera );
 
 
-			renderer.vr.enabled = false; // avoid camera modification
-			renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows
+		renderer.vr.enabled = currentVrEnabled;
+		renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
+		renderer.setRenderTarget( currentRenderTarget );
 
 
-			renderer.setRenderTarget( renderTarget );
-			renderer.clear();
-			renderer.render( scene, virtualCamera );
+		// restore viewport
 
 
-			renderer.vr.enabled = currentVrEnabled;
-			renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
-			renderer.setRenderTarget( currentRenderTarget );
+		if ( camera.isArrayCamera ) {
 
 
-			// restore viewport
+			renderer.state.viewport( camera.viewport );
 
 
-			if ( camera.isArrayCamera ) {
-
-				renderer.state.viewport( camera.viewport );
-
-			}
+		}
 
 
-			scope.visible = true;
+		scope.visible = true;
 
 
-		};
-
-	} )();
+	}
 
 
 	//
 	//
 
 
@@ -262,17 +255,14 @@ THREE.Refractor.RefractorShader = {
 	uniforms: {
 	uniforms: {
 
 
 		'color': {
 		'color': {
-			type: 'c',
 			value: null
 			value: null
 		},
 		},
 
 
 		'tDiffuse': {
 		'tDiffuse': {
-			type: 't',
 			value: null
 			value: null
 		},
 		},
 
 
 		'textureMatrix': {
 		'textureMatrix': {
-			type: 'm4',
 			value: null
 			value: null
 		}
 		}
 
 

+ 0 - 5
examples/js/shaders/WaterRefractionShader.js

@@ -8,27 +8,22 @@ THREE.WaterRefractionShader = {
 	uniforms: {
 	uniforms: {
 
 
 		'color': {
 		'color': {
-			type: 'c',
 			value: null
 			value: null
 		},
 		},
 
 
 		'time': {
 		'time': {
-			type: 'f',
 			value: 0
 			value: 0
 		},
 		},
 
 
 		'tDiffuse': {
 		'tDiffuse': {
-			type: 't',
 			value: null
 			value: null
 		},
 		},
 
 
 		'tDudv': {
 		'tDudv': {
-			type: 't',
 			value: null
 			value: null
 		},
 		},
 
 
 		'textureMatrix': {
 		'textureMatrix': {
-			type: 'm4',
 			value: null
 			value: null
 		}
 		}
 
 

+ 20 - 0
examples/jsm/objects/Refractor.d.ts

@@ -0,0 +1,20 @@
+import {
+  Mesh,
+  BufferGeometry,
+  Color,
+  WebGLRenderTarget
+} from '../../../src/Three';
+
+export interface RefractorOptions {
+  color?: Color;
+  textureWidth?: number;
+  textureHeight?: number;
+  clipBias?: number;
+  shader?: object;
+}
+
+export class Refractor extends Mesh {
+  constructor(geometry?: BufferGeometry, options?: RefractorOptions);
+
+  getRenderTarget(): WebGLRenderTarget;
+}

+ 334 - 0
examples/jsm/objects/Refractor.js

@@ -0,0 +1,334 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ */
+
+import {
+	Color,
+	LinearFilter,
+	Math as _Math,
+	Matrix4,
+	Mesh,
+	PerspectiveCamera,
+	Plane,
+	Quaternion,
+	RGBFormat,
+	ShaderMaterial,
+	UniformsUtils,
+	Vector3,
+	Vector4,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+
+var Refractor = function ( geometry, options ) {
+
+	Mesh.call( this, geometry );
+
+	this.type = 'Refractor';
+
+	var scope = this;
+
+	options = options || {};
+
+	var color = ( options.color !== undefined ) ? new Color( options.color ) : new Color( 0x7F7F7F );
+	var textureWidth = options.textureWidth || 512;
+	var textureHeight = options.textureHeight || 512;
+	var clipBias = options.clipBias || 0;
+	var shader = options.shader || Refractor.RefractorShader;
+
+	//
+
+	var virtualCamera = new PerspectiveCamera();
+	virtualCamera.matrixAutoUpdate = false;
+	virtualCamera.userData.refractor = true;
+
+	//
+
+	var refractorPlane = new Plane();
+	var textureMatrix = new Matrix4();
+
+	// render target
+
+	var parameters = {
+		minFilter: LinearFilter,
+		magFilter: LinearFilter,
+		format: RGBFormat,
+		stencilBuffer: false
+	};
+
+	var renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, parameters );
+
+	if ( ! _Math.isPowerOfTwo( textureWidth ) || ! _Math.isPowerOfTwo( textureHeight ) ) {
+
+		renderTarget.texture.generateMipmaps = false;
+
+	}
+
+	// material
+
+	this.material = new ShaderMaterial( {
+		uniforms: UniformsUtils.clone( shader.uniforms ),
+		vertexShader: shader.vertexShader,
+		fragmentShader: shader.fragmentShader,
+		transparent: true // ensures, refractors are drawn from farthest to closest
+	} );
+
+	this.material.uniforms[ "color" ].value = color;
+	this.material.uniforms[ "tDiffuse" ].value = renderTarget.texture;
+	this.material.uniforms[ "textureMatrix" ].value = textureMatrix;
+
+	// functions
+
+	var visible = ( function () {
+
+		var refractorWorldPosition = new Vector3();
+		var cameraWorldPosition = new Vector3();
+		var rotationMatrix = new Matrix4();
+
+		var view = new Vector3();
+		var normal = new Vector3();
+
+		return function visible( camera ) {
+
+			refractorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
+			cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
+
+			view.subVectors( refractorWorldPosition, cameraWorldPosition );
+
+			rotationMatrix.extractRotation( scope.matrixWorld );
+
+			normal.set( 0, 0, 1 );
+			normal.applyMatrix4( rotationMatrix );
+
+			return view.dot( normal ) < 0;
+
+		};
+
+	} )();
+
+	var updateRefractorPlane = ( function () {
+
+		var normal = new Vector3();
+		var position = new Vector3();
+		var quaternion = new Quaternion();
+		var scale = new Vector3();
+
+		return function updateRefractorPlane() {
+
+			scope.matrixWorld.decompose( position, quaternion, scale );
+			normal.set( 0, 0, 1 ).applyQuaternion( quaternion ).normalize();
+
+			// flip the normal because we want to cull everything above the plane
+
+			normal.negate();
+
+			refractorPlane.setFromNormalAndCoplanarPoint( normal, position );
+
+		};
+
+	} )();
+
+	var updateVirtualCamera = ( function () {
+
+		var clipPlane = new Plane();
+		var clipVector = new Vector4();
+		var q = new Vector4();
+
+		return function updateVirtualCamera( camera ) {
+
+			virtualCamera.matrixWorld.copy( camera.matrixWorld );
+			virtualCamera.matrixWorldInverse.getInverse( virtualCamera.matrixWorld );
+			virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
+			virtualCamera.far = camera.far; // used in WebGLBackground
+
+			// The following code creates an oblique view frustum for clipping.
+			// see: Lengyel, Eric. “Oblique View Frustum Depth Projection and Clipping”.
+			// Journal of Game Development, Vol. 1, No. 2 (2005), Charles River Media, pp. 5–16
+
+			clipPlane.copy( refractorPlane );
+			clipPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
+
+			clipVector.set( clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.constant );
+
+			// calculate the clip-space corner point opposite the clipping plane and
+			// transform it into camera space by multiplying it by the inverse of the projection matrix
+
+			var projectionMatrix = virtualCamera.projectionMatrix;
+
+			q.x = ( Math.sign( clipVector.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
+			q.y = ( Math.sign( clipVector.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
+
+			clipVector.multiplyScalar( 2.0 / clipVector.dot( q ) );
+
+			// replacing the third row of the projection matrix
+
+			projectionMatrix.elements[ 2 ] = clipVector.x;
+			projectionMatrix.elements[ 6 ] = clipVector.y;
+			projectionMatrix.elements[ 10 ] = clipVector.z + 1.0 - clipBias;
+			projectionMatrix.elements[ 14 ] = clipVector.w;
+
+		};
+
+	} )();
+
+	// This will update the texture matrix that is used for projective texture mapping in the shader.
+	// see: http://developer.download.nvidia.com/assets/gamedev/docs/projective_texture_mapping.pdf
+
+	function updateTextureMatrix( camera ) {
+
+		// this matrix does range mapping to [ 0, 1 ]
+
+		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
+		);
+
+		// we use "Object Linear Texgen", so we need to multiply the texture matrix T
+		// (matrix above) with the projection and view matrix of the virtual camera
+		// and the model matrix of the refractor
+
+		textureMatrix.multiply( camera.projectionMatrix );
+		textureMatrix.multiply( camera.matrixWorldInverse );
+		textureMatrix.multiply( scope.matrixWorld );
+
+	}
+
+	//
+
+	function render( renderer, scene, camera ) {
+
+		scope.visible = false;
+
+		var currentRenderTarget = renderer.getRenderTarget();
+		var currentVrEnabled = renderer.vr.enabled;
+		var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
+
+		renderer.vr.enabled = false; // avoid camera modification
+		renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows
+
+		renderer.setRenderTarget( renderTarget );
+		renderer.clear();
+		renderer.render( scene, virtualCamera );
+
+		renderer.vr.enabled = currentVrEnabled;
+		renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
+		renderer.setRenderTarget( currentRenderTarget );
+
+		// restore viewport
+
+		if ( camera.isArrayCamera ) {
+
+			renderer.state.viewport( camera.viewport );
+
+		}
+
+		scope.visible = true;
+
+	}
+
+	//
+
+	this.onBeforeRender = function ( renderer, scene, camera ) {
+
+		// ensure refractors are rendered only once per frame
+
+		if ( camera.userData.refractor === true ) return;
+
+		// avoid rendering when the refractor is viewed from behind
+
+		if ( ! visible( camera ) === true ) return;
+
+		// update
+
+		updateRefractorPlane();
+
+		updateTextureMatrix( camera );
+
+		updateVirtualCamera( camera );
+
+		render( renderer, scene, camera );
+
+	};
+
+	this.getRenderTarget = function () {
+
+		return renderTarget;
+
+	};
+
+};
+
+Refractor.prototype = Object.create( Mesh.prototype );
+Refractor.prototype.constructor = Refractor;
+
+Refractor.RefractorShader = {
+
+	uniforms: {
+
+		'color': {
+			value: null
+		},
+
+		'tDiffuse': {
+			value: null
+		},
+
+		'textureMatrix': {
+			value: null
+		}
+
+	},
+
+	vertexShader: [
+
+		'uniform mat4 textureMatrix;',
+
+		'varying vec4 vUv;',
+
+		'void main() {',
+
+		'	vUv = textureMatrix * vec4( position, 1.0 );',
+
+		'	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+		'}'
+
+	].join( '\n' ),
+
+	fragmentShader: [
+
+		'uniform vec3 color;',
+		'uniform sampler2D tDiffuse;',
+
+		'varying vec4 vUv;',
+
+		'float blendOverlay( float base, float blend ) {',
+
+		'	return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
+
+		'}',
+
+		'vec3 blendOverlay( vec3 base, vec3 blend ) {',
+
+		'	return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
+
+		'}',
+
+		'void main() {',
+
+		'	vec4 base = texture2DProj( tDiffuse, vUv );',
+
+		'	gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );',
+
+		'}'
+
+	].join( '\n' )
+};
+
+export { Refractor };

+ 15 - 0
examples/jsm/shaders/WaterRefractionShader.d.ts

@@ -0,0 +1,15 @@
+import {
+  Uniform
+} from '../../../src/Three';
+
+export interface WaterRefractionShader {
+  uniforms: {
+    color: Uniform;
+    time: Uniform;
+    tDiffuse: Uniform;
+    tDudv: Uniform;
+    textureMatrix: Uniform;
+  };
+  vertexShader: string;
+  fragmentShader:string;
+}

+ 100 - 0
examples/jsm/shaders/WaterRefractionShader.js

@@ -0,0 +1,100 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ */
+
+
+
+var WaterRefractionShader = {
+
+	uniforms: {
+
+		'color': {
+			value: null
+		},
+
+		'time': {
+			value: 0
+		},
+
+		'tDiffuse': {
+			value: null
+		},
+
+		'tDudv': {
+			value: null
+		},
+
+		'textureMatrix': {
+			value: null
+		}
+
+	},
+
+	vertexShader: [
+
+		'uniform mat4 textureMatrix;',
+
+		'varying vec2 vUv;',
+		'varying vec4 vUvRefraction;',
+
+		'void main() {',
+
+		'	vUv = uv;',
+
+		'	vUvRefraction = textureMatrix * vec4( position, 1.0 );',
+
+		'	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+		'}'
+
+	].join( '\n' ),
+
+	fragmentShader: [
+
+		'uniform vec3 color;',
+		'uniform float time;',
+		'uniform sampler2D tDiffuse;',
+		'uniform sampler2D tDudv;',
+
+		'varying vec2 vUv;',
+		'varying vec4 vUvRefraction;',
+
+		'float blendOverlay( float base, float blend ) {',
+
+		'	return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
+
+		'}',
+
+		'vec3 blendOverlay( vec3 base, vec3 blend ) {',
+
+		'	return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ),blendOverlay( base.b, blend.b ) );',
+
+		'}',
+
+		'void main() {',
+
+		' float waveStrength = 0.1;',
+		' float waveSpeed = 0.03;',
+
+		// simple distortion (ripple) via dudv map (see https://www.youtube.com/watch?v=6B7IF6GOu7s)
+
+		'	vec2 distortedUv = texture2D( tDudv, vec2( vUv.x + time * waveSpeed, vUv.y ) ).rg * waveStrength;',
+		'	distortedUv = vUv.xy + vec2( distortedUv.x, distortedUv.y + time * waveSpeed );',
+		'	vec2 distortion = ( texture2D( tDudv, distortedUv ).rg * 2.0 - 1.0 ) * waveStrength;',
+
+		// new uv coords
+
+		' vec4 uv = vec4( vUvRefraction );',
+		' uv.xy += distortion;',
+
+		'	vec4 base = texture2DProj( tDiffuse, uv );',
+
+		'	gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );',
+
+		'}'
+
+	].join( '\n' )
+};
+
+export { WaterRefractionShader };

+ 2 - 0
utils/modularize.js

@@ -61,12 +61,14 @@ var files = [
 	{ path: 'loaders/VRMLLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/VRMLLoader.js', dependencies: [], ignoreList: [] },
 
 
 	{ path: 'objects/Reflector.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Reflector.js', dependencies: [], ignoreList: [] },
+	{ path: 'objects/Refractor.js', dependencies: [], ignoreList: [] },
 
 
 	{ path: 'pmrem/PMREMCubeUVPacker.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMCubeUVPacker.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', dependencies: [], ignoreList: [] },
 
 
 	{ path: 'shaders/BokehShader2.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/BokehShader2.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/UnpackDepthRGBAShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/UnpackDepthRGBAShader.js', dependencies: [], ignoreList: [] },
+	{ path: 'shaders/WaterRefractionShader.js', dependencies: [], ignoreList: [] },
 
 
 	{ path: 'renderers/CSS2DRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/CSS2DRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/CSS3DRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/CSS3DRenderer.js', dependencies: [], ignoreList: [] },