镜面反射插件
import {
Color,
LinearFilter,
MathUtils,
Matrix4,
Mesh,
PerspectiveCamera,
Plane,
RGBFormat,
ShaderMaterial,
RepeatWrapping,
UniformsUtils,
Vector3,
Vector4,
WebGLRenderTarget
} from 'three';
class Reflector extends Mesh {
constructor(geometry, options = {}) {
super(geometry);
this.type = 'Reflector';
const self = this;
const color = options.color !== undefined ? new Color(options.color) : new Color(0x7F7F7F);
const textureWidth = options.textureWidth || 512;
const textureHeight = options.textureHeight || 512;
const strength = options.strength;
const Ttexture = options.Ttexture;
const clipBias = options.clipBias || 0;
const shader = options.shader || Reflector.ReflectorShader;
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 = new PerspectiveCamera();
const parameters = {
minFilter: LinearFilter,
magFilter: LinearFilter,
format: RGBFormat
};
const renderTarget = new WebGLRenderTarget(textureWidth, textureHeight, parameters);
if (!MathUtils.isPowerOfTwo(textureWidth) || !MathUtils.isPowerOfTwo(textureHeight)) {
renderTarget.texture.generateMipmaps = false;
}
const material = new ShaderMaterial({
uniforms: UniformsUtils.clone(shader.uniforms),
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader
});
Ttexture.wrapS = RepeatWrapping;
Ttexture.wrapT = RepeatWrapping;
material.uniforms['tDiffuse'].value = renderTarget.texture;
material.uniforms['color'].value = color;
material.uniforms['textureMatrix'].value = textureMatrix;
material.uniforms['strength'].value = strength;
material.uniforms['Ttexture'].value = Ttexture;
this.material = material;
this.onBeforeRender = function (renderer, scene, camera) {
reflectorWorldPosition.setFromMatrixPosition(self.matrixWorld);
cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld);
rotationMatrix.extractRotation(self.matrixWorld);
normal.set(0, 0, 1);
normal.applyMatrix4(rotationMatrix);
view.subVectors(reflectorWorldPosition, cameraWorldPosition);
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;
virtualCamera.updateMatrixWorld();
virtualCamera.projectionMatrix.copy(camera.projectionMatrix);
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(self.matrixWorld);
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];
clipPlane.multiplyScalar(2.0 / clipPlane.dot(q));
projectionMatrix.elements[2] = clipPlane.x;
projectionMatrix.elements[6] = clipPlane.y;
projectionMatrix.elements[10] = clipPlane.z + 1.0 - clipBias;
projectionMatrix.elements[14] = clipPlane.w;
renderTarget.texture.encoding = renderer.outputEncoding;
self.visible = false;
const currentRenderTarget = renderer.getRenderTarget();
const currentXrEnabled = renderer.xr.enabled;
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.xr.enabled = false;
renderer.shadowMap.autoUpdate = false;
renderer.setRenderTarget(renderTarget);
renderer.state.buffers.depth.setMask(true);
if (renderer.autoClear === false) renderer.clear();
renderer.render(scene, virtualCamera);
renderer.xr.enabled = currentXrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget(currentRenderTarget);
const viewport = camera.viewport;
if (viewport !== undefined) {
renderer.state.viewport(viewport);
}
self.visible = true;
};
this.getRenderTarget = function () {
return renderTarget;
};
this.dispose = function () {
renderTarget.dispose();
self.material.dispose();
};
}
}
Reflector.prototype.isReflector = true;
Reflector.ReflectorShader = {
uniforms: {
color: {
value: null
},
tDiffuse: {
value: null
},
textureMatrix: {
value: null
},
strength: {
value: null
},
Ttexture: {
value: null
}
},
vertexShader: `
uniform mat4 textureMatrix;
varying vec4 vUv;
varying vec2 uv2;
#include <common>
#include <logdepthbuf_pars_vertex>
void main() {
uv2 = uv;
vUv = textureMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
#include <logdepthbuf_vertex>
}`,
fragmentShader: `
uniform vec3 color;
uniform sampler2D tDiffuse;
uniform float strength;
varying vec4 vUv;
varying vec2 uv2;
uniform sampler2D Ttexture;
#include <logdepthbuf_pars_fragment>
float blendOverlay( float base, float blend ) {
return( base < 1.0 ? ( 0.3 * 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 ));
}
/// ==============
// 模糊半径
// for 循环的次数必须为常量
const float RADIUS = 20.0;
// 获取模糊颜色
// vec4 getBlurColor (vec2 pos) {
// vec4 color = vec4(0); // 初始颜色
// float sum = 0.0; // 总权重
// // 卷积过程
// for (float r = -RADIUS; r <= RADIUS; r++) { // 水平方向
// for (float c = -RADIUS; c <= RADIUS; c++) { // 垂直方向
// vec2 target = pos + vec2(r / size.x, c / size.y); // 目标像素位置
// float weight = (RADIUS - abs(r)) * (RADIUS - abs(c)); // 计算权重
// color += texture2D(Ttexture, target) * weight; // 累加颜色
// sum += weight; // 累加权重
// }
// }
// color /= sum; // 求出平均值
// return color;
// }
//========
void main() {
#include <logdepthbuf_fragment>
vec4 textureColor = texture2D( Ttexture, uv2);
vec4 base = texture2DProj( tDiffuse, vUv );
vec4 base2 = vec4( blendOverlay( base.rgb*strength, color ), .1 );
// ====
// vec4 color = getBlurColor(uv2); // 获取模糊后的颜色
//===
gl_FragColor = mix(base,textureColor, 0.01);
#include <encodings_fragment>
}`
};
export {Reflector};
使用方式
const planeLine = textureLoader.load(require('../../assets/img/opacity.png'), texture => {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.needsUpdate = true;
texture.repeat.set(1, 1);
texture.anisotropy = 1;
texture.encoding = THREE.sRGBEncoding;
});
loadGroundMirror() {
const geometry = new THREE.PlaneGeometry(50000, 50000);
const verticalMirror = new Reflector(geometry, {
clipBias: 0.1,
textureWidth: window.innerWidth * window.devicePixelRatio,
textureHeight: window.innerHeight * window.devicePixelRatio,
color: '#fff',
strength: 0.001,
Ttexture: planeLine
});
verticalMirror.position.y = -10;
verticalMirror.position.z = 0;
verticalMirror.position.x = 0;
verticalMirror.rotateX(-Math.PI / 2);
scene.add(verticalMirror);
}