UNPKG

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.

319 lines 15.7 kB
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 { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'; import { Color, HalfFloatType, LinearFilter, NoColorSpace, RGBAFormat, UnsignedByteType, } from 'three'; import { generateUiConfig, uiToggle } from 'uiconfig.js'; import { serialize } from 'ts-browser-helpers'; import { GenericBlendTexturePass } from './GenericBlendTexturePass'; import { ViewerRenderManager } from '../viewer'; export class ExtendedRenderPass extends RenderPass { /** * A render target to render transparent, transmissive objects to. * Note that it is only used when {@link renderManager.rgbm} is true */ get transparentTarget() { if (!this._transparentTarget) { const msaa = this.renderManager.msaa; this._transparentTarget = this.renderManager.getTempTarget({ sizeMultiplier: 1, samples: msaa ? typeof msaa !== 'number' ? ViewerRenderManager.DEFAULT_MSAA_SAMPLES : msaa : 0, stencilBuffer: this.renderManager.composerTarget.stencilBuffer, colorSpace: NoColorSpace, type: this.renderManager.renderer.extensions.has('EXT_color_buffer_half_float') ? HalfFloatType : UnsignedByteType, format: RGBAFormat, minFilter: LinearFilter, magFilter: LinearFilter, depthBuffer: false, }); } return this._transparentTarget; } _releaseTransparentTarget() { if (this._transparentTarget) this.renderManager.releaseTempTarget(this._transparentTarget); this._transparentTarget = undefined; } /** * A render target to render opaque objects to. * Note that it is only used when {@link renderManager.msaa} is true */ get opaqueTarget() { if (!this._opaqueTarget) { const composerTarget = this.renderManager.composerTarget; const msaa = this.renderManager.msaa; this._opaqueTarget = this.renderManager.getTempTarget({ sizeMultiplier: 1, samples: msaa ? typeof msaa !== 'number' ? ViewerRenderManager.DEFAULT_MSAA_SAMPLES : msaa : 0, stencilBuffer: this.renderManager.composerTarget.stencilBuffer, colorSpace: composerTarget.texture.colorSpace, type: this.renderManager.rgbm ? UnsignedByteType : HalfFloatType, format: composerTarget.texture.format, minFilter: composerTarget.texture.minFilter, magFilter: composerTarget.texture.magFilter, depthBuffer: composerTarget.depthBuffer, generateMipmaps: composerTarget.texture.generateMipmaps, }); // console.log(this._opaqueTarget.samples) } return this._opaqueTarget; } _releaseOpaqueTarget() { if (this._opaqueTarget) this.renderManager.releaseTempTarget(this._opaqueTarget); this._opaqueTarget = undefined; } constructor(renderManager, overrideMaterial, clearColor = new Color(0, 0, 0), clearAlpha = 0) { super(undefined, undefined, overrideMaterial, clearColor, clearAlpha); this.isExtendedRenderPass = true; this.enabled = true; this.passId = 'render'; this._doTransmissionFix = true; this.blurTransmissionTarget = true; this.preserveTransparentTarget = false; /** * Preserve the {@link opaqueTarget} after rendering. */ /* readonly */ this.preserveOpaqueTarget = false; this.onDirty = []; this.uiConfig = { label: 'Render Pass', type: 'folder', children: generateUiConfig(this), }; this.renderManager = renderManager; this._blendPass = new GenericBlendTexturePass({}, 'c = vec4(a.rgb * (1. - b.a) + b.rgb * b.a, 1.);', '', undefined, renderManager.maxHDRIntensity); this.setDirty = this.setDirty.bind(this); } // names are incorrect. We read from `writeBuffer` and write to `readBuffer`. same in super class render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { if (!this.enabled) return; let needsSwap = false; renderer.userData.mainRenderPass = true; if (!this._doTransmissionFix && !this.renderManager.rgbm) { super.render(renderer, writeBuffer || null, readBuffer, deltaTime, maskActive); this.needsSwap = needsSwap; renderer.userData.mainRenderPass = undefined; return; } const ud = renderer.userData; if (!ud) console.error('threejs is not patched. Use the @repalash/three.js-modded to this functionality.'); const useGBufferDepth = (this.renderManager.zPrepass || !this.renderManager.depthBuffer) && this.renderManager.gbufferTarget; let depthRenderBuffer = undefined; if (useGBufferDepth) { const gbuffer = this.renderManager.gbufferTarget; if (gbuffer) { const renderBufferProps = renderer.properties.get(gbuffer); depthRenderBuffer = renderBufferProps.__webglDepthRenderbuffer || renderBufferProps.__webglDepthbuffer; } if (!depthRenderBuffer) { console.warn('No depth/gbuffer present for zPrepass.'); } } const lastReadBuffer = readBuffer; // if msaa we need to create a new multi sampled target to render to // todo readBuffer = this.renderManager.msaa ? this.opaqueTarget : readBuffer; // readBuffer = this.opaqueTarget // if(readBuffer?.samples !== gbuffer?.samples) // console.error('ExtendedRenderPass - readBuffer and gbuffer samples are not same', readBuffer?.samples, gbuffer?.samples) let renderFn = () => { super.render(renderer, null, readBuffer, deltaTime, maskActive, depthRenderBuffer); // read is write in super.render (RenderPass) }; if (!this.renderManager.rgbm) { // Opaque + Transparent { const curClear = this.clear; const curClearDepth = renderer.autoClearDepth; renderer.autoClearDepth = !useGBufferDepth; this.clear = true; renderer.renderWithModes({ shadowMapRender: true, backgroundRender: true, opaqueRender: true, transparentRender: true, transmissionRender: false, }, renderFn); this.clear = curClear; renderer.autoClearDepth = curClearDepth; } // Transmissive { const source = !readBuffer ? undefined : Array.isArray(readBuffer.texture) ? readBuffer.texture[0] : readBuffer.texture; // todo: first check if any transmissive object is there to use this buffer this.renderManager.blit(writeBuffer, { clear: true, source }); // viewer.renderer.blit(writeBuffer.texture as any, readBuffer as any, {}) // super.render(renderer, undefined as any, writeBuffer, deltaTime, maskActive); // copy read to write buffer const curClear = this.clear; this.clear = false; // don't need this clear is already false // const curClearDepth = renderer.autoClearDepth // renderer.autoClearDepth = false ud.transmissionRenderTarget = writeBuffer; ud.blurTransmissionTarget = this.blurTransmissionTarget && ud.transmissionRenderTarget.samples === 0; // todo: not working with msaa target. its fine now because writeBuffer is never multi sampled renderer.renderWithModes({ shadowMapRender: false, backgroundRender: false, opaqueRender: false, transparentRender: false, transmissionRender: true, }, renderFn); ud.blurTransmissionTarget = undefined; ud.transmissionRenderTarget = undefined; // renderer.autoClearDepth = curClearDepth this.clear = curClear; } needsSwap = false; } else if (this.renderManager.rgbm) { needsSwap = false; const renderToScreen = this.renderToScreen; if (renderToScreen && !writeBuffer) { console.error('ExtendedRenderPass: renderToScreen is true but writeBuffer is not set, which is required for rgbm'); } this.renderToScreen = false; // for super RenderPass.render if (renderer.info && !renderer.info.autoReset) throw 'renderer.info.autoReset must be true'; // it is required to check if any object is rendered in the scene, also frame count is maintained separately in the render manager, use that. // Opaque { const curClearDepth = renderer.autoClearDepth; renderer.autoClearDepth = !useGBufferDepth; renderer.renderWithModes({ shadowMapRender: true, backgroundRender: true, opaqueRender: true, transparentRender: false, transmissionRender: false, }, renderFn); // render to readBuffer renderer.autoClearDepth = curClearDepth; } if (!useGBufferDepth && readBuffer) { const renderBufferProps2 = renderer.properties.get(readBuffer); depthRenderBuffer = renderBufferProps2.__webglDepthRenderbuffer || renderBufferProps2.__webglDepthbuffer; } // readBuffer has data renderFn = () => { super.render(renderer, null, this.transparentTarget, deltaTime, maskActive, depthRenderBuffer); }; // Transparent { const curClear = this.clear; const curClearDepth = renderer.autoClearDepth; renderer.autoClearDepth = false; this.clear = true; renderer.renderWithModes({ shadowMapRender: false, backgroundRender: false, opaqueRender: false, transparentRender: true, transmissionRender: false, }, renderFn); // render to transparentTarget this.clear = curClear; renderer.autoClearDepth = curClearDepth; } if (!renderer.info || renderer.info.render.calls > 0) { // writeBuffer = transparentTarget + readBuffer this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture; this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive); needsSwap = true; // writeBuffer has the data now. } // if needsSwap, writeBuffer has data, else readBuffer // Transmission { const curClear = this.clear; this.clear = false; // it is cleared in transparent pass above even if no object is rendered // const curClearDepth = renderer.autoClearDepth // renderer.autoClearDepth = false // if needsSwap, writeBuffer has current data, else readBuffer ud.transmissionRenderTarget = needsSwap ? writeBuffer : readBuffer; ud.blurTransmissionTarget = this.blurTransmissionTarget && ud.transmissionRenderTarget.samples === 0; // todo: not working with msaa renderer.renderWithModes({ shadowMapRender: false, backgroundRender: false, opaqueRender: false, transparentRender: false, transmissionRender: true, }, renderFn); // render to transparentTarget ud.blurTransmissionTarget = undefined; ud.transmissionRenderTarget = undefined; // renderer.autoClearDepth = curClearDepth this.clear = curClear; } // console.log(renderer.info.render.calls) if (!renderer.info || renderer.info.render.calls > 0) { // console.log('missive blit', renderer.info.render.frame) // writeBuffer = transparentTarget + readBuffer. opaque will overwrite opaque pixels again this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture; this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive); needsSwap = true; // writeBuffer has the data now. } // if needsSwap, writeBuffer has data, else readBuffer if (renderToScreen) { this.renderToScreen = true; const tex = needsSwap ? writeBuffer?.texture : readBuffer?.texture; const source = Array.isArray(tex) ? tex[0] : tex; source && this.renderManager.blit(undefined, { source, respectColorSpace: true, }); // needsSwap = false } } // todo no need to do this if renderToScreen is true // resolve msaa if (!needsSwap && lastReadBuffer !== readBuffer && readBuffer) { // copy from readBuffer to lastReadBuffer const source = Array.isArray(readBuffer.texture) ? readBuffer.texture[0] : readBuffer.texture; source && this.renderManager.blit(lastReadBuffer, { source: source, clear: true, }); readBuffer = lastReadBuffer; } if (!this.preserveTransparentTarget) this._releaseTransparentTarget(); if (!this.preserveOpaqueTarget) this._releaseOpaqueTarget(); this.needsSwap = needsSwap; renderer.userData.mainRenderPass = undefined; } dispose() { this._releaseTransparentTarget(); this.onDirty = []; this.scene = undefined; this.camera = undefined; super.dispose?.(); } setDirty() { this.onDirty.forEach(v => v()); } beforeRender(scene, camera, _) { this.scene = scene; this.camera = camera; } // legacy /** * @deprecated renamed to {@link isExtendedRenderPass} */ get isRenderPass2() { console.error('isRenderPass2 is deprecated, use isExtendedRenderPass instead'); return true; } } __decorate([ uiToggle('Enabled'), serialize() ], ExtendedRenderPass.prototype, "enabled", void 0); /** * @deprecated renamed to {@link ExtendedRenderPass} */ export class RenderPass2 extends ExtendedRenderPass { constructor(...args) { console.error('RenderPass2 is deprecated, use ExtendedRenderPass instead'); super(...args); } } //# sourceMappingURL=ExtendedRenderPass.js.map