UNPKG

threepipe

Version:

A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.

401 lines 18.2 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 { ClampToEdgeWrapping, Color, DoubleSide, GLSL1, GLSL3, NoBlending, TangentSpaceNormalMap, UniformsLib, UniformsUtils, UnsignedByteType, UnsignedIntType, Vector2, Vector4, } from 'three'; import { GBufferRenderPass } from '../../postprocessing'; import { ViewerRenderManager } from '../../viewer'; import { updateMaterialDefines } from '../../materials'; import { PipelinePassPlugin } from '../base/PipelinePassPlugin'; import { uiFolderContainer, uiImage } from 'uiconfig.js'; import { shaderReplaceString } from '../../utils'; import GBufferUnpack from './shaders/GBufferPlugin.unpack.glsl'; import GBufferMatVert from './shaders/GBufferPlugin.mat.vert.glsl'; import GBufferMatFrag from './shaders/GBufferPlugin.mat.frag.glsl'; import { ShaderMaterial2, } from '../../core'; /** * G-Buffer Plugin * * Adds a pre-render pass to render the g-buffer(depth+normal+flags) to render target(s) that can be used as gbuffer and for postprocessing. * @category Plugins */ let GBufferPlugin = class GBufferPlugin extends PipelinePassPlugin { get normalDepthTexture() { return this.textures[0]; } get flagsTexture() { return this.textures[1]; } get depthTexture() { return this.target?.depthTexture; } _createTargetAndMaterial(recreateTarget = true) { if (!this._viewer) return; if (recreateTarget) this._disposeTarget(); const useMultiple = this._viewer?.renderManager.isWebGL2 && this.renderFlagsBuffer; if (!this.target) { const rm = this._viewer.renderManager; this.target = this._viewer.renderManager.createTarget({ depthBuffer: true, samples: this._viewer.renderManager.zPrepass && this.isPrimaryGBuffer && rm.msaa ? // requirement for zPrepass typeof rm.msaa !== 'number' ? ViewerRenderManager.DEFAULT_MSAA_SAMPLES : rm.msaa : 0, type: this.bufferType, textureCount: useMultiple ? 2 : 1, depthTexture: this.renderDepthTexture, depthTextureType: this.depthTextureType, // magFilter: NearestFilter, // minFilter: NearestFilter, // generateMipmaps: false, // encoding: LinearEncoding, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, }); if (Array.isArray(this.target.texture)) { this.target.texture[0].name = 'gbufferDepthNormal'; this.target.texture[1].name = 'gbufferFlags'; this.textures = this.target.texture; // todo flag buffer filtering? // const flagTexture = this.flagsTexture // flagTexture.generateMipmaps = false // flagTexture.minFilter = NearestFilter // flagTexture.magFilter = NearestFilter } else { this.target.texture.name = 'gbufferDepthNormal'; this.textures.push(this.target.texture); } } if (!this.material) { this.material = new GBufferMaterial(useMultiple, { blending: NoBlending, transparent: true, }); } // if (this._pass) this._pass.target = this.target if (this.isPrimaryGBuffer) { this._viewer.renderManager.gbufferTarget = this.target; this._viewer.renderManager.gbufferUnpackExtension = this.unpackExtension; this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension]); this._isPrimaryGBufferSet = true; } } _disposeTarget() { if (!this._viewer) return; if (this.target) { this._viewer.renderManager.disposeTarget(this.target); this.target = undefined; } this.textures = []; if (this._isPrimaryGBufferSet) { // using a separate flag as when isPrimaryGBuffer is changed, we cannot check it. this._viewer.renderManager.gbufferTarget = undefined; this._viewer.renderManager.gbufferUnpackExtension = undefined; // this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo this._isPrimaryGBufferSet = false; } } _createPass() { this._createTargetAndMaterial(true); if (!this.target) throw new Error('GBufferPlugin: target not created'); if (!this.material) throw new Error('GBufferPlugin: material not created'); this.material.userData.isGBufferMaterial = true; const pass = new GBufferRenderPass(this.passId, () => this.target, this.material, new Color(1, 1, 1), 1); const preprocessMaterial = pass.preprocessMaterial; pass.preprocessMaterial = (m) => preprocessMaterial(m, m.userData.renderToDepth); // if renderToDepth is undefined then renderToGbuffer is taken internally pass.before = ['render']; pass.after = []; pass.required = ['render']; return pass; } _beforeRender(scene, camera, renderManager) { if (!super._beforeRender(scene, camera, renderManager) || !this.material) return false; camera.updateShaderProperties(this.material); return true; } constructor(bufferType = UnsignedByteType, isPrimaryGBuffer = true, enabled = true, renderFlagsBuffer = true, renderDepthTexture = false, depthTextureType = UnsignedIntType) { super(); this.renderFlagsBuffer = renderFlagsBuffer; this.renderDepthTexture = renderDepthTexture; this.depthTextureType = depthTextureType; this.passId = 'gbuffer'; // @uiConfig(/* {readOnly: true}*/) // todo: fix bug in uiconfig or tpImageGenerator because of which 0 index is not showing in the UI, when we uncomment this this.textures = []; // protected _depthPackingChanged() { // this.material.depthPacking = this.depthPacking // this.material.needsUpdate = true // if (this.unpackExtension && this.unpackExtension.extraDefines) { // this.unpackExtension.extraDefines.DEPTH_PACKING = this.depthPacking // this.unpackExtension.setDirty?.() // } // this.setDirty() // } this.unpackExtension = { /** * Use this in shader to get the snippet * ``` * // for gbuffer * #include <packing> * #define THREE_PACKING_INCLUDED * ``` * or if you don't need packing include * ``` * #include <gbuffer_unpack> * ``` * @param shader */ shaderExtender: (shader) => { const includes = ['gbuffer_unpack', 'packing']; const include = includes.find(i => shader.fragmentShader.includes(`#include <${i}>`)); shader.fragmentShader = shaderReplaceString(shader.fragmentShader, `#include <${include}>`, '\n' + GBufferUnpack + '\n', { append: include === 'packing' }); }, extraUniforms: { tNormalDepth: () => ({ value: this.normalDepthTexture }), tGBufferFlags: () => ({ value: this.flagsTexture }), tGBufferDepthTexture: () => ({ value: this.depthTexture }), }, extraDefines: { // ['GBUFFER_PACKING']: BasicDepthPacking, ['HAS_NORMAL_DEPTH_BUFFER']: () => this.normalDepthTexture ? 1 : undefined, ['GBUFFER_HAS_DEPTH_TEXTURE']: () => this.depthTexture ? 1 : undefined, ['GBUFFER_HAS_FLAGS']: () => this.flagsTexture ? 1 : undefined, // ['HAS_FLAGS_BUFFER']: ()=>this.flagsTexture ? 1 : undefined, ['HAS_GBUFFER']: () => this.isPrimaryGBuffer && this.normalDepthTexture ? 1 : undefined, // LINEAR_DEPTH: 1, // to tell that the depth is linear. todo; see SSAOPlugin. also add support in DepthBufferPlugin? }, priority: 100, isCompatible: () => true, }; this._isPrimaryGBufferSet = false; this.enabled = enabled; this.bufferType = bufferType; this.isPrimaryGBuffer = isPrimaryGBuffer; // this.depthPacking = depthPacking } registerGBufferUpdater(key, updater) { if (this.material) this.material.flagUpdaters.set(key, updater); } unregisterGBufferUpdater(key) { if (this.material) this.material.flagUpdaters.delete(key); } onRemove(viewer) { this._disposeTarget(); this.material?.dispose(); this.material = undefined; return super.onRemove(viewer); } /** * @deprecated use {@link normalDepthTexture} instead */ getDepthNormal() { return this.textures.length > 0 ? this.textures[0] : undefined; } /** * @deprecated use {@link flagsTexture} instead */ getFlagsTexture() { return this.textures.length > 1 ? this.textures[1] : undefined; } /** * @deprecated use {@link target} instead */ getTarget() { return this.target; } /** * @deprecated use {@link unpackExtension} instead */ getUnpackSnippet() { return GBufferUnpack; } /** * @deprecated use {@link unpackExtension} instead, it adds the same uniforms and defines * @param material */ updateShaderProperties(material) { if (material.uniforms.tNormalDepth) material.uniforms.tNormalDepth.value = this.normalDepthTexture ?? undefined; else this._viewer?.console.warn('BaseRenderer: no uniform: tNormalDepth'); if (material.uniforms.tGBufferFlags) { material.uniforms.tGBufferFlags.value = this.flagsTexture ?? undefined; const t = material.uniforms.tGBufferFlags.value ? 1 : 0; if (t !== material.defines.GBUFFER_HAS_FLAGS) { material.defines.GBUFFER_HAS_FLAGS = t; material.needsUpdate = true; } } return this; } }; GBufferPlugin.PluginType = 'GBuffer'; __decorate([ uiImage( /* {readOnly: true}*/) ], GBufferPlugin.prototype, "normalDepthTexture", null); __decorate([ uiImage( /* {readOnly: true}*/) ], GBufferPlugin.prototype, "flagsTexture", null); __decorate([ uiImage( /* {readOnly: true}*/) ], GBufferPlugin.prototype, "depthTexture", null); GBufferPlugin = __decorate([ uiFolderContainer('G-Buffer Plugin') ], GBufferPlugin); export { GBufferPlugin }; /** * Renders DepthNormal to a texture and flags to another */ export class GBufferMaterial extends ShaderMaterial2 { constructor(multipleRT = true, parameters) { super({ vertexShader: GBufferMatVert, fragmentShader: GBufferMatFrag, uniforms: UniformsUtils.merge([ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { cameraNearFar: { value: new Vector2(0.1, 1000) }, // this has to be set from outside flags: { value: new Vector4(255, 255, 255, 255) }, }, ]), defines: { // eslint-disable-next-line @typescript-eslint/naming-convention IS_GLSL3: multipleRT ? '1' : '0', }, glslVersion: multipleRT ? GLSL3 : GLSL1, ...parameters, }); this.flagUpdaters = new Map(); this.normalMapType = TangentSpaceNormalMap; this.flatShading = false; this.reset(); } onBeforeRender(renderer, scene, camera, geometry, object) { super.onBeforeRender(renderer, scene, camera, geometry, object); let material = object.material; if (Array.isArray(material)) { // todo: add support for multi materials. material = material[0]; } if (!material) return; const setMap = (key) => { const map = material[key]; if (!map) return; this.uniforms[key].value = map; if (!this.uniforms[key + 'Transform']) console.error('GBufferMaterial: ' + key + 'Transform is not defined in uniform'); else renderer.materials.refreshTransformUniform(map, this.uniforms[key + 'Transform']); }; setMap('map'); if (material.side !== undefined) this.side = material.side ?? DoubleSide; setMap('alphaMap'); if (material.alphaTest !== undefined) this.alphaTest = material.alphaTest < 1e-4 ? 1e-4 : material.alphaTest; setMap('bumpMap'); if (material.bumpScale !== undefined) this.uniforms.bumpScale.value = material.bumpScale; setMap('normalMap'); if (material.normalScale !== undefined) this.uniforms.normalScale.value.copy(material.normalScale); if (material.normalMapType !== undefined) this.normalMapType = material.normalMapType; if (material.flatShading !== undefined) this.flatShading = material.flatShading; setMap('displacementMap'); if (material.displacementScale !== undefined) this.uniforms.displacementScale.value = material.displacementScale; if (material.displacementBias !== undefined) this.uniforms.displacementBias.value = material.displacementBias; if (material.wireframe !== undefined) this.wireframe = material.wireframe; if (material.wireframeLinewidth !== undefined) this.wireframeLinewidth = material.wireframeLinewidth; /* GBuffer Flags has the following data 1st Rendertarget has Depth and Normal buffers 2nd Render Target:: x : Empty y : first 3 bits lut index, second 5 bits bevel radius z : material id (userData.gBufferData?.materialId, userData.matId) w : this field is for setting bits - lutEnable-0, tonemap-1, bloom-2, ssao(cast)-3, dof-4 */ this.uniforms.flags.value.set(255, 255, 255, 255); const materialId = material.userData.gBufferData?.materialId ?? material.userData.matId; // matId for backward compatibility this.uniforms.flags.value.z = materialId || 0; this.flagUpdaters.forEach((updater) => updater(this.uniforms.flags.value, { material, renderer, scene, camera, geometry, object })); this.uniforms.flags.value.x /= 255; this.uniforms.flags.value.y /= 255; this.uniforms.flags.value.z /= 255; this.uniforms.flags.value.w /= 255; this.uniformsNeedUpdate = true; updateMaterialDefines({ // ['USE_ALPHAMAP']: this.uniforms.alphaMap.value ? 1 : undefined, ['ALPHAMAP_UV']: this.uniforms.alphaMap.value ? 'uv' : undefined, // todo use getChannel, see WebGLPrograms.js ['USE_DISPLACEMENTMAP']: this.uniforms.displacementMap.value ? 1 : undefined, ['DISPLACEMENTMAP_UV']: this.uniforms.displacementMap.value ? 'uv' : undefined, // todo use getChannel, see WebGLPrograms.js ['ALPHA_I_RGBA_PACKING']: material.userData.ALPHA_I_RGBA_PACKING ? 1 : undefined, ['FORCED_LINEAR_DEPTH']: material.userData.forcedLinearDepth ?? undefined, // todo add to DepthBufferPlugin as well. }, material); // todo: do the same in DepthBufferPlugin and NormalBufferPlugin // what about the material extension settings in the userData of the source materials? if (material.materialExtensions?.length) { this.registerMaterialExtensions(material.materialExtensions); } // this.transparent = true this.needsUpdate = true; } onAfterRender(renderer, scene, camera, geometry, object) { super.onAfterRender(renderer, scene, camera, geometry, object); let material = object.material; if (Array.isArray(material)) { // todo: add support for multi materials. material = material[0]; } if (!material) return; if (material.materialExtensions?.length) { this.unregisterMaterialExtensions(material.materialExtensions); } this.reset(); } reset() { this.uniforms.map.value = null; this.side = DoubleSide; this.uniforms.alphaMap.value = null; this.alphaTest = 0.001; this.uniforms.bumpMap.value = null; this.uniforms.bumpScale.value = 1; this.uniforms.normalMap.value = null; this.uniforms.normalScale.value.set(1, 1); this.normalMapType = TangentSpaceNormalMap; this.flatShading = false; this.uniforms.displacementMap.value = null; this.uniforms.displacementScale.value = 1; this.uniforms.displacementBias.value = 0; this.uniforms.flags.value.set(255, 255, 255, 255); this.wireframe = false; this.wireframeLinewidth = 1; } } /** * @deprecated use GBufferMaterial instead */ export class DepthNormalMaterial extends GBufferMaterial { constructor(multipleRT, parameters) { super(multipleRT, parameters); console.warn('DepthNormalMaterial is deprecated, use GBufferMaterial instead'); } } //# sourceMappingURL=GBufferPlugin.js.map