Kaynağa Gözat

Create AScreenPassExtensionPlugin from TonemapPlugin, Add VignettePlugin and example

master
Palash Bansal 2 yıl önce
ebeveyn
işleme
dc73adf1ed
No account linked to committer's email address

+ 1
- 0
examples/index.html Dosyayı Görüntüle

@@ -220,6 +220,7 @@
<h2 class="category">Post-Processing</h2>
<ul>
<li><a href="./tonemap-plugin/">Tonemap Plugin </a></li>
<li><a href="./vignette-plugin/">Vignette Plugin </a></li>
<li><a href="./frame-fade-plugin/">Frame Fade Plugin </a></li>
</ul>
<h2 class="category">Rendering</h2>

+ 36
- 0
examples/vignette-plugin/index.html Dosyayı Görüntüle

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vignette Plugin</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs",
"@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs"
}
}

</script>
<style id="example-style">
html, body, #canvas-container, #mcanvas {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>
<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script>
</head>
<body>
<div id="canvas-container">
<canvas id="mcanvas"></canvas>
</div>

</body>

+ 21
- 0
examples/vignette-plugin/script.ts Dosyayı Görüntüle

@@ -0,0 +1,21 @@
import {_testFinish, IObject3D, ThreeViewer, VignettePlugin} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
})

// A GBuffer(depth buffer here) is required for the `tonemapBackground` flag in TonemapPlugin to work
viewer.addPluginSync(VignettePlugin)

await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf')

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPluginUi(VignettePlugin)

}

init().then(_testFinish)

+ 2
- 0
src/plugins/index.ts Dosyayı Görüntüle

@@ -31,7 +31,9 @@ export {KTXLoadPlugin} from './import/KTXLoadPlugin'
export {KTX2LoadPlugin} from './import/KTX2LoadPlugin'

// postprocessing
export {AScreenPassExtensionPlugin} from './postprocessing/AScreenPassExtensionPlugin'
export {TonemapPlugin} from './postprocessing/TonemapPlugin'
export {VignettePlugin} from './postprocessing/VignettePlugin'

// animation
export {GLTFAnimationPlugin} from './animation/GLTFAnimationPlugin'

+ 109
- 0
src/plugins/postprocessing/AScreenPassExtensionPlugin.ts Dosyayı Görüntüle

@@ -0,0 +1,109 @@
import {AViewerPlugin, AViewerPluginSync, ThreeViewer} from '../../viewer'
import {MaterialExtension} from '../../materials'
import {Shader, Vector4, WebGLRenderer} from 'three'
import {IMaterial} from '../../core'
import {shaderReplaceString} from '../../utils'

// todo move
export interface GBufferUpdater {
updateGBufferFlags: (material: IMaterial, data: Vector4) => void
}

/**
* Base Screen Pass Extension Plugin
*
* Extend the class to add an extension to {@link ScreenPass} material.
* See {@link TonemapPlugin} and {@link VignettePlugin} for examples.
*
*
* @category Plugins
*/
export abstract class AScreenPassExtensionPlugin<T extends string> extends AViewerPluginSync<T> implements MaterialExtension, GBufferUpdater {
declare ['constructor']: (typeof AScreenPassExtensionPlugin) & (typeof AViewerPluginSync) & (typeof AViewerPlugin)

abstract enabled: boolean

set uniformsNeedUpdate(v: boolean) { // for @uniform decorator
if (v) this.setDirty()
}

constructor() {
super()
this.setDirty = this.setDirty.bind(this)
}

/**
* The priority of the material extension when applied to the material in ScreenPass
* set to very low priority, so applied at the end
*/
priority = -100

protected _shaderPatch = ''

shaderExtender(shader: Shader, _: IMaterial, _1: WebGLRenderer): void {
if (!this.enabled) return

shader.fragmentShader = shaderReplaceString(
shader.fragmentShader,
'#glMarker', '\n' + this._shaderPatch + '\n',
{prepend: true}
)
}

getUiConfig(): any {
return this.uiConfig
}

computeCacheKey = (_: IMaterial) => this.enabled ? '1' : '0'

isCompatible(_: IMaterial): boolean {
return true // (material as MeshStandardMaterial2).isMeshStandardMaterial2
}

setDirty() {
this.__setDirty?.() // this will update version which will set needsUpdate on material
this._viewer?.renderManager.screenPass.setDirty()
}

fromJSON(data: any, meta?: any): this | null | Promise<this | null> {
// really old legacy
if (data.pass) {
data = {...data}
data.extension = {...data.pass}
delete data.extension.enabled
delete data.pass
}
// legacy
if (data.extension) {
data = {...data, ...data.extension}
delete data.extension
}
return super.fromJSON(data, meta)
}

onAdded(viewer: ThreeViewer) {
super.onAdded(viewer)
// viewer.getPlugin(GBufferPlugin)?.registerGBufferUpdater(this.updateGBufferFlags) // todo
viewer.renderManager.screenPass.material.registerMaterialExtensions([this])
}

onRemove(viewer: ThreeViewer) {
// viewer.getPlugin(GBufferPlugin)?.unregisterGBufferUpdater(this.updateGBufferFlags)
viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this])
super.onRemove(viewer)
}

