提交 2f84f189 编写于 作者: M Mugen87

Added refractor example

上级 3bdf3cde
......@@ -231,6 +231,7 @@ var files = {
"webgl_postprocessing_unreal_bloom",
"webgl_raycast_texture",
"webgl_read_float_buffer",
"webgl_refraction",
"webgl_rtt",
"webgl_sandbox",
"webgl_shader",
......
/**
* @author Mugen87 / https://github.com/Mugen87
*
*/
THREE.Refractor = function ( width, height, options ) {
THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
this.type = 'Refractor';
var scope = this;
var color = options.color || new THREE.Color( 0x7f7f7f );
var textureWidth = options.textureWidth || 512;
var textureHeight = options.textureHeight || 512;
var clipBias = options.clipBias || 0;
var shader = options.shader || THREE.DefaultRefractionShader;
//
var virtualCamera = new THREE.PerspectiveCamera();
virtualCamera.matrixAutoUpdate = false;
virtualCamera.userData.refractor = true;
//
var refractorPlane = new THREE.Plane();
var textureMatrix = new THREE.Matrix4();
// render target
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;
}
// material
this.material = new THREE.ShaderMaterial( {
uniforms: THREE.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 THREE.Vector3();
var cameraWorldPosition = new THREE.Vector3();
var rotationMatrix = new THREE.Matrix4();
var view = new THREE.Vector3();
var normal = new THREE.Vector3();
return function updateRefractorPlane( 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 THREE.Vector3();
var position = new THREE.Vector3();
var quaternion = new THREE.Quaternion();
var scale = new THREE.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 THREE.Plane();
var clipVector = new THREE.Vector4();
var q = new THREE.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 );
}
//
var render = ( function () {
var viewport = new THREE.Vector4();
return 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.render( scene, virtualCamera, renderTarget, true );
renderer.vr.enabled = currentVrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
// 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 );
}
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( camera );
updateTextureMatrix( camera );
updateVirtualCamera( camera );
render( renderer, scene, camera );
};
};
THREE.Refractor.prototype = Object.create( THREE.Mesh.prototype );
THREE.Refractor.prototype.constructor = THREE.Refractor;
THREE.DefaultRefractionShader = {
uniforms: {
'color': {
type: 'c',
value: null
},
'tDiffuse': {
type: 't',
value: null
},
'textureMatrix': {
type: 'm4',
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' )
};
/**
* @author Mugen87 / https://github.com/Mugen87
*
*/
THREE.WaterRefractionShader = {
uniforms: {
'color': {
type: 'c',
value: null
},
'time': {
type: 'f',
value: 0
},
'tDiffuse': {
type: 't',
value: null
},
'tDudv': {
type: 't',
value: null
},
'textureMatrix': {
type: 'm4',
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' )
};
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js refraction</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
background:#777;
padding:0;
margin:0;
font-weight: bold;
overflow:hidden;
}
#info {
position: absolute;
top: 0px;
width: 100%;
color: #ffffff;
padding: 5px;
font-family:Monospace;
font-size:13px;
text-align:center;
}
a {
color: #ffffff;
}
</style>
<script src="../build/three.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/shaders/WaterRefractionShader.js"></script>
<script src="js/objects/Refractor.js"></script>
<script src="js/Detector.js"></script>
</head>
<body>
<div id="container"></div>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> refraction
</div>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var scene, camera, clock, renderer, refractor;
init();
function init() {
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( - 10, 0, 15 );
camera.lookAt( scene.position );
// clock
clock = new THREE.Clock();
// mesh
var geometry = new THREE.TorusKnotBufferGeometry( 3, 1, 256, 32 );
var material = new THREE.MeshStandardMaterial( { color: 0x6083c2 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// refractor
refractor = new THREE.Refractor( 10, 10, {
color: new THREE.Color( 0x999999 ),
textureWidth: 1024,
textureHeight: 1024,
shader: THREE.WaterRefractionShader
} );
refractor.position.set( 0, 0, 5 );
scene.add( refractor );
// load dudv map for distortion effect
var dudvMap = new THREE.TextureLoader().load( 'textures/waterdudv.jpg', function () {
animate();
} );
dudvMap.wrapS = dudvMap.wrapT = THREE.RepeatWrapping;
refractor.material.uniforms.tDudv.value = dudvMap;
// light
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
var pointLight = new THREE.PointLight( 0xffffff, 0.8 );
camera.add( pointLight );
scene.add( camera );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x20252f );
renderer.setPixelRatio( window.devicePixelRatio );
document.body.appendChild( renderer.domElement );
//
controls = new THREE.OrbitControls( camera, renderer.domElement );
//
window.addEventListener( 'resize', onResize, false );
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
refractor.material.uniforms.time.value += clock.getDelta();
renderer.render( scene, camera );
}
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册