Sfoglia il codice sorgente

Add material extension to depth buffer plugin, refactor normal buffer plugin, rewrite ScreenPass to support GBuffer(Depth buffer).

master
Palash Bansal 2 anni fa
parent
commit
2fd631cdbc
Nessun account collegato all'indirizzo email del committer

+ 8
- 3
src/plugins/base/PipelinePassPlugin.ts Vedi File

set enabled(value: boolean) { set enabled(value: boolean) {
if (this._pass) this._pass.enabled = value if (this._pass) this._pass.enabled = value
this._enabledTemp = value this._enabledTemp = value
this.setDirty()
} }



@uiConfig() @uiConfig()
@serialize('pass') @serialize('pass')
protected _pass?: T protected _pass?: T
abstract createPass(v:TViewer):T
protected abstract _createPass():T


/** /**
* This function is called every frame before composer render, if this pass is being used in the pipeline * This function is called every frame before composer render, if this pass is being used in the pipeline
onAdded(viewer: TViewer): void { onAdded(viewer: TViewer): void {
super.onAdded(viewer) super.onAdded(viewer)


this._pass = this.createPass(viewer)
this._pass = this._createPass()
this._pass.onDirty?.push(viewer.setDirty) this._pass.onDirty?.push(viewer.setDirty)
this._pass.beforeRender = wrapThisFunction(this._beforeRender, this._pass.beforeRender) this._pass.beforeRender = wrapThisFunction(this._beforeRender, this._pass.beforeRender)
viewer.renderManager.registerPass(this._pass) viewer.renderManager.registerPass(this._pass)
return super.fromJSON(data, meta) return super.fromJSON(data, meta)
} }


setDirty() {
this._viewer?.setDirty()
this.uiConfig?.uiRefresh?.(true, 'postFrame', 100) // adding delay for a few frames, so render target(if any can update)
}

} }


function wrapThisFunction<T extends AnyFunction, T2>(f1: ()=>void, f2?: T): T { function wrapThisFunction<T extends AnyFunction, T2>(f1: ()=>void, f2?: T): T {

+ 94
- 31
src/plugins/pipeline/DepthBufferPlugin.ts Vedi File

import { import {
BasicDepthPacking, BasicDepthPacking,
Color, Color,
IUniform,
DepthPackingStrategies,
MeshDepthMaterial, MeshDepthMaterial,
NoBlending, NoBlending,
Texture, Texture,
} from 'three' } from 'three'
import {GBufferRenderPass} from '../../postprocessing' import {GBufferRenderPass} from '../../postprocessing'
import {ThreeViewer} from '../../viewer' import {ThreeViewer} from '../../viewer'
import {IShaderPropertiesUpdater} from '../../materials'
import {MaterialExtension} from '../../materials'
import {PipelinePassPlugin} from '../base/PipelinePassPlugin' import {PipelinePassPlugin} from '../base/PipelinePassPlugin'
import {uiFolderContainer, uiImage} from 'uiconfig.js'
import {uiDropdown, uiFolderContainer, uiImage} from 'uiconfig.js'
import {shaderReplaceString} from '../../utils'
import {onChange} from 'ts-browser-helpers'
import DepthBufferUnpack from './shaders/DepthBufferPlugin.unpack.glsl'
import {threeConstMappings} from '../../three'


export type DepthBufferPluginEventTypes = '' export type DepthBufferPluginEventTypes = ''
// type DepthBufferPluginTarget = WebGLMultipleRenderTargets | WebGLRenderTarget // type DepthBufferPluginTarget = WebGLMultipleRenderTargets | WebGLRenderTarget


@uiFolderContainer('Depth Buffer Plugin') @uiFolderContainer('Depth Buffer Plugin')
export class DepthBufferPlugin export class DepthBufferPlugin
extends PipelinePassPlugin<DepthBufferPluginPass, 'depth', DepthBufferPluginEventTypes>
implements IShaderPropertiesUpdater {
extends PipelinePassPlugin<DepthBufferPluginPass, 'depth', DepthBufferPluginEventTypes> {


readonly passId = 'depth' readonly passId = 'depth'
public static readonly PluginType = 'DepthBufferPlugin' public static readonly PluginType = 'DepthBufferPlugin'
depthPacking: BasicDepthPacking, depthPacking: BasicDepthPacking,
blending: NoBlending, blending: NoBlending,
}) })
// private _gbufferPass?: IFilter<GBufferRenderPass<WebGLMultipleRenderTargets>

createPass(v: ThreeViewer) {
if (!this.target) this.target = v.renderManager.createTarget<DepthBufferPluginTarget>(
{
depthBuffer: true,
samples: v.renderManager.composerTarget.samples || 0,
type: this.bufferType,
// magFilter: NearestFilter,
// minFilter: NearestFilter,
// generateMipmaps: false,
// encoding: LinearEncoding,
})

@onChange(DepthBufferPlugin.prototype._depthPackingChanged)
@uiDropdown('Depth Packing', threeConstMappings.DepthPackingStrategies.uiConfig) depthPacking: DepthPackingStrategies

// @onChange2(DepthBufferPlugin.prototype._createTarget)
// @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
readonly bufferType: TextureDataType // cannot be changed after creation (for now)

// @uiToggle()
// @onChange2(DepthBufferPlugin.prototype._createTarget)
readonly isPrimaryGBuffer: boolean // cannot be changed after creation (for now)

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()
}

unpackExtension: MaterialExtension = {
shaderExtender: (shader)=>{
shader.fragmentShader = shaderReplaceString(shader.fragmentShader,
'#include <packing>',
'\n' + DepthBufferUnpack + '\n', {append: true})
},
extraUniforms: {
tDepthBuffer: ()=>({value: this.target?.texture}),
},
extraDefines: {
['DEPTH_PACKING']: BasicDepthPacking,
['HAS_DEPTH_BUFFER']: ()=>this.target?.texture ? 1 : undefined,
['HAS_GBUFFER']: ()=>this.isPrimaryGBuffer && this.target?.texture ? 1 : undefined,
},
priority: 100,
isCompatible: () => true,
}

private _isPrimaryGBufferSet = false
protected _createTarget(recreate = true) {
if (!this._viewer) return
if (recreate) this._disposeTarget()
if (!this.target)
this.target = this._viewer.renderManager.createTarget<DepthBufferPluginTarget>(
{
depthBuffer: true,
samples: this._viewer.renderManager.composerTarget.samples || 0,
type: this.bufferType,
// magFilter: NearestFilter,
// minFilter: NearestFilter,
// generateMipmaps: false,
// encoding: LinearEncoding,
})

this.texture = this.target.texture this.texture = this.target.texture
this.texture.name = 'depthBuffer' this.texture.name = 'depthBuffer'


if (this.isPrimaryGBuffer) v.renderManager.gbufferTarget = this.target
if (this.isPrimaryGBuffer) {
this._viewer.renderManager.gbufferTarget = this.target
this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension])
this._isPrimaryGBufferSet = true
}
}


protected _disposeTarget() {
if (!this._viewer) return
if (this.target) {
this._viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this.texture = undefined
if (this._isPrimaryGBufferSet) { // using a separate flag as when isPrimaryGBuffer is changed, we cannot check it.
this._viewer.renderManager.gbufferTarget = undefined
// this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo
this._isPrimaryGBufferSet = false
}
}

protected _createPass() {
this._createTarget(true)
if (!this.target) throw new Error('DepthBufferPlugin: target not created')
this.material.userData.isGBufferMaterial = true this.material.userData.isGBufferMaterial = true
const pass = new GBufferRenderPass('depth', this.target, this.material, new Color(0, 0, 0), 1) const pass = new GBufferRenderPass('depth', this.target, this.material, new Color(0, 0, 0), 1)
const preprocessMaterial = pass.preprocessMaterial const preprocessMaterial = pass.preprocessMaterial
} }


constructor( constructor(
public readonly bufferType: TextureDataType = UnsignedByteType,
public readonly isPrimaryGBuffer = false,
bufferType: TextureDataType = UnsignedByteType,
isPrimaryGBuffer = false,
enabled = true, enabled = true,
depthPacking: DepthPackingStrategies = BasicDepthPacking,
) { ) {
super() super()
this.enabled = enabled this.enabled = enabled
this.depthPacking = depthPacking
this.bufferType = bufferType
this.isPrimaryGBuffer = isPrimaryGBuffer
} }


onRemove(viewer: ThreeViewer): void { onRemove(viewer: ThreeViewer): void {
if (this.target) {
viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this._disposeTarget()
return super.onRemove(viewer) return super.onRemove(viewer)
} }


updateShaderProperties(material: {defines: Record<string, string | number | undefined>; uniforms: {[p: string]: IUniform}}): this {
if (material.uniforms.tDepth) material.uniforms.tDepth.value = this.texture ?? undefined
else this._viewer?.console.warn('BaseRenderer: no uniform: tDepth')
return this
}

} }



+ 25
- 18
src/plugins/pipeline/NormalBufferPlugin.ts Vedi File

Color, Color,
FrontSide, FrontSide,
HalfFloatType, HalfFloatType,
IUniform,
LinearSRGBColorSpace, LinearSRGBColorSpace,
MeshNormalMaterial, MeshNormalMaterial,
NearestFilter, NearestFilter,
} from 'three' } from 'three'
import {GBufferRenderPass} from '../../postprocessing' import {GBufferRenderPass} from '../../postprocessing'
import {ThreeViewer} from '../../viewer' import {ThreeViewer} from '../../viewer'
import {IShaderPropertiesUpdater} from '../../materials'
import {PipelinePassPlugin} from '../base/PipelinePassPlugin' import {PipelinePassPlugin} from '../base/PipelinePassPlugin'
import type {IMaterial, PhysicalMaterial} from '../../core' import type {IMaterial, PhysicalMaterial} from '../../core'
import {uiFolderContainer, uiImage} from 'uiconfig.js' import {uiFolderContainer, uiImage} from 'uiconfig.js'
export type NormalBufferPluginPass = GBufferRenderPass<'normal', NormalBufferPluginTarget> export type NormalBufferPluginPass = GBufferRenderPass<'normal', NormalBufferPluginTarget>
@uiFolderContainer('Normal Buffer Plugin') @uiFolderContainer('Normal Buffer Plugin')
export class NormalBufferPlugin export class NormalBufferPlugin
extends PipelinePassPlugin<NormalBufferPluginPass, 'normal', NormalBufferPluginEventTypes>
implements IShaderPropertiesUpdater {
extends PipelinePassPlugin<NormalBufferPluginPass, 'normal', NormalBufferPluginEventTypes> {


readonly passId = 'normal' readonly passId = 'normal'
public static readonly PluginType = 'NormalBufferPlugin' public static readonly PluginType = 'NormalBufferPlugin'
readonly material: MeshNormalMaterial = new MeshNormalMaterial2({ readonly material: MeshNormalMaterial = new MeshNormalMaterial2({
blending: NoBlending, blending: NoBlending,
}) })
// private _gbufferPass?: IFilter<GBufferRenderPass<WebGLMultipleRenderTargets>


createPass(v: ThreeViewer) {
if (!this.target) this.target = v.renderManager.createTarget<NormalBufferPluginTarget>(
// @onChange2(NormalBufferPlugin.prototype._createTarget)
// @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
readonly bufferType: TextureDataType // cannot be changed after creation (for now)

protected _createTarget(recreate = true) {
if (!this._viewer) return
if (recreate) this._disposeTarget()

if (!this.target) this.target = this._viewer.renderManager.createTarget<NormalBufferPluginTarget>(
{ {
depthBuffer: true, depthBuffer: true,
// samples: v.renderManager.composerTarget.samples || 0, // samples: v.renderManager.composerTarget.samples || 0,
}) })
this.texture = this.target.texture this.texture = this.target.texture
this.texture.name = 'normalBuffer' this.texture.name = 'normalBuffer'
}
protected _disposeTarget() {
if (!this._viewer) return
if (this.target) {
this._viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this.texture = undefined
}


protected _createPass() {
this._createTarget(true)
if (!this.target) throw new Error('NormalBufferPlugin: target not created')
this.material.userData.isGBufferMaterial = true this.material.userData.isGBufferMaterial = true
const pass = new GBufferRenderPass('normal', this.target, this.material, new Color(0, 0, 0), 1) const pass = new GBufferRenderPass('normal', this.target, this.material, new Color(0, 0, 0), 1)
const preprocessMaterial = pass.preprocessMaterial const preprocessMaterial = pass.preprocessMaterial
} }


constructor( constructor(
public readonly bufferType: TextureDataType = HalfFloatType,
bufferType: TextureDataType = HalfFloatType,
enabled = true, enabled = true,
) { ) {
super() super()
this.enabled = enabled this.enabled = enabled
this.bufferType = bufferType
} }


onRemove(viewer: ThreeViewer): void { onRemove(viewer: ThreeViewer): void {
if (this.target) {
viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this._disposeTarget()
return super.onRemove(viewer) return super.onRemove(viewer)
} }


updateShaderProperties(material: {defines: Record<string, string | number | undefined>; uniforms: {[p: string]: IUniform}}): this {
if (material.uniforms.tNormal) material.uniforms.tNormal.value = this.texture ?? undefined
else this._viewer?.console.warn('BaseRenderer: no uniform: tNormal')
return this
}

} }


class MeshNormalMaterial2 extends MeshNormalMaterial { class MeshNormalMaterial2 extends MeshNormalMaterial {

+ 10
- 0
src/plugins/pipeline/shaders/DepthBufferPlugin.unpack.glsl Vedi File

#if defined(HAS_DEPTH_BUFFER)
#if DEPTH_PACKING == 3200
#define unpackDepth(rgba_depth) (1.0 - rgba_depth.r)
#elif DEPTH_PACKING == 3201
#define unpackDepth(rgba_depth) unpackRGBAToDepth(rgba_depth)
#endif
uniform sampler2D tDepthBuffer;
#define getDepth(uv) unpackDepth(texture2D(tDepthBuffer, uv))
#endif


+ 43
- 0
src/postprocessing/ScreenPass.glsl Vedi File

#include <packing>

varying vec2 vUv;

#include <alphatest_pars_fragment>

void main() {

vec4 diffuseColor = tDiffuseTexelToLinear (texture2D(tDiffuse, vUv));

#ifdef HAS_TRANSPARENT_TARGET
vec4 transparentColor = tTransparentTexelToLinear (texture2D(tTransparent, vUv));
#else
vec4 transparentColor = vec4(0.0);
#endif

#ifdef HAS_GBUFFER
float depth = getDepth(vUv);
bool isBackground = depth>0.99 && transparentColor.a < 0.001;
#endif

#glMarker

#ifdef HAS_GBUFFER

#ifdef CLIP_BACKGROUND
if(isBackground) diffuseColor.a = 0.0;
if(depth>0.99 && transparentColor.a >= 0.001) diffuseColor.a = transparentColor.a;
#endif

if(depth < 0.00001) diffuseColor.a = 0.0;

#endif

#include <alphatest_fragment>
#ifdef OPAQUE
diffuseColor.a = 1.0;
#endif
gl_FragColor = diffuseColor;
//gl_FragColor = isBackground ? vec4(0, 0, 0, 1) : gl_FragColor;
// gl_FragColor = vec4(depth, 0, 0, 1);
#include <encodings_fragment>
}

+ 28
- 25
src/postprocessing/ScreenPass.ts Vedi File

WebGLMultipleRenderTargets, WebGLMultipleRenderTargets,
WebGLRenderTarget, WebGLRenderTarget,
} from 'three' } from 'three'
import {IWebGLRenderer, ShaderMaterial2} from '../core'
import {ICamera, IRenderManager, IScene, IWebGLRenderer, ShaderMaterial2} from '../core'
import {CopyShader} from 'three/examples/jsm/shaders/CopyShader.js' import {CopyShader} from 'three/examples/jsm/shaders/CopyShader.js'
import {IPassID, IPipelinePass} from './Pass' import {IPassID, IPipelinePass} from './Pass'
import {uiFolderContainer} from 'uiconfig.js'
import {uiFolderContainer, uiToggle} from 'uiconfig.js'
import {ViewerRenderManager} from '../viewer'
import {matDefine} from '../three'
import ScreenPassShader from './ScreenPass.glsl'
import {shaderReplaceString} from '../utils'


export type TViewerScreenShaderFrag = string | [string, string] | {pars?: string, main: string} export type TViewerScreenShaderFrag = string | [string, string] | {pars?: string, main: string}
export type TViewerScreenShader = TViewerScreenShaderFrag | ShaderMaterialParameters | ShaderMaterial2 export type TViewerScreenShader = TViewerScreenShaderFrag | ShaderMaterialParameters | ShaderMaterial2
after: IPassID[] = ['render'] after: IPassID[] = ['render']
required: IPassID[] = ['render'] required: IPassID[] = ['render']


constructor(shader: TViewerScreenShader, ...textureID: string[]) {
constructor(shader: TViewerScreenShader = '', ...textureID: string[]) {
super( super(
(<any>shader)?.fragmentShader || (<ShaderMaterial2>shader)?.isShaderMaterial ? <ShaderMaterialParameters|ShaderMaterial2>shader : (<any>shader)?.fragmentShader || (<ShaderMaterial2>shader)?.isShaderMaterial ? <ShaderMaterialParameters|ShaderMaterial2>shader :
makeScreenShader(shader), makeScreenShader(shader),
...textureID.length ? textureID : ['tDiffuse'])
...textureID.length ? textureID : ['tDiffuse', 'tTransparent', 'tGBuffer'])
this.material.addEventListener('materialUpdate', this.setDirty)
} }


outputColorSpace: ColorSpace = SRGBColorSpace outputColorSpace: ColorSpace = SRGBColorSpace
this._lastReadBuffer = undefined this._lastReadBuffer = undefined
super.dispose() super.dispose()
} }

@matDefine('CLIP_BACKGROUND', undefined, undefined, ScreenPass.prototype.setDirty, (v)=>v ? '1' : undefined, (v)=>!!v)
@uiToggle() clipBackground = false

beforeRender(_: IScene, _1: ICamera, renderManager: ViewerRenderManager) {
this.material.uniforms.tTransparent.value = renderManager.renderPass.preserveTransparentTarget ? renderManager.renderPass.transparentTarget?.texture || null : null
this.material.defines.HAS_TRANSPARENT_TARGET = this.material.uniforms.tTransparent.value ? 1 : undefined
if (!this.material.defines.HAS_TRANSPARENT_TARGET) delete this.material.defines.HAS_TRANSPARENT_TARGET
}

setDirty() {
super.setDirty()
this._needsReRender = true
}
} }


function makeScreenShader(shader: string | [string, string] | {pars?: string; main: string} | ShaderMaterialParameters | ShaderMaterial2) { function makeScreenShader(shader: string | [string, string] | {pars?: string; main: string} | ShaderMaterialParameters | ShaderMaterial2) {
return { return {
...CopyShader, ...CopyShader,
fragmentShader: `
varying vec2 vUv;

#include <alphatest_pars_fragment>
${Array.isArray(shader) ? shader[0] : (<any>shader)?.pars || ''}

void main() {

vec4 diffuseColor = tDiffuseTexelToLinear (texture2D(tDiffuse, vUv));
#glMarker
${Array.isArray(shader) ? shader[1] : typeof shader === 'string' ? shader : (shader as any)?.main || ''}
#include <alphatest_fragment>
#ifdef OPAQUE
diffuseColor.a = 1.0;
#endif
gl_FragColor = diffuseColor;
#include <encodings_fragment>
}`,
fragmentShader:
shaderReplaceString(shaderReplaceString(ScreenPassShader,
'void main()', (Array.isArray(shader) ? shader[0] : (<any>shader)?.pars || '') + '\n', {prepend: true}),
'#glMarker', (Array.isArray(shader) ? shader[1] : typeof shader === 'string' ? shader : (shader as any)?.main || '') + '\n', {prepend: true}),
uniforms: { uniforms: {
tDiffuse: {value: null}, tDiffuse: {value: null},
tTransparent: {value: null},
}, },
transparent: true, transparent: true,
blending: NoBlending, blending: NoBlending,

+ 1
- 2
src/utils/browser-helpers.ts Vedi File

export {absMax, clearBit, updateBit} from 'ts-browser-helpers' export {absMax, clearBit, updateBit} from 'ts-browser-helpers'
export {includesAll} from 'ts-browser-helpers' export {includesAll} from 'ts-browser-helpers'
export {copyProps, getOrCall, getPropertyDescriptor, isPropertyWritable, safeSetProperty} from 'ts-browser-helpers' export {copyProps, getOrCall, getPropertyDescriptor, isPropertyWritable, safeSetProperty} from 'ts-browser-helpers'
export {deepAccessObject, getKeyByValue, objectHasOwn} from 'ts-browser-helpers'
export {deepAccessObject, getKeyByValue, objectHasOwn, objectMap2, objectMap} from 'ts-browser-helpers'
export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers' export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers'
export {timeout, now} from 'ts-browser-helpers' export {timeout, now} from 'ts-browser-helpers'
export {pathJoin, getUrlQueryParam, setUrlQueryParam, remoteWorkerURL} from 'ts-browser-helpers' export {pathJoin, getUrlQueryParam, setUrlQueryParam, remoteWorkerURL} from 'ts-browser-helpers'
export {css, glsl, html, svgUrl} from 'ts-browser-helpers' export {css, glsl, html, svgUrl} from 'ts-browser-helpers'
export {Serialization} from 'ts-browser-helpers' export {Serialization} from 'ts-browser-helpers'


Loading…
Annulla
Salva