threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
203 lines • 8.82 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { getOrCall, onChange, serialize } from 'ts-browser-helpers';
import { BasicDepthPacking, Color, Euler, LinearFilter, MeshDepthMaterial, NoBlending, NoColorSpace, OrthographicCamera, RGBAFormat, UnsignedByteType, Vector3, } from 'three';
import { BaseGroundPlugin } from '../base/BaseGroundPlugin';
import { GBufferRenderPass } from '../../postprocessing';
import { uiPanelContainer, uiSlider, uiToggle } from 'uiconfig.js';
import { HVBlurHelper } from '../../three/utils/HVBlurHelper';
import { shaderReplaceString } from '../../utils';
let ContactShadowGroundPlugin = class ContactShadowGroundPlugin extends BaseGroundPlugin {
constructor() {
super();
this.contactShadows = true;
this.shadowScale = 1;
this.shadowHeight = 5;
this.blurAmount = 1;
this.shadowCamera = new OrthographicCamera(-1, 1, 1, -1, 0.001, this.shadowHeight);
this._depthTex = null;
this._refreshShadowCameraFrustum = this._refreshShadowCameraFrustum.bind(this);
this.refresh = this.refresh.bind(this);
}
onAdded(viewer) {
const target = viewer.renderManager.createTarget({
type: UnsignedByteType,
format: RGBAFormat,
colorSpace: NoColorSpace,
size: { width: 512, height: 512 },
generateMipmaps: false,
depthBuffer: true,
minFilter: LinearFilter,
magFilter: LinearFilter,
// isAntialiased: this._viewer.isAntialiased,
});
target.texture.name = 'groundContactDepthTexture';
// https://github.com/mrdoob/three.js/blob/master/examples/webgl_shadow_contact.html
const material = new MeshDepthMaterial({
// depthPacking: RGBADepthPacking, // todo
depthPacking: BasicDepthPacking,
transparent: false,
blending: NoBlending,
});
material.onBeforeCompile = (shader) => {
shader.uniforms.opacity.value = 1.;
shader.fragmentShader = shaderReplaceString(shader.fragmentShader, 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );', 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), 1.0 );');
};
this._depthPass = new GBufferRenderPass('contactShadowGround', target, material, new Color(0, 0, 0), 0);
this._blurHelper = new HVBlurHelper(viewer);
super.onAdded(viewer);
}
onRemove(viewer) {
const target = getOrCall(this._depthPass?.target);
if (target)
this._viewer?.renderManager.disposeTarget(target);
this._depthPass?.dispose();
this._depthPass = undefined;
this._blurHelper?.dispose();
this._blurHelper = undefined;
return super.onRemove(viewer);
}
// todo: dispose target, material, pass and stuff
_postFrame() {
super._postFrame();
if (!this._viewer)
return;
}
_preRender() {
super._preRender();
if (!this._viewer || !this._depthPass || !this._blurHelper || !this.contactShadows || !this.visible)
return;
this._depthPass.scene = this._viewer.scene;
this._depthPass.camera = this.shadowCamera;
this._depthPass.render(this._viewer.renderManager.renderer, null);
const target = getOrCall(this._depthPass.target);
if (!target)
return;
const blurTarget = this._viewer.renderManager.getTempTarget({
type: UnsignedByteType,
format: RGBAFormat,
colorSpace: NoColorSpace,
size: { width: 1024, height: 1024 },
generateMipmaps: false,
depthBuffer: false,
minFilter: LinearFilter,
magFilter: LinearFilter,
// isAntialiased: this._viewer.isAntialiased,
});
this._blurHelper.blur(target.texture, target, blurTarget, this.blurAmount / 256);
this._blurHelper.blur(target.texture, target, blurTarget, 0.4 * this.blurAmount / 256);
this._viewer.renderManager.releaseTempTarget(blurTarget);
}
_refreshTransform() {
if (!super._refreshTransform())
return false;
if (!this._mesh)
return false;
if (!this._viewer)
return false;
this.shadowCamera.position.copy(this._mesh.getWorldPosition(new Vector3()));
this.shadowCamera.setRotationFromEuler(new Euler(Math.PI / 2., 0, 0));
this.shadowCamera.updateMatrixWorld();
this._refreshShadowCameraFrustum();
this._mesh.scale.y = -this.size;
return true;
}
_refreshShadowCameraFrustum() {
if (!this.shadowCamera)
return;
this.shadowCamera.left = -this.size / (2 * this.shadowScale);
this.shadowCamera.right = this.size / (2 * this.shadowScale);
this.shadowCamera.top = this.size / (2 * this.shadowScale);
this.shadowCamera.bottom = -this.size / (2 * this.shadowScale);
this.shadowCamera.far = this.shadowHeight;
this.shadowCamera.updateProjectionMatrix();
this._setDirty();
}
_setDirty() {
this._viewer?.setDirty();
}
_removeMaterial() {
if (!this._material)
return;
this._material.alphaMap = null;
if (this._material.userData.ssreflDisabled)
delete this._material.userData.ssreflDisabled;
if (this._material.userData.ssreflNonPhysical)
delete this._material.userData.ssreflNonPhysical;
super._removeMaterial();
}
refresh() {
if (!this._viewer)
return;
if (!this.contactShadows) {
if (this._material?.alphaMap === this._depthTex) {
this._material.alphaMap = null;
this._material.setDirty();
}
if (this._material?.userData.__csgpParamsSet) {
delete this._material.userData.__csgpParamsSet;
delete this._material.userData.ssreflDisabled;
delete this._material.userData.ssreflNonPhysical;
}
this._depthTex = null;
}
else {
this._depthTex = getOrCall(this._depthPass?.target)?.texture || null;
}
super.refresh();
}
_createMaterial(material) {
const mat = super._createMaterial(material);
mat.roughness = 1;
mat.metalness = 0;
mat.color.set(0x111111);
mat.transparent = true;
// mat.userData.inverseAlphaMap = false // this must be false, if getting inverted colors, check clear color of gbuffer render pass.
return mat;
}
_refreshMaterial() {
if (!this._viewer)
return false;
const isNewMaterial = super._refreshMaterial();
if (!this._material)
return isNewMaterial;
if (this.contactShadows) {
this._material.userData.ssreflDisabled = true;
this._material.userData.ssreflNonPhysical = false;
this._material.userData.__csgpParamsSet = true;
this._material.alphaMap = this._depthTex || null;
this._material.setDirty();
}
return isNewMaterial;
}
};
ContactShadowGroundPlugin.PluginType = 'ContactShadowGroundPlugin';
__decorate([
uiToggle('Contact Shadows'),
onChange(ContactShadowGroundPlugin.prototype.refresh),
serialize()
], ContactShadowGroundPlugin.prototype, "contactShadows", void 0);
__decorate([
uiSlider('Shadow Scale', [0, 2]),
serialize(),
onChange(ContactShadowGroundPlugin.prototype._refreshShadowCameraFrustum)
], ContactShadowGroundPlugin.prototype, "shadowScale", void 0);
__decorate([
uiSlider('Shadow Height', [0, 20]),
serialize(),
onChange(ContactShadowGroundPlugin.prototype._refreshShadowCameraFrustum)
], ContactShadowGroundPlugin.prototype, "shadowHeight", void 0);
__decorate([
uiSlider('Blur Amount', [0, 10]),
serialize(),
onChange(ContactShadowGroundPlugin.prototype._setDirty)
], ContactShadowGroundPlugin.prototype, "blurAmount", void 0);
ContactShadowGroundPlugin = __decorate([
uiPanelContainer('Contact Shadow Ground')
], ContactShadowGroundPlugin);
export { ContactShadowGroundPlugin };
//# sourceMappingURL=ContactShadowGroundPlugin.js.map