添加关照、全局等高线、修改图层问题
This commit is contained in:
50
static/sdk/three/jsm/objects/GroundedSkybox.js
Normal file
50
static/sdk/three/jsm/objects/GroundedSkybox.js
Normal file
@ -0,0 +1,50 @@
|
||||
import { Mesh, MeshBasicMaterial, SphereGeometry, Vector3 } from 'three';
|
||||
|
||||
/**
|
||||
* A ground-projected skybox. The height is how far the camera that took the photo was above the ground -
|
||||
* a larger value will magnify the downward part of the image. By default the object is centered at the camera,
|
||||
* so it is often helpful to set skybox.position.y = height to put the ground at the origin. Set the radius
|
||||
* large enough to ensure your user's camera stays inside.
|
||||
*/
|
||||
|
||||
class GroundedSkybox extends Mesh {
|
||||
|
||||
constructor( map, height, radius, resolution = 128 ) {
|
||||
|
||||
if ( height <= 0 || radius <= 0 || resolution <= 0 ) {
|
||||
|
||||
throw new Error( 'GroundedSkybox height, radius, and resolution must be positive.' );
|
||||
|
||||
}
|
||||
|
||||
const geometry = new SphereGeometry( radius, 2 * resolution, resolution );
|
||||
geometry.scale( 1, 1, -1 );
|
||||
|
||||
const pos = geometry.getAttribute( 'position' );
|
||||
const tmp = new Vector3();
|
||||
|
||||
for ( let i = 0; i < pos.count; ++ i ) {
|
||||
|
||||
tmp.fromBufferAttribute( pos, i );
|
||||
if ( tmp.y < 0 ) {
|
||||
|
||||
// Smooth out the transition from flat floor to sphere:
|
||||
const y1 = - height * 3 / 2;
|
||||
const f =
|
||||
tmp.y < y1 ? - height / tmp.y : ( 1 - tmp.y * tmp.y / ( 3 * y1 * y1 ) );
|
||||
tmp.multiplyScalar( f );
|
||||
tmp.toArray( pos.array, 3 * i );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pos.needsUpdate = true;
|
||||
|
||||
super( geometry, new MeshBasicMaterial( { map, depthWrite: false } ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { GroundedSkybox };
|
21
static/sdk/three/jsm/objects/InstancedPoints.js
Normal file
21
static/sdk/three/jsm/objects/InstancedPoints.js
Normal file
@ -0,0 +1,21 @@
|
||||
import {
|
||||
Mesh
|
||||
} from 'three';
|
||||
import InstancedPointsGeometry from '../geometries/InstancedPointsGeometry.js';
|
||||
import InstancedPointsNodeMaterial from '../nodes/materials/InstancedPointsNodeMaterial.js';
|
||||
|
||||
class InstancedPoints extends Mesh {
|
||||
|
||||
constructor( geometry = new InstancedPointsGeometry(), material = new InstancedPointsNodeMaterial() ) {
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
this.isInstancedPoints = true;
|
||||
|
||||
this.type = 'InstancedPoints';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default InstancedPoints;
|
397
static/sdk/three/jsm/objects/Lensflare.js
Normal file
397
static/sdk/three/jsm/objects/Lensflare.js
Normal file
@ -0,0 +1,397 @@
|
||||
import {
|
||||
AdditiveBlending,
|
||||
Box2,
|
||||
BufferGeometry,
|
||||
Color,
|
||||
FramebufferTexture,
|
||||
InterleavedBuffer,
|
||||
InterleavedBufferAttribute,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
RawShaderMaterial,
|
||||
UnsignedByteType,
|
||||
Vector2,
|
||||
Vector3,
|
||||
Vector4
|
||||
} from 'three';
|
||||
|
||||
class Lensflare extends Mesh {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( Lensflare.Geometry, new MeshBasicMaterial( { opacity: 0, transparent: true } ) );
|
||||
|
||||
this.isLensflare = true;
|
||||
|
||||
this.type = 'Lensflare';
|
||||
this.frustumCulled = false;
|
||||
this.renderOrder = Infinity;
|
||||
|
||||
//
|
||||
|
||||
const positionScreen = new Vector3();
|
||||
const positionView = new Vector3();
|
||||
|
||||
// textures
|
||||
|
||||
const tempMap = new FramebufferTexture( 16, 16 );
|
||||
const occlusionMap = new FramebufferTexture( 16, 16 );
|
||||
|
||||
let currentType = UnsignedByteType;
|
||||
|
||||
// material
|
||||
|
||||
const geometry = Lensflare.Geometry;
|
||||
|
||||
const material1a = new RawShaderMaterial( {
|
||||
uniforms: {
|
||||
'scale': { value: null },
|
||||
'screenPosition': { value: null }
|
||||
},
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec3 screenPosition;
|
||||
uniform vec2 scale;
|
||||
|
||||
attribute vec3 position;
|
||||
|
||||
void main() {
|
||||
|
||||
gl_Position = vec4( position.xy * scale + screenPosition.xy, screenPosition.z, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
void main() {
|
||||
|
||||
gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );
|
||||
|
||||
}`,
|
||||
depthTest: true,
|
||||
depthWrite: false,
|
||||
transparent: false
|
||||
} );
|
||||
|
||||
const material1b = new RawShaderMaterial( {
|
||||
uniforms: {
|
||||
'map': { value: tempMap },
|
||||
'scale': { value: null },
|
||||
'screenPosition': { value: null }
|
||||
},
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec3 screenPosition;
|
||||
uniform vec2 scale;
|
||||
|
||||
attribute vec3 position;
|
||||
attribute vec2 uv;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
|
||||
vUV = uv;
|
||||
|
||||
gl_Position = vec4( position.xy * scale + screenPosition.xy, screenPosition.z, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D map;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
|
||||
gl_FragColor = texture2D( map, vUV );
|
||||
|
||||
}`,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: false
|
||||
} );
|
||||
|
||||
// the following object is used for occlusionMap generation
|
||||
|
||||
const mesh1 = new Mesh( geometry, material1a );
|
||||
|
||||
//
|
||||
|
||||
const elements = [];
|
||||
|
||||
const shader = LensflareElement.Shader;
|
||||
|
||||
const material2 = new RawShaderMaterial( {
|
||||
name: shader.name,
|
||||
uniforms: {
|
||||
'map': { value: null },
|
||||
'occlusionMap': { value: occlusionMap },
|
||||
'color': { value: new Color( 0xffffff ) },
|
||||
'scale': { value: new Vector2() },
|
||||
'screenPosition': { value: new Vector3() }
|
||||
},
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader,
|
||||
blending: AdditiveBlending,
|
||||
transparent: true,
|
||||
depthWrite: false
|
||||
} );
|
||||
|
||||
const mesh2 = new Mesh( geometry, material2 );
|
||||
|
||||
this.addElement = function ( element ) {
|
||||
|
||||
elements.push( element );
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
const scale = new Vector2();
|
||||
const screenPositionPixels = new Vector2();
|
||||
const validArea = new Box2();
|
||||
const viewport = new Vector4();
|
||||
|
||||
this.onBeforeRender = function ( renderer, scene, camera ) {
|
||||
|
||||
renderer.getCurrentViewport( viewport );
|
||||
|
||||
const renderTarget = renderer.getRenderTarget();
|
||||
const type = ( renderTarget !== null ) ? renderTarget.texture.type : UnsignedByteType;
|
||||
|
||||
if ( currentType !== type ) {
|
||||
|
||||
tempMap.dispose();
|
||||
occlusionMap.dispose();
|
||||
|
||||
tempMap.type = occlusionMap.type = type;
|
||||
|
||||
currentType = type;
|
||||
|
||||
}
|
||||
|
||||
const invAspect = viewport.w / viewport.z;
|
||||
const halfViewportWidth = viewport.z / 2.0;
|
||||
const halfViewportHeight = viewport.w / 2.0;
|
||||
|
||||
let size = 16 / viewport.w;
|
||||
scale.set( size * invAspect, size );
|
||||
|
||||
validArea.min.set( viewport.x, viewport.y );
|
||||
validArea.max.set( viewport.x + ( viewport.z - 16 ), viewport.y + ( viewport.w - 16 ) );
|
||||
|
||||
// calculate position in screen space
|
||||
|
||||
positionView.setFromMatrixPosition( this.matrixWorld );
|
||||
positionView.applyMatrix4( camera.matrixWorldInverse );
|
||||
|
||||
if ( positionView.z > 0 ) return; // lensflare is behind the camera
|
||||
|
||||
positionScreen.copy( positionView ).applyMatrix4( camera.projectionMatrix );
|
||||
|
||||
// horizontal and vertical coordinate of the lower left corner of the pixels to copy
|
||||
|
||||
screenPositionPixels.x = viewport.x + ( positionScreen.x * halfViewportWidth ) + halfViewportWidth - 8;
|
||||
screenPositionPixels.y = viewport.y + ( positionScreen.y * halfViewportHeight ) + halfViewportHeight - 8;
|
||||
|
||||
// screen cull
|
||||
|
||||
if ( validArea.containsPoint( screenPositionPixels ) ) {
|
||||
|
||||
// save current RGB to temp texture
|
||||
|
||||
renderer.copyFramebufferToTexture( screenPositionPixels, tempMap );
|
||||
|
||||
// render pink quad
|
||||
|
||||
let uniforms = material1a.uniforms;
|
||||
uniforms[ 'scale' ].value = scale;
|
||||
uniforms[ 'screenPosition' ].value = positionScreen;
|
||||
|
||||
renderer.renderBufferDirect( camera, null, geometry, material1a, mesh1, null );
|
||||
|
||||
// copy result to occlusionMap
|
||||
|
||||
renderer.copyFramebufferToTexture( screenPositionPixels, occlusionMap );
|
||||
|
||||
// restore graphics
|
||||
|
||||
uniforms = material1b.uniforms;
|
||||
uniforms[ 'scale' ].value = scale;
|
||||
uniforms[ 'screenPosition' ].value = positionScreen;
|
||||
|
||||
renderer.renderBufferDirect( camera, null, geometry, material1b, mesh1, null );
|
||||
|
||||
// render elements
|
||||
|
||||
const vecX = - positionScreen.x * 2;
|
||||
const vecY = - positionScreen.y * 2;
|
||||
|
||||
for ( let i = 0, l = elements.length; i < l; i ++ ) {
|
||||
|
||||
const element = elements[ i ];
|
||||
|
||||
const uniforms = material2.uniforms;
|
||||
|
||||
uniforms[ 'color' ].value.copy( element.color );
|
||||
uniforms[ 'map' ].value = element.texture;
|
||||
uniforms[ 'screenPosition' ].value.x = positionScreen.x + vecX * element.distance;
|
||||
uniforms[ 'screenPosition' ].value.y = positionScreen.y + vecY * element.distance;
|
||||
|
||||
size = element.size / viewport.w;
|
||||
const invAspect = viewport.w / viewport.z;
|
||||
|
||||
uniforms[ 'scale' ].value.set( size * invAspect, size );
|
||||
|
||||
material2.uniformsNeedUpdate = true;
|
||||
|
||||
renderer.renderBufferDirect( camera, null, geometry, material2, mesh2, null );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
material1a.dispose();
|
||||
material1b.dispose();
|
||||
material2.dispose();
|
||||
|
||||
tempMap.dispose();
|
||||
occlusionMap.dispose();
|
||||
|
||||
for ( let i = 0, l = elements.length; i < l; i ++ ) {
|
||||
|
||||
elements[ i ].texture.dispose();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class LensflareElement {
|
||||
|
||||
constructor( texture, size = 1, distance = 0, color = new Color( 0xffffff ) ) {
|
||||
|
||||
this.texture = texture;
|
||||
this.size = size;
|
||||
this.distance = distance;
|
||||
this.color = color;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LensflareElement.Shader = {
|
||||
|
||||
name: 'LensflareElementShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'map': { value: null },
|
||||
'occlusionMap': { value: null },
|
||||
'color': { value: null },
|
||||
'scale': { value: null },
|
||||
'screenPosition': { value: null }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec3 screenPosition;
|
||||
uniform vec2 scale;
|
||||
|
||||
uniform sampler2D occlusionMap;
|
||||
|
||||
attribute vec3 position;
|
||||
attribute vec2 uv;
|
||||
|
||||
varying vec2 vUV;
|
||||
varying float vVisibility;
|
||||
|
||||
void main() {
|
||||
|
||||
vUV = uv;
|
||||
|
||||
vec2 pos = position.xy;
|
||||
|
||||
vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );
|
||||
visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );
|
||||
|
||||
vVisibility = visibility.r / 9.0;
|
||||
vVisibility *= 1.0 - visibility.g / 9.0;
|
||||
vVisibility *= visibility.b / 9.0;
|
||||
|
||||
gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D map;
|
||||
uniform vec3 color;
|
||||
|
||||
varying vec2 vUV;
|
||||
varying float vVisibility;
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 texture = texture2D( map, vUV );
|
||||
texture.a *= vVisibility;
|
||||
gl_FragColor = texture;
|
||||
gl_FragColor.rgb *= color;
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
Lensflare.Geometry = ( function () {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
|
||||
const float32Array = new Float32Array( [
|
||||
- 1, - 1, 0, 0, 0,
|
||||
1, - 1, 0, 1, 0,
|
||||
1, 1, 0, 1, 1,
|
||||
- 1, 1, 0, 0, 1
|
||||
] );
|
||||
|
||||
const interleavedBuffer = new InterleavedBuffer( float32Array, 5 );
|
||||
|
||||
geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] );
|
||||
geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) );
|
||||
geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) );
|
||||
|
||||
return geometry;
|
||||
|
||||
} )();
|
||||
|
||||
export { Lensflare, LensflareElement };
|
1176
static/sdk/three/jsm/objects/MarchingCubes.js
Normal file
1176
static/sdk/three/jsm/objects/MarchingCubes.js
Normal file
File diff suppressed because it is too large
Load Diff
50
static/sdk/three/jsm/objects/QuadMesh.js
Normal file
50
static/sdk/three/jsm/objects/QuadMesh.js
Normal file
@ -0,0 +1,50 @@
|
||||
import { BufferGeometry, Float32BufferAttribute, Mesh, OrthographicCamera } from 'three';
|
||||
|
||||
// Helper for passes that need to fill the viewport with a single quad.
|
||||
|
||||
const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
|
||||
// https://github.com/mrdoob/three.js/pull/21358
|
||||
|
||||
class QuadGeometry extends BufferGeometry {
|
||||
|
||||
constructor( flipY = false ) {
|
||||
|
||||
super();
|
||||
|
||||
const uv = flipY === false ? [ 0, - 1, 0, 1, 2, 1 ] : [ 0, 2, 0, 0, 2, 0 ];
|
||||
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) );
|
||||
this.setAttribute( 'uv', new Float32BufferAttribute( uv, 2 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const _geometry = new QuadGeometry();
|
||||
|
||||
class QuadMesh extends Mesh {
|
||||
|
||||
constructor( material = null ) {
|
||||
|
||||
super( _geometry, material );
|
||||
|
||||
this.camera = _camera;
|
||||
|
||||
}
|
||||
|
||||
renderAsync( renderer ) {
|
||||
|
||||
return renderer.renderAsync( this, _camera );
|
||||
|
||||
}
|
||||
|
||||
render( renderer ) {
|
||||
|
||||
renderer.render( this, _camera );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default QuadMesh;
|
264
static/sdk/three/jsm/objects/Reflector.js
Normal file
264
static/sdk/three/jsm/objects/Reflector.js
Normal file
@ -0,0 +1,264 @@
|
||||
import {
|
||||
Color,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
PerspectiveCamera,
|
||||
Plane,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector3,
|
||||
Vector4,
|
||||
WebGLRenderTarget,
|
||||
HalfFloatType
|
||||
} from 'three';
|
||||
|
||||
class Reflector extends Mesh {
|
||||
|
||||
constructor( geometry, options = {} ) {
|
||||
|
||||
super( geometry );
|
||||
|
||||
this.isReflector = true;
|
||||
|
||||
this.type = 'Reflector';
|
||||
this.camera = new PerspectiveCamera();
|
||||
|
||||
const scope = this;
|
||||
|
||||
const color = ( options.color !== undefined ) ? new Color( options.color ) : new Color( 0x7F7F7F );
|
||||
const textureWidth = options.textureWidth || 512;
|
||||
const textureHeight = options.textureHeight || 512;
|
||||
const clipBias = options.clipBias || 0;
|
||||
const shader = options.shader || Reflector.ReflectorShader;
|
||||
const multisample = ( options.multisample !== undefined ) ? options.multisample : 4;
|
||||
|
||||
//
|
||||
|
||||
const reflectorPlane = new Plane();
|
||||
const normal = new Vector3();
|
||||
const reflectorWorldPosition = new Vector3();
|
||||
const cameraWorldPosition = new Vector3();
|
||||
const rotationMatrix = new Matrix4();
|
||||
const lookAtPosition = new Vector3( 0, 0, - 1 );
|
||||
const clipPlane = new Vector4();
|
||||
|
||||
const view = new Vector3();
|
||||
const target = new Vector3();
|
||||
const q = new Vector4();
|
||||
|
||||
const textureMatrix = new Matrix4();
|
||||
const virtualCamera = this.camera;
|
||||
|
||||
const renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, { samples: multisample, type: HalfFloatType } );
|
||||
|
||||
const material = new ShaderMaterial( {
|
||||
name: ( shader.name !== undefined ) ? shader.name : 'unspecified',
|
||||
uniforms: UniformsUtils.clone( shader.uniforms ),
|
||||
fragmentShader: shader.fragmentShader,
|
||||
vertexShader: shader.vertexShader
|
||||
} );
|
||||
|
||||
material.uniforms[ 'tDiffuse' ].value = renderTarget.texture;
|
||||
material.uniforms[ 'color' ].value = color;
|
||||
material.uniforms[ 'textureMatrix' ].value = textureMatrix;
|
||||
|
||||
this.material = material;
|
||||
|
||||
this.onBeforeRender = function ( renderer, scene, camera ) {
|
||||
|
||||
reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
|
||||
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
|
||||
|
||||
rotationMatrix.extractRotation( scope.matrixWorld );
|
||||
|
||||
normal.set( 0, 0, 1 );
|
||||
normal.applyMatrix4( rotationMatrix );
|
||||
|
||||
view.subVectors( reflectorWorldPosition, cameraWorldPosition );
|
||||
|
||||
// Avoid rendering when reflector is facing away
|
||||
|
||||
if ( view.dot( normal ) > 0 ) return;
|
||||
|
||||
view.reflect( normal ).negate();
|
||||
view.add( reflectorWorldPosition );
|
||||
|
||||
rotationMatrix.extractRotation( camera.matrixWorld );
|
||||
|
||||
lookAtPosition.set( 0, 0, - 1 );
|
||||
lookAtPosition.applyMatrix4( rotationMatrix );
|
||||
lookAtPosition.add( cameraWorldPosition );
|
||||
|
||||
target.subVectors( reflectorWorldPosition, lookAtPosition );
|
||||
target.reflect( normal ).negate();
|
||||
target.add( reflectorWorldPosition );
|
||||
|
||||
virtualCamera.position.copy( view );
|
||||
virtualCamera.up.set( 0, 1, 0 );
|
||||
virtualCamera.up.applyMatrix4( rotationMatrix );
|
||||
virtualCamera.up.reflect( normal );
|
||||
virtualCamera.lookAt( target );
|
||||
|
||||
virtualCamera.far = camera.far; // Used in WebGLBackground
|
||||
|
||||
virtualCamera.updateMatrixWorld();
|
||||
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
|
||||
|
||||
// Update the texture matrix
|
||||
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
|
||||
);
|
||||
textureMatrix.multiply( virtualCamera.projectionMatrix );
|
||||
textureMatrix.multiply( virtualCamera.matrixWorldInverse );
|
||||
textureMatrix.multiply( scope.matrixWorld );
|
||||
|
||||
// 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
|
||||
reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );
|
||||
reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
|
||||
|
||||
clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );
|
||||
|
||||
const projectionMatrix = virtualCamera.projectionMatrix;
|
||||
|
||||
q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
|
||||
q.y = ( Math.sign( clipPlane.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
|
||||
clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
|
||||
|
||||
// Replacing the third row of the projection matrix
|
||||
projectionMatrix.elements[ 2 ] = clipPlane.x;
|
||||
projectionMatrix.elements[ 6 ] = clipPlane.y;
|
||||
projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
|
||||
projectionMatrix.elements[ 14 ] = clipPlane.w;
|
||||
|
||||
// Render
|
||||
scope.visible = false;
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
|
||||
const currentXrEnabled = renderer.xr.enabled;
|
||||
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
||||
|
||||
renderer.xr.enabled = false; // Avoid camera modification
|
||||
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
|
||||
|
||||
if ( renderer.autoClear === false ) renderer.clear();
|
||||
renderer.render( scene, virtualCamera );
|
||||
|
||||
renderer.xr.enabled = currentXrEnabled;
|
||||
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
||||
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
// Restore viewport
|
||||
|
||||
const viewport = camera.viewport;
|
||||
|
||||
if ( viewport !== undefined ) {
|
||||
|
||||
renderer.state.viewport( viewport );
|
||||
|
||||
}
|
||||
|
||||
scope.visible = true;
|
||||
|
||||
};
|
||||
|
||||
this.getRenderTarget = function () {
|
||||
|
||||
return renderTarget;
|
||||
|
||||
};
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
renderTarget.dispose();
|
||||
scope.material.dispose();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reflector.ReflectorShader = {
|
||||
|
||||
name: 'ReflectorShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'color': {
|
||||
value: null
|
||||
},
|
||||
|
||||
'tDiffuse': {
|
||||
value: null
|
||||
},
|
||||
|
||||
'textureMatrix': {
|
||||
value: null
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
uniform mat4 textureMatrix;
|
||||
varying vec4 vUv;
|
||||
|
||||
#include <common>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = textureMatrix * vec4( position, 1.0 );
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
#include <logdepthbuf_vertex>
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
uniform vec3 color;
|
||||
uniform sampler2D tDiffuse;
|
||||
varying vec4 vUv;
|
||||
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
|
||||
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() {
|
||||
|
||||
#include <logdepthbuf_fragment>
|
||||
|
||||
vec4 base = texture2DProj( tDiffuse, vUv );
|
||||
gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );
|
||||
|
||||
#include <tonemapping_fragment>
|
||||
#include <colorspace_fragment>
|
||||
|
||||
}`
|
||||
};
|
||||
|
||||
export { Reflector };
|
352
static/sdk/three/jsm/objects/ReflectorForSSRPass.js
Normal file
352
static/sdk/three/jsm/objects/ReflectorForSSRPass.js
Normal file
@ -0,0 +1,352 @@
|
||||
import {
|
||||
Color,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
PerspectiveCamera,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector2,
|
||||
Vector3,
|
||||
WebGLRenderTarget,
|
||||
DepthTexture,
|
||||
UnsignedShortType,
|
||||
NearestFilter,
|
||||
Plane,
|
||||
HalfFloatType
|
||||
} from 'three';
|
||||
|
||||
class ReflectorForSSRPass extends Mesh {
|
||||
|
||||
constructor( geometry, options = {} ) {
|
||||
|
||||
super( geometry );
|
||||
|
||||
this.isReflectorForSSRPass = true;
|
||||
|
||||
this.type = 'ReflectorForSSRPass';
|
||||
|
||||
const scope = this;
|
||||
|
||||
const color = ( options.color !== undefined ) ? new Color( options.color ) : new Color( 0x7F7F7F );
|
||||
const textureWidth = options.textureWidth || 512;
|
||||
const textureHeight = options.textureHeight || 512;
|
||||
const clipBias = options.clipBias || 0;
|
||||
const shader = options.shader || ReflectorForSSRPass.ReflectorShader;
|
||||
const useDepthTexture = options.useDepthTexture === true;
|
||||
const yAxis = new Vector3( 0, 1, 0 );
|
||||
const vecTemp0 = new Vector3();
|
||||
const vecTemp1 = new Vector3();
|
||||
|
||||
//
|
||||
|
||||
scope.needsUpdate = false;
|
||||
scope.maxDistance = ReflectorForSSRPass.ReflectorShader.uniforms.maxDistance.value;
|
||||
scope.opacity = ReflectorForSSRPass.ReflectorShader.uniforms.opacity.value;
|
||||
scope.color = color;
|
||||
scope.resolution = options.resolution || new Vector2( window.innerWidth, window.innerHeight );
|
||||
|
||||
|
||||
scope._distanceAttenuation = ReflectorForSSRPass.ReflectorShader.defines.DISTANCE_ATTENUATION;
|
||||
Object.defineProperty( scope, 'distanceAttenuation', {
|
||||
get() {
|
||||
|
||||
return scope._distanceAttenuation;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( scope._distanceAttenuation === val ) return;
|
||||
scope._distanceAttenuation = val;
|
||||
scope.material.defines.DISTANCE_ATTENUATION = val;
|
||||
scope.material.needsUpdate = true;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
scope._fresnel = ReflectorForSSRPass.ReflectorShader.defines.FRESNEL;
|
||||
Object.defineProperty( scope, 'fresnel', {
|
||||
get() {
|
||||
|
||||
return scope._fresnel;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( scope._fresnel === val ) return;
|
||||
scope._fresnel = val;
|
||||
scope.material.defines.FRESNEL = val;
|
||||
scope.material.needsUpdate = true;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
const normal = new Vector3();
|
||||
const reflectorWorldPosition = new Vector3();
|
||||
const cameraWorldPosition = new Vector3();
|
||||
const rotationMatrix = new Matrix4();
|
||||
const lookAtPosition = new Vector3( 0, 0, - 1 );
|
||||
|
||||
const view = new Vector3();
|
||||
const target = new Vector3();
|
||||
|
||||
const textureMatrix = new Matrix4();
|
||||
const virtualCamera = new PerspectiveCamera();
|
||||
|
||||
let depthTexture;
|
||||
|
||||
if ( useDepthTexture ) {
|
||||
|
||||
depthTexture = new DepthTexture();
|
||||
depthTexture.type = UnsignedShortType;
|
||||
depthTexture.minFilter = NearestFilter;
|
||||
depthTexture.magFilter = NearestFilter;
|
||||
|
||||
}
|
||||
|
||||
const parameters = {
|
||||
depthTexture: useDepthTexture ? depthTexture : null,
|
||||
type: HalfFloatType
|
||||
};
|
||||
|
||||
const renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, parameters );
|
||||
|
||||
const material = new ShaderMaterial( {
|
||||
name: ( shader.name !== undefined ) ? shader.name : 'unspecified',
|
||||
transparent: useDepthTexture,
|
||||
defines: Object.assign( {}, ReflectorForSSRPass.ReflectorShader.defines, {
|
||||
useDepthTexture
|
||||
} ),
|
||||
uniforms: UniformsUtils.clone( shader.uniforms ),
|
||||
fragmentShader: shader.fragmentShader,
|
||||
vertexShader: shader.vertexShader
|
||||
} );
|
||||
|
||||
material.uniforms[ 'tDiffuse' ].value = renderTarget.texture;
|
||||
material.uniforms[ 'color' ].value = scope.color;
|
||||
material.uniforms[ 'textureMatrix' ].value = textureMatrix;
|
||||
if ( useDepthTexture ) {
|
||||
|
||||
material.uniforms[ 'tDepth' ].value = renderTarget.depthTexture;
|
||||
|
||||
}
|
||||
|
||||
this.material = material;
|
||||
|
||||
const globalPlane = new Plane( new Vector3( 0, 1, 0 ), clipBias );
|
||||
const globalPlanes = [ globalPlane ];
|
||||
|
||||
this.doRender = function ( renderer, scene, camera ) {
|
||||
|
||||
material.uniforms[ 'maxDistance' ].value = scope.maxDistance;
|
||||
material.uniforms[ 'color' ].value = scope.color;
|
||||
material.uniforms[ 'opacity' ].value = scope.opacity;
|
||||
|
||||
vecTemp0.copy( camera.position ).normalize();
|
||||
vecTemp1.copy( vecTemp0 ).reflect( yAxis );
|
||||
material.uniforms[ 'fresnelCoe' ].value = ( vecTemp0.dot( vecTemp1 ) + 1. ) / 2.; // TODO: Also need to use glsl viewPosition and viewNormal per pixel.
|
||||
|
||||
reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
|
||||
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
|
||||
|
||||
rotationMatrix.extractRotation( scope.matrixWorld );
|
||||
|
||||
normal.set( 0, 0, 1 );
|
||||
normal.applyMatrix4( rotationMatrix );
|
||||
|
||||
view.subVectors( reflectorWorldPosition, cameraWorldPosition );
|
||||
|
||||
// Avoid rendering when reflector is facing away
|
||||
|
||||
if ( view.dot( normal ) > 0 ) return;
|
||||
|
||||
view.reflect( normal ).negate();
|
||||
view.add( reflectorWorldPosition );
|
||||
|
||||
rotationMatrix.extractRotation( camera.matrixWorld );
|
||||
|
||||
lookAtPosition.set( 0, 0, - 1 );
|
||||
lookAtPosition.applyMatrix4( rotationMatrix );
|
||||
lookAtPosition.add( cameraWorldPosition );
|
||||
|
||||
target.subVectors( reflectorWorldPosition, lookAtPosition );
|
||||
target.reflect( normal ).negate();
|
||||
target.add( reflectorWorldPosition );
|
||||
|
||||
virtualCamera.position.copy( view );
|
||||
virtualCamera.up.set( 0, 1, 0 );
|
||||
virtualCamera.up.applyMatrix4( rotationMatrix );
|
||||
virtualCamera.up.reflect( normal );
|
||||
virtualCamera.lookAt( target );
|
||||
|
||||
virtualCamera.far = camera.far; // Used in WebGLBackground
|
||||
|
||||
virtualCamera.updateMatrixWorld();
|
||||
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
|
||||
|
||||
material.uniforms[ 'virtualCameraNear' ].value = camera.near;
|
||||
material.uniforms[ 'virtualCameraFar' ].value = camera.far;
|
||||
material.uniforms[ 'virtualCameraMatrixWorld' ].value = virtualCamera.matrixWorld;
|
||||
material.uniforms[ 'virtualCameraProjectionMatrix' ].value = camera.projectionMatrix;
|
||||
material.uniforms[ 'virtualCameraProjectionMatrixInverse' ].value = camera.projectionMatrixInverse;
|
||||
material.uniforms[ 'resolution' ].value = scope.resolution;
|
||||
|
||||
// Update the texture matrix
|
||||
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
|
||||
);
|
||||
textureMatrix.multiply( virtualCamera.projectionMatrix );
|
||||
textureMatrix.multiply( virtualCamera.matrixWorldInverse );
|
||||
textureMatrix.multiply( scope.matrixWorld );
|
||||
|
||||
// scope.visible = false;
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
|
||||
const currentXrEnabled = renderer.xr.enabled;
|
||||
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
||||
const currentClippingPlanes = renderer.clippingPlanes;
|
||||
|
||||
renderer.xr.enabled = false; // Avoid camera modification
|
||||
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
|
||||
renderer.clippingPlanes = globalPlanes;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
|
||||
|
||||
if ( renderer.autoClear === false ) renderer.clear();
|
||||
renderer.render( scene, virtualCamera );
|
||||
|
||||
renderer.xr.enabled = currentXrEnabled;
|
||||
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
||||
renderer.clippingPlanes = currentClippingPlanes;
|
||||
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
// Restore viewport
|
||||
|
||||
const viewport = camera.viewport;
|
||||
|
||||
if ( viewport !== undefined ) {
|
||||
|
||||
renderer.state.viewport( viewport );
|
||||
|
||||
}
|
||||
|
||||
// scope.visible = true;
|
||||
|
||||
};
|
||||
|
||||
this.getRenderTarget = function () {
|
||||
|
||||
return renderTarget;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ReflectorForSSRPass.ReflectorShader = {
|
||||
|
||||
name: 'ReflectorShader',
|
||||
|
||||
defines: {
|
||||
DISTANCE_ATTENUATION: true,
|
||||
FRESNEL: true,
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
|
||||
color: { value: null },
|
||||
tDiffuse: { value: null },
|
||||
tDepth: { value: null },
|
||||
textureMatrix: { value: new Matrix4() },
|
||||
maxDistance: { value: 180 },
|
||||
opacity: { value: 0.5 },
|
||||
fresnelCoe: { value: null },
|
||||
virtualCameraNear: { value: null },
|
||||
virtualCameraFar: { value: null },
|
||||
virtualCameraProjectionMatrix: { value: new Matrix4() },
|
||||
virtualCameraMatrixWorld: { value: new Matrix4() },
|
||||
virtualCameraProjectionMatrixInverse: { value: new Matrix4() },
|
||||
resolution: { value: new Vector2() },
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
uniform mat4 textureMatrix;
|
||||
varying vec4 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = textureMatrix * vec4( position, 1.0 );
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
uniform vec3 color;
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform sampler2D tDepth;
|
||||
uniform float maxDistance;
|
||||
uniform float opacity;
|
||||
uniform float fresnelCoe;
|
||||
uniform float virtualCameraNear;
|
||||
uniform float virtualCameraFar;
|
||||
uniform mat4 virtualCameraProjectionMatrix;
|
||||
uniform mat4 virtualCameraProjectionMatrixInverse;
|
||||
uniform mat4 virtualCameraMatrixWorld;
|
||||
uniform vec2 resolution;
|
||||
varying vec4 vUv;
|
||||
#include <packing>
|
||||
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 ) );
|
||||
}
|
||||
float getDepth( const in vec2 uv ) {
|
||||
return texture2D( tDepth, uv ).x;
|
||||
}
|
||||
float getViewZ( const in float depth ) {
|
||||
return perspectiveDepthToViewZ( depth, virtualCameraNear, virtualCameraFar );
|
||||
}
|
||||
vec3 getViewPosition( const in vec2 uv, const in float depth/*clip space*/, const in float clipW ) {
|
||||
vec4 clipPosition = vec4( ( vec3( uv, depth ) - 0.5 ) * 2.0, 1.0 );//ndc
|
||||
clipPosition *= clipW; //clip
|
||||
return ( virtualCameraProjectionMatrixInverse * clipPosition ).xyz;//view
|
||||
}
|
||||
void main() {
|
||||
vec4 base = texture2DProj( tDiffuse, vUv );
|
||||
#ifdef useDepthTexture
|
||||
vec2 uv=(gl_FragCoord.xy-.5)/resolution.xy;
|
||||
uv.x=1.-uv.x;
|
||||
float depth = texture2DProj( tDepth, vUv ).r;
|
||||
float viewZ = getViewZ( depth );
|
||||
float clipW = virtualCameraProjectionMatrix[2][3] * viewZ+virtualCameraProjectionMatrix[3][3];
|
||||
vec3 viewPosition=getViewPosition( uv, depth, clipW );
|
||||
vec3 worldPosition=(virtualCameraMatrixWorld*vec4(viewPosition,1)).xyz;
|
||||
if(worldPosition.y>maxDistance) discard;
|
||||
float op=opacity;
|
||||
#ifdef DISTANCE_ATTENUATION
|
||||
float ratio=1.-(worldPosition.y/maxDistance);
|
||||
float attenuation=ratio*ratio;
|
||||
op=opacity*attenuation;
|
||||
#endif
|
||||
#ifdef FRESNEL
|
||||
op*=fresnelCoe;
|
||||
#endif
|
||||
gl_FragColor = vec4( blendOverlay( base.rgb, color ), op );
|
||||
#else
|
||||
gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );
|
||||
#endif
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export { ReflectorForSSRPass };
|
327
static/sdk/three/jsm/objects/Refractor.js
Normal file
327
static/sdk/three/jsm/objects/Refractor.js
Normal file
@ -0,0 +1,327 @@
|
||||
import {
|
||||
Color,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
PerspectiveCamera,
|
||||
Plane,
|
||||
Quaternion,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector3,
|
||||
Vector4,
|
||||
WebGLRenderTarget,
|
||||
HalfFloatType
|
||||
} from 'three';
|
||||
|
||||
class Refractor extends Mesh {
|
||||
|
||||
constructor( geometry, options = {} ) {
|
||||
|
||||
super( geometry );
|
||||
|
||||
this.isRefractor = true;
|
||||
|
||||
this.type = 'Refractor';
|
||||
this.camera = new PerspectiveCamera();
|
||||
|
||||
const scope = this;
|
||||
|
||||
const color = ( options.color !== undefined ) ? new Color( options.color ) : new Color( 0x7F7F7F );
|
||||
const textureWidth = options.textureWidth || 512;
|
||||
const textureHeight = options.textureHeight || 512;
|
||||
const clipBias = options.clipBias || 0;
|
||||
const shader = options.shader || Refractor.RefractorShader;
|
||||
const multisample = ( options.multisample !== undefined ) ? options.multisample : 4;
|
||||
|
||||
//
|
||||
|
||||
const virtualCamera = this.camera;
|
||||
virtualCamera.matrixAutoUpdate = false;
|
||||
virtualCamera.userData.refractor = true;
|
||||
|
||||
//
|
||||
|
||||
const refractorPlane = new Plane();
|
||||
const textureMatrix = new Matrix4();
|
||||
|
||||
// render target
|
||||
|
||||
const renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, { samples: multisample, type: HalfFloatType } );
|
||||
|
||||
// material
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
name: ( shader.name !== undefined ) ? shader.name : 'unspecified',
|
||||
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
|
||||
|
||||
const visible = ( function () {
|
||||
|
||||
const refractorWorldPosition = new Vector3();
|
||||
const cameraWorldPosition = new Vector3();
|
||||
const rotationMatrix = new Matrix4();
|
||||
|
||||
const view = new Vector3();
|
||||
const 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;
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
const updateRefractorPlane = ( function () {
|
||||
|
||||
const normal = new Vector3();
|
||||
const position = new Vector3();
|
||||
const quaternion = new Quaternion();
|
||||
const 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 );
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
const updateVirtualCamera = ( function () {
|
||||
|
||||
const clipPlane = new Plane();
|
||||
const clipVector = new Vector4();
|
||||
const q = new Vector4();
|
||||
|
||||
return function updateVirtualCamera( camera ) {
|
||||
|
||||
virtualCamera.matrixWorld.copy( camera.matrixWorld );
|
||||
virtualCamera.matrixWorldInverse.copy( virtualCamera.matrixWorld ).invert();
|
||||
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
|
||||
|
||||
const 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;
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
const currentXrEnabled = renderer.xr.enabled;
|
||||
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
||||
|
||||
renderer.xr.enabled = false; // avoid camera modification
|
||||
renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
if ( renderer.autoClear === false ) renderer.clear();
|
||||
renderer.render( scene, virtualCamera );
|
||||
|
||||
renderer.xr.enabled = currentXrEnabled;
|
||||
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
// restore viewport
|
||||
|
||||
const viewport = camera.viewport;
|
||||
|
||||
if ( viewport !== undefined ) {
|
||||
|
||||
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();
|
||||
|
||||
updateTextureMatrix( camera );
|
||||
|
||||
updateVirtualCamera( camera );
|
||||
|
||||
render( renderer, scene, camera );
|
||||
|
||||
};
|
||||
|
||||
this.getRenderTarget = function () {
|
||||
|
||||
return renderTarget;
|
||||
|
||||
};
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
renderTarget.dispose();
|
||||
scope.material.dispose();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Refractor.RefractorShader = {
|
||||
|
||||
name: 'RefractorShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'color': {
|
||||
value: null
|
||||
},
|
||||
|
||||
'tDiffuse': {
|
||||
value: null
|
||||
},
|
||||
|
||||
'textureMatrix': {
|
||||
value: null
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
uniform mat4 textureMatrix;
|
||||
|
||||
varying vec4 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = textureMatrix * vec4( position, 1.0 );
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
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 );
|
||||
|
||||
#include <tonemapping_fragment>
|
||||
#include <colorspace_fragment>
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { Refractor };
|
80
static/sdk/three/jsm/objects/ShadowMesh.js
Normal file
80
static/sdk/three/jsm/objects/ShadowMesh.js
Normal file
@ -0,0 +1,80 @@
|
||||
import {
|
||||
Matrix4,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
EqualStencilFunc,
|
||||
IncrementStencilOp
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* A shadow Mesh that follows a shadow-casting Mesh in the scene, but is confined to a single plane.
|
||||
*/
|
||||
|
||||
const _shadowMatrix = new Matrix4();
|
||||
|
||||
class ShadowMesh extends Mesh {
|
||||
|
||||
constructor( mesh ) {
|
||||
|
||||
const shadowMaterial = new MeshBasicMaterial( {
|
||||
|
||||
color: 0x000000,
|
||||
transparent: true,
|
||||
opacity: 0.6,
|
||||
depthWrite: false,
|
||||
stencilWrite: true,
|
||||
stencilFunc: EqualStencilFunc,
|
||||
stencilRef: 0,
|
||||
stencilZPass: IncrementStencilOp
|
||||
|
||||
} );
|
||||
|
||||
super( mesh.geometry, shadowMaterial );
|
||||
|
||||
this.isShadowMesh = true;
|
||||
|
||||
this.meshMatrix = mesh.matrixWorld;
|
||||
|
||||
this.frustumCulled = false;
|
||||
this.matrixAutoUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
update( plane, lightPosition4D ) {
|
||||
|
||||
// based on https://www.opengl.org/archives/resources/features/StencilTalk/tsld021.htm
|
||||
|
||||
const dot = plane.normal.x * lightPosition4D.x +
|
||||
plane.normal.y * lightPosition4D.y +
|
||||
plane.normal.z * lightPosition4D.z +
|
||||
- plane.constant * lightPosition4D.w;
|
||||
|
||||
const sme = _shadowMatrix.elements;
|
||||
|
||||
sme[ 0 ] = dot - lightPosition4D.x * plane.normal.x;
|
||||
sme[ 4 ] = - lightPosition4D.x * plane.normal.y;
|
||||
sme[ 8 ] = - lightPosition4D.x * plane.normal.z;
|
||||
sme[ 12 ] = - lightPosition4D.x * - plane.constant;
|
||||
|
||||
sme[ 1 ] = - lightPosition4D.y * plane.normal.x;
|
||||
sme[ 5 ] = dot - lightPosition4D.y * plane.normal.y;
|
||||
sme[ 9 ] = - lightPosition4D.y * plane.normal.z;
|
||||
sme[ 13 ] = - lightPosition4D.y * - plane.constant;
|
||||
|
||||
sme[ 2 ] = - lightPosition4D.z * plane.normal.x;
|
||||
sme[ 6 ] = - lightPosition4D.z * plane.normal.y;
|
||||
sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z;
|
||||
sme[ 14 ] = - lightPosition4D.z * - plane.constant;
|
||||
|
||||
sme[ 3 ] = - lightPosition4D.w * plane.normal.x;
|
||||
sme[ 7 ] = - lightPosition4D.w * plane.normal.y;
|
||||
sme[ 11 ] = - lightPosition4D.w * plane.normal.z;
|
||||
sme[ 15 ] = dot - lightPosition4D.w * - plane.constant;
|
||||
|
||||
this.matrix.multiplyMatrices( _shadowMatrix, this.meshMatrix );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ShadowMesh };
|
219
static/sdk/three/jsm/objects/Sky.js
Normal file
219
static/sdk/three/jsm/objects/Sky.js
Normal file
@ -0,0 +1,219 @@
|
||||
import {
|
||||
BackSide,
|
||||
BoxGeometry,
|
||||
Mesh,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* Based on "A Practical Analytic Model for Daylight"
|
||||
* aka The Preetham Model, the de facto standard analytic skydome model
|
||||
* https://www.researchgate.net/publication/220720443_A_Practical_Analytic_Model_for_Daylight
|
||||
*
|
||||
* First implemented by Simon Wallner
|
||||
* http://simonwallner.at/project/atmospheric-scattering/
|
||||
*
|
||||
* Improved by Martin Upitis
|
||||
* http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
|
||||
*
|
||||
* Three.js integration by zz85 http://twitter.com/blurspline
|
||||
*/
|
||||
|
||||
class Sky extends Mesh {
|
||||
|
||||
constructor() {
|
||||
|
||||
const shader = Sky.SkyShader;
|
||||
|
||||
const material = new ShaderMaterial( {
|
||||
name: shader.name,
|
||||
uniforms: UniformsUtils.clone( shader.uniforms ),
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader,
|
||||
side: BackSide,
|
||||
depthWrite: false
|
||||
} );
|
||||
|
||||
super( new BoxGeometry( 1, 1, 1 ), material );
|
||||
|
||||
this.isSky = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Sky.SkyShader = {
|
||||
|
||||
name: 'SkyShader',
|
||||
|
||||
uniforms: {
|
||||
'turbidity': { value: 2 },
|
||||
'rayleigh': { value: 1 },
|
||||
'mieCoefficient': { value: 0.005 },
|
||||
'mieDirectionalG': { value: 0.8 },
|
||||
'sunPosition': { value: new Vector3() },
|
||||
'up': { value: new Vector3( 0, 1, 0 ) }
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
uniform vec3 sunPosition;
|
||||
uniform float rayleigh;
|
||||
uniform float turbidity;
|
||||
uniform float mieCoefficient;
|
||||
uniform vec3 up;
|
||||
|
||||
varying vec3 vWorldPosition;
|
||||
varying vec3 vSunDirection;
|
||||
varying float vSunfade;
|
||||
varying vec3 vBetaR;
|
||||
varying vec3 vBetaM;
|
||||
varying float vSunE;
|
||||
|
||||
// constants for atmospheric scattering
|
||||
const float e = 2.71828182845904523536028747135266249775724709369995957;
|
||||
const float pi = 3.141592653589793238462643383279502884197169;
|
||||
|
||||
// wavelength of used primaries, according to preetham
|
||||
const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );
|
||||
// this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function:
|
||||
// (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn))
|
||||
const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );
|
||||
|
||||
// mie stuff
|
||||
// K coefficient for the primaries
|
||||
const float v = 4.0;
|
||||
const vec3 K = vec3( 0.686, 0.678, 0.666 );
|
||||
// MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K
|
||||
const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );
|
||||
|
||||
// earth shadow hack
|
||||
// cutoffAngle = pi / 1.95;
|
||||
const float cutoffAngle = 1.6110731556870734;
|
||||
const float steepness = 1.5;
|
||||
const float EE = 1000.0;
|
||||
|
||||
float sunIntensity( float zenithAngleCos ) {
|
||||
zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );
|
||||
return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );
|
||||
}
|
||||
|
||||
vec3 totalMie( float T ) {
|
||||
float c = ( 0.2 * T ) * 10E-18;
|
||||
return 0.434 * c * MieConst;
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
|
||||
vWorldPosition = worldPosition.xyz;
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
gl_Position.z = gl_Position.w; // set z to camera.far
|
||||
|
||||
vSunDirection = normalize( sunPosition );
|
||||
|
||||
vSunE = sunIntensity( dot( vSunDirection, up ) );
|
||||
|
||||
vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );
|
||||
|
||||
float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );
|
||||
|
||||
// extinction (absorbtion + out scattering)
|
||||
// rayleigh coefficients
|
||||
vBetaR = totalRayleigh * rayleighCoefficient;
|
||||
|
||||
// mie coefficients
|
||||
vBetaM = totalMie( turbidity ) * mieCoefficient;
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
varying vec3 vWorldPosition;
|
||||
varying vec3 vSunDirection;
|
||||
varying float vSunfade;
|
||||
varying vec3 vBetaR;
|
||||
varying vec3 vBetaM;
|
||||
varying float vSunE;
|
||||
|
||||
uniform float mieDirectionalG;
|
||||
uniform vec3 up;
|
||||
|
||||
// constants for atmospheric scattering
|
||||
const float pi = 3.141592653589793238462643383279502884197169;
|
||||
|
||||
const float n = 1.0003; // refractive index of air
|
||||
const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius)
|
||||
|
||||
// optical length at zenith for molecules
|
||||
const float rayleighZenithLength = 8.4E3;
|
||||
const float mieZenithLength = 1.25E3;
|
||||
// 66 arc seconds -> degrees, and the cosine of that
|
||||
const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;
|
||||
|
||||
// 3.0 / ( 16.0 * pi )
|
||||
const float THREE_OVER_SIXTEENPI = 0.05968310365946075;
|
||||
// 1.0 / ( 4.0 * pi )
|
||||
const float ONE_OVER_FOURPI = 0.07957747154594767;
|
||||
|
||||
float rayleighPhase( float cosTheta ) {
|
||||
return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );
|
||||
}
|
||||
|
||||
float hgPhase( float cosTheta, float g ) {
|
||||
float g2 = pow( g, 2.0 );
|
||||
float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );
|
||||
return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
vec3 direction = normalize( vWorldPosition - cameraPosition );
|
||||
|
||||
// optical length
|
||||
// cutoff angle at 90 to avoid singularity in next formula.
|
||||
float zenithAngle = acos( max( 0.0, dot( up, direction ) ) );
|
||||
float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );
|
||||
float sR = rayleighZenithLength * inverse;
|
||||
float sM = mieZenithLength * inverse;
|
||||
|
||||
// combined extinction factor
|
||||
vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );
|
||||
|
||||
// in scattering
|
||||
float cosTheta = dot( direction, vSunDirection );
|
||||
|
||||
float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );
|
||||
vec3 betaRTheta = vBetaR * rPhase;
|
||||
|
||||
float mPhase = hgPhase( cosTheta, mieDirectionalG );
|
||||
vec3 betaMTheta = vBetaM * mPhase;
|
||||
|
||||
vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );
|
||||
Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );
|
||||
|
||||
// nightsky
|
||||
float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]
|
||||
float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]
|
||||
vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );
|
||||
vec3 L0 = vec3( 0.1 ) * Fex;
|
||||
|
||||
// composition + solar disc
|
||||
float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );
|
||||
L0 += ( vSunE * 19000.0 * Fex ) * sundisk;
|
||||
|
||||
vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );
|
||||
|
||||
vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );
|
||||
|
||||
gl_FragColor = vec4( retColor, 1.0 );
|
||||
|
||||
#include <tonemapping_fragment>
|
||||
#include <colorspace_fragment>
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { Sky };
|
333
static/sdk/three/jsm/objects/Water.js
Normal file
333
static/sdk/three/jsm/objects/Water.js
Normal file
@ -0,0 +1,333 @@
|
||||
import {
|
||||
Color,
|
||||
FrontSide,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
PerspectiveCamera,
|
||||
Plane,
|
||||
ShaderMaterial,
|
||||
UniformsLib,
|
||||
UniformsUtils,
|
||||
Vector3,
|
||||
Vector4,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* Work based on :
|
||||
* https://github.com/Slayvin: Flat mirror for three.js
|
||||
* https://home.adelphi.edu/~stemkoski/ : An implementation of water shader based on the flat mirror
|
||||
* http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL
|
||||
*/
|
||||
|
||||
class Water extends Mesh {
|
||||
|
||||
constructor( geometry, options = {} ) {
|
||||
|
||||
super( geometry );
|
||||
|
||||
this.isWater = true;
|
||||
|
||||
const scope = this;
|
||||
|
||||
const textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
|
||||
const textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
|
||||
|
||||
const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
|
||||
const alpha = options.alpha !== undefined ? options.alpha : 1.0;
|
||||
const time = options.time !== undefined ? options.time : 0.0;
|
||||
const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null;
|
||||
const sunDirection = options.sunDirection !== undefined ? options.sunDirection : new Vector3( 0.70707, 0.70707, 0.0 );
|
||||
const sunColor = new Color( options.sunColor !== undefined ? options.sunColor : 0xffffff );
|
||||
const waterColor = new Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F );
|
||||
const eye = options.eye !== undefined ? options.eye : new Vector3( 0, 0, 0 );
|
||||
const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0;
|
||||
const side = options.side !== undefined ? options.side : FrontSide;
|
||||
const fog = options.fog !== undefined ? options.fog : false;
|
||||
|
||||
//
|
||||
|
||||
const mirrorPlane = new Plane();
|
||||
const normal = new Vector3();
|
||||
const mirrorWorldPosition = new Vector3();
|
||||
const cameraWorldPosition = new Vector3();
|
||||
const rotationMatrix = new Matrix4();
|
||||
const lookAtPosition = new Vector3( 0, 0, - 1 );
|
||||
const clipPlane = new Vector4();
|
||||
|
||||
const view = new Vector3();
|
||||
const target = new Vector3();
|
||||
const q = new Vector4();
|
||||
|
||||
const textureMatrix = new Matrix4();
|
||||
|
||||
const mirrorCamera = new PerspectiveCamera();
|
||||
|
||||
const renderTarget = new WebGLRenderTarget( textureWidth, textureHeight );
|
||||
|
||||
const mirrorShader = {
|
||||
|
||||
name: 'MirrorShader',
|
||||
|
||||
uniforms: UniformsUtils.merge( [
|
||||
UniformsLib[ 'fog' ],
|
||||
UniformsLib[ 'lights' ],
|
||||
{
|
||||
'normalSampler': { value: null },
|
||||
'mirrorSampler': { value: null },
|
||||
'alpha': { value: 1.0 },
|
||||
'time': { value: 0.0 },
|
||||
'size': { value: 1.0 },
|
||||
'distortionScale': { value: 20.0 },
|
||||
'textureMatrix': { value: new Matrix4() },
|
||||
'sunColor': { value: new Color( 0x7F7F7F ) },
|
||||
'sunDirection': { value: new Vector3( 0.70707, 0.70707, 0 ) },
|
||||
'eye': { value: new Vector3() },
|
||||
'waterColor': { value: new Color( 0x555555 ) }
|
||||
}
|
||||
] ),
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
uniform mat4 textureMatrix;
|
||||
uniform float time;
|
||||
|
||||
varying vec4 mirrorCoord;
|
||||
varying vec4 worldPosition;
|
||||
|
||||
#include <common>
|
||||
#include <fog_pars_vertex>
|
||||
#include <shadowmap_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
|
||||
void main() {
|
||||
mirrorCoord = modelMatrix * vec4( position, 1.0 );
|
||||
worldPosition = mirrorCoord.xyzw;
|
||||
mirrorCoord = textureMatrix * mirrorCoord;
|
||||
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
|
||||
#include <beginnormal_vertex>
|
||||
#include <defaultnormal_vertex>
|
||||
#include <logdepthbuf_vertex>
|
||||
#include <fog_vertex>
|
||||
#include <shadowmap_vertex>
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
uniform sampler2D mirrorSampler;
|
||||
uniform float alpha;
|
||||
uniform float time;
|
||||
uniform float size;
|
||||
uniform float distortionScale;
|
||||
uniform sampler2D normalSampler;
|
||||
uniform vec3 sunColor;
|
||||
uniform vec3 sunDirection;
|
||||
uniform vec3 eye;
|
||||
uniform vec3 waterColor;
|
||||
|
||||
varying vec4 mirrorCoord;
|
||||
varying vec4 worldPosition;
|
||||
|
||||
vec4 getNoise( vec2 uv ) {
|
||||
vec2 uv0 = ( uv / 103.0 ) + vec2(time / 17.0, time / 29.0);
|
||||
vec2 uv1 = uv / 107.0-vec2( time / -19.0, time / 31.0 );
|
||||
vec2 uv2 = uv / vec2( 8907.0, 9803.0 ) + vec2( time / 101.0, time / 97.0 );
|
||||
vec2 uv3 = uv / vec2( 1091.0, 1027.0 ) - vec2( time / 109.0, time / -113.0 );
|
||||
vec4 noise = texture2D( normalSampler, uv0 ) +
|
||||
texture2D( normalSampler, uv1 ) +
|
||||
texture2D( normalSampler, uv2 ) +
|
||||
texture2D( normalSampler, uv3 );
|
||||
return noise * 0.5 - 1.0;
|
||||
}
|
||||
|
||||
void sunLight( const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor ) {
|
||||
vec3 reflection = normalize( reflect( -sunDirection, surfaceNormal ) );
|
||||
float direction = max( 0.0, dot( eyeDirection, reflection ) );
|
||||
specularColor += pow( direction, shiny ) * sunColor * spec;
|
||||
diffuseColor += max( dot( sunDirection, surfaceNormal ), 0.0 ) * sunColor * diffuse;
|
||||
}
|
||||
|
||||
#include <common>
|
||||
#include <packing>
|
||||
#include <bsdfs>
|
||||
#include <fog_pars_fragment>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
#include <lights_pars_begin>
|
||||
#include <shadowmap_pars_fragment>
|
||||
#include <shadowmask_pars_fragment>
|
||||
|
||||
void main() {
|
||||
|
||||
#include <logdepthbuf_fragment>
|
||||
vec4 noise = getNoise( worldPosition.xz * size );
|
||||
vec3 surfaceNormal = normalize( noise.xzy * vec3( 1.5, 1.0, 1.5 ) );
|
||||
|
||||
vec3 diffuseLight = vec3(0.0);
|
||||
vec3 specularLight = vec3(0.0);
|
||||
|
||||
vec3 worldToEye = eye-worldPosition.xyz;
|
||||
vec3 eyeDirection = normalize( worldToEye );
|
||||
sunLight( surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight );
|
||||
|
||||
float distance = length(worldToEye);
|
||||
|
||||
vec2 distortion = surfaceNormal.xz * ( 0.001 + 1.0 / distance ) * distortionScale;
|
||||
vec3 reflectionSample = vec3( texture2D( mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion ) );
|
||||
|
||||
float theta = max( dot( eyeDirection, surfaceNormal ), 0.0 );
|
||||
float rf0 = 0.3;
|
||||
float reflectance = rf0 + ( 1.0 - rf0 ) * pow( ( 1.0 - theta ), 5.0 );
|
||||
vec3 scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ) * waterColor;
|
||||
vec3 albedo = mix( ( sunColor * diffuseLight * 0.3 + scatter ) * getShadowMask(), ( vec3( 0.1 ) + reflectionSample * 0.9 + reflectionSample * specularLight ), reflectance);
|
||||
vec3 outgoingLight = albedo;
|
||||
gl_FragColor = vec4( outgoingLight, alpha );
|
||||
|
||||
#include <tonemapping_fragment>
|
||||
#include <colorspace_fragment>
|
||||
#include <fog_fragment>
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
const material = new ShaderMaterial( {
|
||||
name: mirrorShader.name,
|
||||
uniforms: UniformsUtils.clone( mirrorShader.uniforms ),
|
||||
vertexShader: mirrorShader.vertexShader,
|
||||
fragmentShader: mirrorShader.fragmentShader,
|
||||
lights: true,
|
||||
side: side,
|
||||
fog: fog
|
||||
} );
|
||||
|
||||
material.uniforms[ 'mirrorSampler' ].value = renderTarget.texture;
|
||||
material.uniforms[ 'textureMatrix' ].value = textureMatrix;
|
||||
material.uniforms[ 'alpha' ].value = alpha;
|
||||
material.uniforms[ 'time' ].value = time;
|
||||
material.uniforms[ 'normalSampler' ].value = normalSampler;
|
||||
material.uniforms[ 'sunColor' ].value = sunColor;
|
||||
material.uniforms[ 'waterColor' ].value = waterColor;
|
||||
material.uniforms[ 'sunDirection' ].value = sunDirection;
|
||||
material.uniforms[ 'distortionScale' ].value = distortionScale;
|
||||
|
||||
material.uniforms[ 'eye' ].value = eye;
|
||||
|
||||
scope.material = material;
|
||||
|
||||
scope.onBeforeRender = function ( renderer, scene, camera ) {
|
||||
|
||||
mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
|
||||
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
|
||||
|
||||
rotationMatrix.extractRotation( scope.matrixWorld );
|
||||
|
||||
normal.set( 0, 0, 1 );
|
||||
normal.applyMatrix4( rotationMatrix );
|
||||
|
||||
view.subVectors( mirrorWorldPosition, cameraWorldPosition );
|
||||
|
||||
// Avoid rendering when mirror is facing away
|
||||
|
||||
if ( view.dot( normal ) > 0 ) return;
|
||||
|
||||
view.reflect( normal ).negate();
|
||||
view.add( mirrorWorldPosition );
|
||||
|
||||
rotationMatrix.extractRotation( camera.matrixWorld );
|
||||
|
||||
lookAtPosition.set( 0, 0, - 1 );
|
||||
lookAtPosition.applyMatrix4( rotationMatrix );
|
||||
lookAtPosition.add( cameraWorldPosition );
|
||||
|
||||
target.subVectors( mirrorWorldPosition, lookAtPosition );
|
||||
target.reflect( normal ).negate();
|
||||
target.add( mirrorWorldPosition );
|
||||
|
||||
mirrorCamera.position.copy( view );
|
||||
mirrorCamera.up.set( 0, 1, 0 );
|
||||
mirrorCamera.up.applyMatrix4( rotationMatrix );
|
||||
mirrorCamera.up.reflect( normal );
|
||||
mirrorCamera.lookAt( target );
|
||||
|
||||
mirrorCamera.far = camera.far; // Used in WebGLBackground
|
||||
|
||||
mirrorCamera.updateMatrixWorld();
|
||||
mirrorCamera.projectionMatrix.copy( camera.projectionMatrix );
|
||||
|
||||
// Update the texture matrix
|
||||
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
|
||||
);
|
||||
textureMatrix.multiply( mirrorCamera.projectionMatrix );
|
||||
textureMatrix.multiply( mirrorCamera.matrixWorldInverse );
|
||||
|
||||
// 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
|
||||
mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition );
|
||||
mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse );
|
||||
|
||||
clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant );
|
||||
|
||||
const projectionMatrix = mirrorCamera.projectionMatrix;
|
||||
|
||||
q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
|
||||
q.y = ( Math.sign( clipPlane.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
|
||||
clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
|
||||
|
||||
// Replacing the third row of the projection matrix
|
||||
projectionMatrix.elements[ 2 ] = clipPlane.x;
|
||||
projectionMatrix.elements[ 6 ] = clipPlane.y;
|
||||
projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
|
||||
projectionMatrix.elements[ 14 ] = clipPlane.w;
|
||||
|
||||
eye.setFromMatrixPosition( camera.matrixWorld );
|
||||
|
||||
// Render
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
|
||||
const currentXrEnabled = renderer.xr.enabled;
|
||||
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
||||
|
||||
scope.visible = false;
|
||||
|
||||
renderer.xr.enabled = false; // Avoid camera modification and recursion
|
||||
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
|
||||
|
||||
if ( renderer.autoClear === false ) renderer.clear();
|
||||
renderer.render( scene, mirrorCamera );
|
||||
|
||||
scope.visible = true;
|
||||
|
||||
renderer.xr.enabled = currentXrEnabled;
|
||||
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
||||
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
// Restore viewport
|
||||
|
||||
const viewport = camera.viewport;
|
||||
|
||||
if ( viewport !== undefined ) {
|
||||
|
||||
renderer.state.viewport( viewport );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Water };
|
361
static/sdk/three/jsm/objects/Water2.js
Normal file
361
static/sdk/three/jsm/objects/Water2.js
Normal file
@ -0,0 +1,361 @@
|
||||
import {
|
||||
Clock,
|
||||
Color,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
RepeatWrapping,
|
||||
ShaderMaterial,
|
||||
TextureLoader,
|
||||
UniformsLib,
|
||||
UniformsUtils,
|
||||
Vector2,
|
||||
Vector4
|
||||
} from 'three';
|
||||
import { Reflector } from '../objects/Reflector.js';
|
||||
import { Refractor } from '../objects/Refractor.js';
|
||||
|
||||
/**
|
||||
* References:
|
||||
* https://alex.vlachos.com/graphics/Vlachos-SIGGRAPH10-WaterFlow.pdf
|
||||
* http://graphicsrunner.blogspot.de/2010/08/water-using-flow-maps.html
|
||||
*
|
||||
*/
|
||||
|
||||
class Water extends Mesh {
|
||||
|
||||
constructor( geometry, options = {} ) {
|
||||
|
||||
super( geometry );
|
||||
|
||||
this.isWater = true;
|
||||
|
||||
this.type = 'Water';
|
||||
|
||||
const scope = this;
|
||||
|
||||
const color = ( options.color !== undefined ) ? new Color( options.color ) : new Color( 0xFFFFFF );
|
||||
const textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
|
||||
const textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
|
||||
const clipBias = options.clipBias !== undefined ? options.clipBias : 0;
|
||||
const flowDirection = options.flowDirection !== undefined ? options.flowDirection : new Vector2( 1, 0 );
|
||||
const flowSpeed = options.flowSpeed !== undefined ? options.flowSpeed : 0.03;
|
||||
const reflectivity = options.reflectivity !== undefined ? options.reflectivity : 0.02;
|
||||
const scale = options.scale !== undefined ? options.scale : 1;
|
||||
const shader = options.shader !== undefined ? options.shader : Water.WaterShader;
|
||||
|
||||
const textureLoader = new TextureLoader();
|
||||
|
||||
const flowMap = options.flowMap || undefined;
|
||||
const normalMap0 = options.normalMap0 || textureLoader.load( 'textures/water/Water_1_M_Normal.jpg' );
|
||||
const normalMap1 = options.normalMap1 || textureLoader.load( 'textures/water/Water_2_M_Normal.jpg' );
|
||||
|
||||
const cycle = 0.15; // a cycle of a flow map phase
|
||||
const halfCycle = cycle * 0.5;
|
||||
const textureMatrix = new Matrix4();
|
||||
const clock = new Clock();
|
||||
|
||||
// internal components
|
||||
|
||||
if ( Reflector === undefined ) {
|
||||
|
||||
console.error( 'THREE.Water: Required component Reflector not found.' );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if ( Refractor === undefined ) {
|
||||
|
||||
console.error( 'THREE.Water: Required component Refractor not found.' );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const reflector = new Reflector( geometry, {
|
||||
textureWidth: textureWidth,
|
||||
textureHeight: textureHeight,
|
||||
clipBias: clipBias
|
||||
} );
|
||||
|
||||
const refractor = new Refractor( geometry, {
|
||||
textureWidth: textureWidth,
|
||||
textureHeight: textureHeight,
|
||||
clipBias: clipBias
|
||||
} );
|
||||
|
||||
reflector.matrixAutoUpdate = false;
|
||||
refractor.matrixAutoUpdate = false;
|
||||
|
||||
// material
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
name: shader.name,
|
||||
uniforms: UniformsUtils.merge( [
|
||||
UniformsLib[ 'fog' ],
|
||||
shader.uniforms
|
||||
] ),
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader,
|
||||
transparent: true,
|
||||
fog: true
|
||||
} );
|
||||
|
||||
if ( flowMap !== undefined ) {
|
||||
|
||||
this.material.defines.USE_FLOWMAP = '';
|
||||
this.material.uniforms[ 'tFlowMap' ] = {
|
||||
type: 't',
|
||||
value: flowMap
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
this.material.uniforms[ 'flowDirection' ] = {
|
||||
type: 'v2',
|
||||
value: flowDirection
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// maps
|
||||
|
||||
normalMap0.wrapS = normalMap0.wrapT = RepeatWrapping;
|
||||
normalMap1.wrapS = normalMap1.wrapT = RepeatWrapping;
|
||||
|
||||
this.material.uniforms[ 'tReflectionMap' ].value = reflector.getRenderTarget().texture;
|
||||
this.material.uniforms[ 'tRefractionMap' ].value = refractor.getRenderTarget().texture;
|
||||
this.material.uniforms[ 'tNormalMap0' ].value = normalMap0;
|
||||
this.material.uniforms[ 'tNormalMap1' ].value = normalMap1;
|
||||
|
||||
// water
|
||||
|
||||
this.material.uniforms[ 'color' ].value = color;
|
||||
this.material.uniforms[ 'reflectivity' ].value = reflectivity;
|
||||
this.material.uniforms[ 'textureMatrix' ].value = textureMatrix;
|
||||
|
||||
// inital values
|
||||
|
||||
this.material.uniforms[ 'config' ].value.x = 0; // flowMapOffset0
|
||||
this.material.uniforms[ 'config' ].value.y = halfCycle; // flowMapOffset1
|
||||
this.material.uniforms[ 'config' ].value.z = halfCycle; // halfCycle
|
||||
this.material.uniforms[ 'config' ].value.w = scale; // scale
|
||||
|
||||
// functions
|
||||
|
||||
function updateTextureMatrix( camera ) {
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
textureMatrix.multiply( camera.projectionMatrix );
|
||||
textureMatrix.multiply( camera.matrixWorldInverse );
|
||||
textureMatrix.multiply( scope.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
function updateFlow() {
|
||||
|
||||
const delta = clock.getDelta();
|
||||
const config = scope.material.uniforms[ 'config' ];
|
||||
|
||||
config.value.x += flowSpeed * delta; // flowMapOffset0
|
||||
config.value.y = config.value.x + halfCycle; // flowMapOffset1
|
||||
|
||||
// Important: The distance between offsets should be always the value of "halfCycle".
|
||||
// Moreover, both offsets should be in the range of [ 0, cycle ].
|
||||
// This approach ensures a smooth water flow and avoids "reset" effects.
|
||||
|
||||
if ( config.value.x >= cycle ) {
|
||||
|
||||
config.value.x = 0;
|
||||
config.value.y = halfCycle;
|
||||
|
||||
} else if ( config.value.y >= cycle ) {
|
||||
|
||||
config.value.y = config.value.y - cycle;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
this.onBeforeRender = function ( renderer, scene, camera ) {
|
||||
|
||||
updateTextureMatrix( camera );
|
||||
updateFlow();
|
||||
|
||||
scope.visible = false;
|
||||
|
||||
reflector.matrixWorld.copy( scope.matrixWorld );
|
||||
refractor.matrixWorld.copy( scope.matrixWorld );
|
||||
|
||||
reflector.onBeforeRender( renderer, scene, camera );
|
||||
refractor.onBeforeRender( renderer, scene, camera );
|
||||
|
||||
scope.visible = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Water.WaterShader = {
|
||||
|
||||
name: 'WaterShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'color': {
|
||||
type: 'c',
|
||||
value: null
|
||||
},
|
||||
|
||||
'reflectivity': {
|
||||
type: 'f',
|
||||
value: 0
|
||||
},
|
||||
|
||||
'tReflectionMap': {
|
||||
type: 't',
|
||||
value: null
|
||||
},
|
||||
|
||||
'tRefractionMap': {
|
||||
type: 't',
|
||||
value: null
|
||||
},
|
||||
|
||||
'tNormalMap0': {
|
||||
type: 't',
|
||||
value: null
|
||||
},
|
||||
|
||||
'tNormalMap1': {
|
||||
type: 't',
|
||||
value: null
|
||||
},
|
||||
|
||||
'textureMatrix': {
|
||||
type: 'm4',
|
||||
value: null
|
||||
},
|
||||
|
||||
'config': {
|
||||
type: 'v4',
|
||||
value: new Vector4()
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
#include <common>
|
||||
#include <fog_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
|
||||
uniform mat4 textureMatrix;
|
||||
|
||||
varying vec4 vCoord;
|
||||
varying vec2 vUv;
|
||||
varying vec3 vToEye;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
vCoord = textureMatrix * vec4( position, 1.0 );
|
||||
|
||||
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
|
||||
vToEye = cameraPosition - worldPosition.xyz;
|
||||
|
||||
vec4 mvPosition = viewMatrix * worldPosition; // used in fog_vertex
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
|
||||
#include <logdepthbuf_vertex>
|
||||
#include <fog_vertex>
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
#include <common>
|
||||
#include <fog_pars_fragment>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
|
||||
uniform sampler2D tReflectionMap;
|
||||
uniform sampler2D tRefractionMap;
|
||||
uniform sampler2D tNormalMap0;
|
||||
uniform sampler2D tNormalMap1;
|
||||
|
||||
#ifdef USE_FLOWMAP
|
||||
uniform sampler2D tFlowMap;
|
||||
#else
|
||||
uniform vec2 flowDirection;
|
||||
#endif
|
||||
|
||||
uniform vec3 color;
|
||||
uniform float reflectivity;
|
||||
uniform vec4 config;
|
||||
|
||||
varying vec4 vCoord;
|
||||
varying vec2 vUv;
|
||||
varying vec3 vToEye;
|
||||
|
||||
void main() {
|
||||
|
||||
#include <logdepthbuf_fragment>
|
||||
|
||||
float flowMapOffset0 = config.x;
|
||||
float flowMapOffset1 = config.y;
|
||||
float halfCycle = config.z;
|
||||
float scale = config.w;
|
||||
|
||||
vec3 toEye = normalize( vToEye );
|
||||
|
||||
// determine flow direction
|
||||
vec2 flow;
|
||||
#ifdef USE_FLOWMAP
|
||||
flow = texture2D( tFlowMap, vUv ).rg * 2.0 - 1.0;
|
||||
#else
|
||||
flow = flowDirection;
|
||||
#endif
|
||||
flow.x *= - 1.0;
|
||||
|
||||
// sample normal maps (distort uvs with flowdata)
|
||||
vec4 normalColor0 = texture2D( tNormalMap0, ( vUv * scale ) + flow * flowMapOffset0 );
|
||||
vec4 normalColor1 = texture2D( tNormalMap1, ( vUv * scale ) + flow * flowMapOffset1 );
|
||||
|
||||
// linear interpolate to get the final normal color
|
||||
float flowLerp = abs( halfCycle - flowMapOffset0 ) / halfCycle;
|
||||
vec4 normalColor = mix( normalColor0, normalColor1, flowLerp );
|
||||
|
||||
// calculate normal vector
|
||||
vec3 normal = normalize( vec3( normalColor.r * 2.0 - 1.0, normalColor.b, normalColor.g * 2.0 - 1.0 ) );
|
||||
|
||||
// calculate the fresnel term to blend reflection and refraction maps
|
||||
float theta = max( dot( toEye, normal ), 0.0 );
|
||||
float reflectance = reflectivity + ( 1.0 - reflectivity ) * pow( ( 1.0 - theta ), 5.0 );
|
||||
|
||||
// calculate final uv coords
|
||||
vec3 coord = vCoord.xyz / vCoord.w;
|
||||
vec2 uv = coord.xy + coord.z * normal.xz * 0.05;
|
||||
|
||||
vec4 reflectColor = texture2D( tReflectionMap, vec2( 1.0 - uv.x, uv.y ) );
|
||||
vec4 refractColor = texture2D( tRefractionMap, uv );
|
||||
|
||||
// multiply water color with the mix of both textures
|
||||
gl_FragColor = vec4( color, 1.0 ) * mix( refractColor, reflectColor, reflectance );
|
||||
|
||||
#include <tonemapping_fragment>
|
||||
#include <colorspace_fragment>
|
||||
#include <fog_fragment>
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { Water };
|
Reference in New Issue
Block a user