Просмотр исходного кода

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

master
Palash Bansal 2 лет назад
Родитель
Сommit
2fd631cdbc
Аккаунт пользователя с таким Email не найден

+ 8
- 3
src/plugins/base/PipelinePassPlugin.ts Просмотреть файл

@@ -16,13 +16,13 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend
set enabled(value: boolean) {
if (this._pass) this._pass.enabled = value
this._enabledTemp = value
this.setDirty()
}


@uiConfig()
@serialize('pass')
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
@@ -40,7 +40,7 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend
onAdded(viewer: TViewer): void {
super.onAdded(viewer)

this._pass = this.createPass(viewer)
this._pass = this._createPass()
this._pass.onDirty?.push(viewer.setDirty)
this._pass.beforeRender = wrapThisFunction(this._beforeRender, this._pass.beforeRender)
viewer.renderManager.registerPass(this._pass)
@@ -66,6 +66,11 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend
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 {

+ 94
- 31
src/plugins/pipeline/DepthBufferPlugin.ts Просмотреть файл

@@ -1,7 +1,7 @@
import {
BasicDepthPacking,
Color,
IUniform,
DepthPackingStrategies,
MeshDepthMaterial,
NoBlending,
Texture,
@@ -11,9 +11,13 @@ import {
} from 'three'
import {GBufferRenderPass} from '../../postprocessing'
import {ThreeViewer} from '../../viewer'
import {IShaderPropertiesUpdater} from '../../materials'
import {MaterialExtension} from '../../materials'
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 = ''
// type DepthBufferPluginTarget = WebGLMultipleRenderTargets | WebGLRenderTarget
@@ -22,8 +26,7 @@ export type DepthBufferPluginPass = GBufferRenderPass<'depth', DepthBufferPlugin

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

readonly passId = 'depth'
public static readonly PluginType = 'DepthBufferPlugin'
@@ -36,24 +39,89 @@ export class DepthBufferPlugin
depthPacking: BasicDepthPacking,
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.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
const pass = new GBufferRenderPass('depth', this.target, this.material, new Color(0, 0, 0), 1)
const preprocessMaterial = pass.preprocessMaterial
@@ -65,27 +133,22 @@ export class DepthBufferPlugin
}

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

onRemove(viewer: ThreeViewer): void {
if (this.target) {
viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this._disposeTarget()
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 Просмотреть файл

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

readonly passId = 'normal'
public static readonly PluginType = 'NormalBufferPlugin'
@@ -41,10 +38,16 @@ export class NormalBufferPlugin
readonly material: MeshNormalMaterial = new MeshNormalMaterial2({
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,
// samples: v.renderManager.composerTarget.samples || 0,
@@ -57,7 +60,19 @@ export class NormalBufferPlugin
})
this.texture = this.target.texture
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
const pass = new GBufferRenderPass('normal', this.target, this.material, new Color(0, 0, 0), 1)
const preprocessMaterial = pass.preprocessMaterial
@@ -69,27 +84,19 @@ export class NormalBufferPlugin
}

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

onRemove(viewer: ThreeViewer): void {
if (this.target) {
viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this._disposeTarget()
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 {

+ 10
- 0
src/plugins/pipeline/shaders/DepthBufferPlugin.unpack.glsl Просмотреть файл

@@ -0,0 +1,10 @@
#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 Просмотреть файл

@@ -0,0 +1,43 @@
#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 Просмотреть файл

@@ -8,10 +8,14 @@ import {
WebGLMultipleRenderTargets,
WebGLRenderTarget,
} 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 {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 TViewerScreenShader = TViewerScreenShaderFrag | ShaderMaterialParameters | ShaderMaterial2
@@ -22,11 +26,12 @@ export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'scr
after: IPassID[] = ['render']
required: IPassID[] = ['render']

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

outputColorSpace: ColorSpace = SRGBColorSpace
@@ -56,34 +61,32 @@ export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'scr
this._lastReadBuffer = undefined
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) {
return {
...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: {
tDiffuse: {value: null},
tTransparent: {value: null},
},
transparent: true,
blending: NoBlending,

+ 1
- 2
src/utils/browser-helpers.ts Просмотреть файл

@@ -19,10 +19,9 @@ export {imageToCanvas, imageBitmapToBase64, imageUrlToImageData, imageDataToCanv
export {absMax, clearBit, updateBit} from 'ts-browser-helpers'
export {includesAll} 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 {timeout, now} from 'ts-browser-helpers'
export {pathJoin, getUrlQueryParam, setUrlQueryParam, remoteWorkerURL} from 'ts-browser-helpers'
export {css, glsl, html, svgUrl} from 'ts-browser-helpers'
export {Serialization} from 'ts-browser-helpers'


Загрузка…
Отмена
Сохранить