@react-three/drei
Version:
useful add-ons for react-three-fiber
115 lines (112 loc) • 4.62 kB
JavaScript
import _extends from '@babel/runtime/helpers/esm/extends';
import * as React from 'react';
import * as THREE from 'three';
import { useThree, useFrame } from '@react-three/fiber';
import { HorizontalBlurShader, VerticalBlurShader } from 'three-stdlib';
const ContactShadows = /* @__PURE__ */React.forwardRef(({
scale = 10,
frames = Infinity,
opacity = 1,
width = 1,
height = 1,
blur = 1,
near = 0,
far = 10,
resolution = 512,
smooth = true,
color = '#000000',
depthWrite = false,
renderOrder,
...props
}, fref) => {
const ref = React.useRef(null);
const scene = useThree(state => state.scene);
const gl = useThree(state => state.gl);
const shadowCamera = React.useRef(null);
width = width * (Array.isArray(scale) ? scale[0] : scale || 1);
height = height * (Array.isArray(scale) ? scale[1] : scale || 1);
const [renderTarget, planeGeometry, depthMaterial, blurPlane, horizontalBlurMaterial, verticalBlurMaterial, renderTargetBlur] = React.useMemo(() => {
const renderTarget = new THREE.WebGLRenderTarget(resolution, resolution);
const renderTargetBlur = new THREE.WebGLRenderTarget(resolution, resolution);
renderTargetBlur.texture.generateMipmaps = renderTarget.texture.generateMipmaps = false;
const planeGeometry = new THREE.PlaneGeometry(width, height).rotateX(Math.PI / 2);
const blurPlane = new THREE.Mesh(planeGeometry);
const depthMaterial = new THREE.MeshDepthMaterial();
depthMaterial.depthTest = depthMaterial.depthWrite = false;
depthMaterial.onBeforeCompile = shader => {
shader.uniforms = {
...shader.uniforms,
ucolor: {
value: new THREE.Color(color)
}
};
shader.fragmentShader = shader.fragmentShader.replace(`void main() {`,
//
`uniform vec3 ucolor;
void main() {
`);
shader.fragmentShader = shader.fragmentShader.replace('vec4( vec3( 1.0 - fragCoordZ ), opacity );',
// Colorize the shadow, multiply by the falloff so that the center can remain darker
'vec4( ucolor * fragCoordZ * 2.0, ( 1.0 - fragCoordZ ) * 1.0 );');
};
const horizontalBlurMaterial = new THREE.ShaderMaterial(HorizontalBlurShader);
const verticalBlurMaterial = new THREE.ShaderMaterial(VerticalBlurShader);
verticalBlurMaterial.depthTest = horizontalBlurMaterial.depthTest = false;
return [renderTarget, planeGeometry, depthMaterial, blurPlane, horizontalBlurMaterial, verticalBlurMaterial, renderTargetBlur];
}, [resolution, width, height, scale, color]);
const blurShadows = blur => {
blurPlane.visible = true;
blurPlane.material = horizontalBlurMaterial;
horizontalBlurMaterial.uniforms.tDiffuse.value = renderTarget.texture;
horizontalBlurMaterial.uniforms.h.value = blur * 1 / 256;
gl.setRenderTarget(renderTargetBlur);
gl.render(blurPlane, shadowCamera.current);
blurPlane.material = verticalBlurMaterial;
verticalBlurMaterial.uniforms.tDiffuse.value = renderTargetBlur.texture;
verticalBlurMaterial.uniforms.v.value = blur * 1 / 256;
gl.setRenderTarget(renderTarget);
gl.render(blurPlane, shadowCamera.current);
blurPlane.visible = false;
};
let count = 0;
let initialBackground;
let initialOverrideMaterial;
useFrame(() => {
if (shadowCamera.current && (frames === Infinity || count < frames)) {
count++;
initialBackground = scene.background;
initialOverrideMaterial = scene.overrideMaterial;
ref.current.visible = false;
scene.background = null;
scene.overrideMaterial = depthMaterial;
gl.setRenderTarget(renderTarget);
gl.render(scene, shadowCamera.current);
blurShadows(blur);
if (smooth) blurShadows(blur * 0.4);
gl.setRenderTarget(null);
ref.current.visible = true;
scene.overrideMaterial = initialOverrideMaterial;
scene.background = initialBackground;
}
});
React.useImperativeHandle(fref, () => ref.current, []);
return /*#__PURE__*/React.createElement("group", _extends({
"rotation-x": Math.PI / 2
}, props, {
ref: ref
}), /*#__PURE__*/React.createElement("mesh", {
renderOrder: renderOrder,
geometry: planeGeometry,
scale: [1, -1, 1],
rotation: [-Math.PI / 2, 0, 0]
}, /*#__PURE__*/React.createElement("meshBasicMaterial", {
transparent: true,
map: renderTarget.texture,
opacity: opacity,
depthWrite: depthWrite
})), /*#__PURE__*/React.createElement("orthographicCamera", {
ref: shadowCamera,
args: [-width / 2, width / 2, height / 2, -height / 2, near, far]
}));
});
export { ContactShadows };