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.

157 lines (133 loc) 5.54 kB
// noinspection ES6PreferShortImport import {uiDropdown, uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js' import { ACESFilmicToneMapping, AgXToneMapping, CineonToneMapping, CustomToneMapping, LinearToneMapping, Object3D, ReinhardToneMapping, ShaderChunk, ToneMapping, Vector4, WebGLRenderer, } from 'three' import {glsl, onChange, serialize} from 'ts-browser-helpers' import {IMaterial} from '../../core' import {updateBit} from '../../utils' import {uniform} from '../../three' import Uncharted2ToneMappingShader from './shaders/Uncharted2ToneMapping.glsl' import TonemapShader from './shaders/TonemapPlugin.pars.glsl' import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl' import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin' import {GBufferUpdaterContext} from '../pipeline/GBufferMaterial' // eslint-disable-next-line @typescript-eslint/naming-convention export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping /** * Tonemap Plugin * * Adds an extension to {@link ScreenPass} material * for applying tonemapping on the final buffer before rendering to screen. * * Also adds support for Uncharted2 tone-mapping. * @category Plugins */ @uiFolderContainer('Tonemapping') export class TonemapPlugin extends AScreenPassExtensionPlugin { static readonly PluginType = 'Tonemap' readonly extraUniforms = { toneMappingContrast: {value: 1}, toneMappingSaturation: {value: 1}, } as const readonly extraDefines = { ['TONEMAP_BACKGROUND']: ()=>this.tonemapBackground === false || this._viewer?.scene.backgroundTonemap === false ? '0' : '1', } as const @serialize() @onChange(TonemapPlugin.prototype.setDirty) @uiToggle('Enabled') enabled = true @uiDropdown('Mode', ([ ['Linear', LinearToneMapping], ['Reinhard', ReinhardToneMapping], ['Cineon', CineonToneMapping], ['ACESFilmic', ACESFilmicToneMapping], ['Uncharted2', Uncharted2Tonemapping], ['AgX', AgXToneMapping], ] as [string, ToneMapping][]).map(value => ({ label: value[0], value: value[1], }))) @onChange(TonemapPlugin.prototype.setDirty) @serialize() toneMapping: ToneMapping = ACESFilmicToneMapping /** * Global toggle to apply tonemapping on the background. * The tonemapping is not applied if either this or {@link RootScene.backgroundTonemap} is false. */ @onChange(TonemapPlugin.prototype.setDirty) @serialize() tonemapBackground = true // todo handle legacy deserialize // moved to ScreenPass.clipBackground // @onChange(TonemapPlugin.prototype.setDirty) // @uiToggle('Clip Background') // @serialize() clipBackground = false @onChange(TonemapPlugin.prototype.setDirty) @uiSlider('Exposure', [0, 2 * Math.PI], 0.01) @serialize() exposure = 1 @uiSlider('Saturation', [0, 2], 0.01) @uniform({propKey: 'toneMappingSaturation'}) @serialize() saturation: number @uiSlider('Contrast', [0, 2], 0.01) @uniform({propKey: 'toneMappingContrast'}) @serialize() contrast: number /** * 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 = () => { if (this.isDisabled()) return '' return glsl` uniform float toneMappingContrast; uniform float toneMappingSaturation; ${TonemapShader} ` } protected _shaderPatch = TonemapShaderPatch private _rendererState: any = {} onObjectRender(_: Object3D, material: IMaterial, renderer: WebGLRenderer): void { if (this.isDisabled()) return const {toneMapping, toneMappingExposure} = renderer this._rendererState.toneMapping = toneMapping this._rendererState.toneMappingExposure = toneMappingExposure renderer.toneMapping = this.toneMapping renderer.toneMappingExposure = this.exposure material.toneMapped = true material.needsUpdate = true } onAfterRender(_: Object3D, _1: IMaterial, renderer: WebGLRenderer): void { renderer.toneMapping = this._rendererState.toneMapping renderer.toneMappingExposure = this._rendererState.toneMappingExposure } fromJSON(data: any, meta?: any): this|null|Promise<this|null> { // legacy if (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') delete data.clipBackground } } return super.fromJSON(data, meta) } // TODO: add gBufferData or just tonemapEnabled to the scene material UI with an extension like bloom updateGBufferFlags(data: Vector4, c: GBufferUpdaterContext): void { const x = (c.material.userData.gBufferData?.tonemapEnabled ?? c.material?.userData.postTonemap) === false ? 0 : 1 data.w = updateBit(data.w, 1, x) // 2nd Bit super.updateGBufferFlags(data, c) } static { // Add support for Uncharted2 tone mapping ShaderChunk.tonemapping_pars_fragment = ShaderChunk.tonemapping_pars_fragment.replace('vec3 CustomToneMapping( vec3 color ) { return color; }', Uncharted2ToneMappingShader) } }