// updateGBufferFlags(material: IMaterial, data: Vector4): void {
// const x = material?.userData.postTonemap === false ? 0 : 1
// data.w = updateBit(data.w, 1, x) // 2nd Bit
// }

// for typescript
// eslint-disable-next-line @typescript-eslint/naming-convention
__setDirty?: () => void

updateGBufferFlags(_: IMaterial, _1: Vector4): void {
return
}

}

+ 22
- 87
src/plugins/postprocessing/TonemapPlugin.ts Dosyayı Görüntüle

@@ -1,7 +1,4 @@
// noinspection ES6PreferShortImport
import {AViewerPluginSync} from '../../viewer/AViewerPlugin'
import type {ThreeViewer} from '../../viewer'
import {MaterialExtension} from '../../materials'
import {uiDropdown, uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js'
import {
ACESFilmicToneMapping,
@@ -10,7 +7,6 @@ import {
LinearToneMapping,
Object3D,
ReinhardToneMapping,
Shader,
ShaderChunk,
ToneMapping,
Vector4,
@@ -18,16 +14,12 @@ import {
} from 'three'
import {glsl, onChange, serialize} from 'ts-browser-helpers'
import {IMaterial} from '../../core'
import {shaderReplaceString, updateBit} from '../../utils'
import {updateBit} from '../../utils'
import {matDefine, uniform} from '../../three'
import Uncharted2ToneMapping from './shaders/Uncharted2ToneMapping.glsl'
import Uncharted2ToneMappingShader from './shaders/Uncharted2ToneMapping.glsl'
import TonemapShader from './shaders/TonemapPlugin.pars.glsl'
import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl'

// todo move
export interface GBufferUpdater {
updateGBufferFlags: (material: IMaterial, data: Vector4) => void
}
import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin'

// eslint-disable-next-line @typescript-eslint/naming-convention
export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping
@@ -42,9 +34,18 @@ export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping
* @category Plugins
*/
@uiFolderContainer('Tonemapping')
export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExtension, GBufferUpdater {
export class TonemapPlugin extends AScreenPassExtensionPlugin<''> {
static readonly PluginType = 'Tonemap'

readonly extraUniforms = {
toneMappingContrast: {value: 1},
toneMappingSaturation: {value: 1},
} as const

readonly extraDefines = {
['TONEMAP_BACKGROUND']: '1',
} as const

@serialize() @uiToggle('Enabled') enabled = true

@uiDropdown('Mode', ([
@@ -57,7 +58,6 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte
label: value[0],
value: value[1],
})))

@onChange(TonemapPlugin.prototype.setDirty)
@serialize() toneMapping: ToneMapping = ACESFilmicToneMapping

@@ -82,16 +82,13 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte
@uniform({propKey: 'toneMappingContrast'})
@serialize() contrast: number

readonly extraUniforms = {
toneMappingContrast: {value: 1},
toneMappingSaturation: {value: 1},
} as const

set uniformsNeedUpdate(v: boolean) { // for @uniform decorator
if (v) this.setDirty()
}
/**
* The priority of the material extension when applied to the material in ScreenPass
* set to very low priority, so applied at the end
*/
priority = -100

parsFragmentSnippet: any = (_: WebGLRenderer, _1: IMaterial) => {
parsFragmentSnippet = () => {
if (!this.enabled) return ''

return glsl`
@@ -101,30 +98,7 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte
`
}

constructor() {
super()
this.setDirty = this.setDirty.bind(this)
}

/**
* The priority of the material extension when applied to the material in ScreenPass
* set to very low priority, so applied at the end
*/
readonly priority = -100

shaderExtender(shader: Shader, _: IMaterial, _1: WebGLRenderer): void {
if (!this.enabled) return

shader.fragmentShader = shaderReplaceString(
shader.fragmentShader,
'#glMarker', '\n' + TonemapShaderPatch + '\n',
{prepend: true}
)
}

readonly extraDefines = {
['TONEMAP_BACKGROUND']: '1',
} as const
protected _shaderPatch = TonemapShaderPatch

private _rendererState: any = {}

@@ -145,33 +119,9 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte
renderer.toneMappingExposure = this._rendererState.toneMappingExposure
}

getUiConfig(): any {
return this.uiConfig
}

computeCacheKey = (_: IMaterial) => this.enabled ? '1' : '0'

isCompatible(_: IMaterial): boolean {
return true // (material as MeshStandardMaterial2).isMeshStandardMaterial2
}

setDirty() {
this.__setDirty?.() // this will update version which will set needsUpdate on material
this._viewer?.renderManager.screenPass.setDirty()
}

fromJSON(data: any, meta?: any): this|null|Promise<this|null> {
// really pld legacy
if (data.pass) {
data = {...data}
data.extension = {...data.pass}
delete data.extension.enabled
delete data.pass
}
// legacy
if (data.extension) {
data = {...data, ...data.extension}
delete data.extension
if (data.clipBackground !== undefined) {
if (this._viewer) this._viewer.renderManager.screenPass.clipBackground = data.clipBackground
else console.warn('TonemapPlugin: no viewer attached, clipBackground ignored')
@@ -181,30 +131,15 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte
return super.fromJSON(data, meta)
}

onAdded(viewer: ThreeViewer) {
super.onAdded(viewer)
// viewer.getPlugin(GBufferPlugin)?.registerGBufferUpdater(this.updateGBufferFlags) // todo
viewer.renderManager.screenPass.material.registerMaterialExtensions([this])
}

onRemove(viewer: ThreeViewer) {
// viewer.getPlugin(GBufferPlugin)?.unregisterGBufferUpdater(this.updateGBufferFlags)
viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this])
super.onRemove(viewer)
}

updateGBufferFlags(material: IMaterial, data: Vector4): void {
const x = material?.userData.postTonemap === false ? 0 : 1
data.w = updateBit(data.w, 1, x) // 2nd Bit
super.updateGBufferFlags(material, data)
}

static {
// Add support for Uncharted2 tone mapping
ShaderChunk.tonemapping_pars_fragment = ShaderChunk.tonemapping_pars_fragment.replace('vec3 CustomToneMapping( vec3 color ) { return color; }', Uncharted2ToneMapping)
ShaderChunk.tonemapping_pars_fragment = ShaderChunk.tonemapping_pars_fragment.replace('vec3 CustomToneMapping( vec3 color ) { return color; }', Uncharted2ToneMappingShader)
}

// for typescript
// eslint-disable-next-line @typescript-eslint/naming-convention
__setDirty?: () => void

}

+ 65
- 0
src/plugins/postprocessing/VignettePlugin.ts Dosyayı Görüntüle

@@ -0,0 +1,65 @@
import {uiColor, uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js'
import {Color} from 'three'
import {glsl, onChange, serialize} from 'ts-browser-helpers'
import {uniform} from '../../three'
import vignette from './shaders/VignettePlugin.glsl'
import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin'

/**
* Vignette Plugin
*
* Adds an extension to {@link ScreenPass} material
* for applying vignette effect on the final buffer before rendering to screen.
* The power of the vignette can be controlled with the `power` property.
* The color of the vignette can be controlled with the `color`(previously `bgcolor`) property.
*
* @category Plugins
*/
@uiFolderContainer('Vignette')
export class VignettePlugin extends AScreenPassExtensionPlugin<''> {
static readonly PluginType = 'Vignette'

readonly extraUniforms = {
power: {value: 1},
bgcolor: {value: new Color()},
} as const

@onChange(VignettePlugin.prototype.setDirty)
@uiToggle('Enable')
@serialize() enabled = false

@uiSlider('Power', [0.1, 4], 0.01)
@uniform({propKey: 'power'})
@serialize() power = 0.5

@uiColor<VignettePlugin>('Color', t=>({onChange:()=>t?.setDirty()}))
@uniform({propKey: 'bgcolor'})
@serialize('bgcolor') color = new Color(0x000000)

/**
* The priority of the material extension when applied to the material in ScreenPass
* set to very low priority, so applied at the end
*/
priority = -50

parsFragmentSnippet = () => {
if (!this.enabled) return ''

return glsl`
uniform float power;
uniform vec3 bgcolor;
${vignette}
`
}

protected _shaderPatch = 'diffuseColor = Vignette(diffuseColor);'

get bgcolor() {
console.warn('VignettePlugin.bgcolor is deprecated, use VignettePlugin.color instead')
return this.color
}
set bgcolor(v) {
console.warn('VignettePlugin.bgcolor is deprecated, use VignettePlugin.color instead')
this.color = v
}
}

+ 6
- 0
src/plugins/postprocessing/shaders/VignettePlugin.glsl Dosyayı Görüntüle

@@ -0,0 +1,6 @@
vec4 Vignette(in vec4 color) {
vec2 uv = vUv * (1.0 - vUv);
float vig = uv.x * uv.y * 16.0; // max value of this function is 1/16 at the centre(0.5, 0.5)
vig = pow(vig, power);
return vec4( mix( color.rgb, vec3( bgcolor ), 1. - vig ), color.a );
}

Loading…
İptal
Kaydet