threepipe
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

TonemapPlugin.ts 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // noinspection ES6PreferShortImport
  2. import {uiDropdown, uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js'
  3. import {
  4. ACESFilmicToneMapping,
  5. CineonToneMapping,
  6. CustomToneMapping,
  7. LinearToneMapping,
  8. Object3D,
  9. ReinhardToneMapping,
  10. ShaderChunk,
  11. ToneMapping,
  12. Vector4,
  13. WebGLRenderer,
  14. } from 'three'
  15. import {glsl, onChange, serialize} from 'ts-browser-helpers'
  16. import {IMaterial} from '../../core'
  17. import {updateBit} from '../../utils'
  18. import {uniform} from '../../three'
  19. import Uncharted2ToneMappingShader from './shaders/Uncharted2ToneMapping.glsl'
  20. import TonemapShader from './shaders/TonemapPlugin.pars.glsl'
  21. import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl'
  22. import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin'
  23. import {GBufferUpdaterContext} from '../pipeline/GBufferPlugin'
  24. import {matDefineBool} from '../../three/utils/decorators'
  25. // eslint-disable-next-line @typescript-eslint/naming-convention
  26. export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping
  27. /**
  28. * Tonemap Plugin
  29. *
  30. * Adds an extension to {@link ScreenPass} material
  31. * for applying tonemapping on the final buffer before rendering to screen.
  32. *
  33. * Also adds support for Uncharted2 tone-mapping.
  34. * @category Plugins
  35. */
  36. @uiFolderContainer('Tonemapping')
  37. export class TonemapPlugin extends AScreenPassExtensionPlugin<''> {
  38. static readonly PluginType = 'Tonemap'
  39. readonly extraUniforms = {
  40. toneMappingContrast: {value: 1},
  41. toneMappingSaturation: {value: 1},
  42. } as const
  43. readonly extraDefines = {
  44. ['TONEMAP_BACKGROUND']: '1',
  45. } as const
  46. @serialize() @uiToggle('Enabled') enabled = true
  47. @uiDropdown('Mode', ([
  48. ['Linear', LinearToneMapping],
  49. ['Reinhard', ReinhardToneMapping],
  50. ['Cineon', CineonToneMapping],
  51. ['ACESFilmic', ACESFilmicToneMapping],
  52. ['Uncharted2', Uncharted2Tonemapping],
  53. ] as [string, ToneMapping][]).map(value => ({
  54. label: value[0],
  55. value: value[1],
  56. })))
  57. @onChange(TonemapPlugin.prototype.setDirty)
  58. @serialize() toneMapping: ToneMapping = ACESFilmicToneMapping
  59. @uiToggle('Tonemap Background', (t: TonemapPlugin)=>({hidden: ()=>!t._viewer?.renderManager.gbufferTarget}))
  60. @matDefineBool('TONEMAP_BACKGROUND', undefined, true, TonemapPlugin.prototype.setDirty)
  61. @serialize() tonemapBackground = true
  62. // todo handle legacy deserialize
  63. // @onChange(TonemapPlugin.prototype.setDirty)
  64. // @uiToggle('Clip Background')
  65. // @serialize() clipBackground = false
  66. @onChange(TonemapPlugin.prototype.setDirty)
  67. @uiSlider('Exposure', [0, 2 * Math.PI], 0.01)
  68. @serialize() exposure = 1
  69. @uiSlider('Saturation', [0, 2], 0.01)
  70. @uniform({propKey: 'toneMappingSaturation'})
  71. @serialize() saturation: number
  72. @uiSlider('Contrast', [0, 2], 0.01)
  73. @uniform({propKey: 'toneMappingContrast'})
  74. @serialize() contrast: number
  75. /**
  76. * The priority of the material extension when applied to the material in ScreenPass
  77. * set to very low priority, so applied at the end
  78. */
  79. priority = -100
  80. parsFragmentSnippet = () => {
  81. if (this.isDisabled()) return ''
  82. return glsl`
  83. uniform float toneMappingContrast;
  84. uniform float toneMappingSaturation;
  85. ${TonemapShader}
  86. `
  87. }
  88. protected _shaderPatch = TonemapShaderPatch
  89. private _rendererState: any = {}
  90. onObjectRender(_: Object3D, material: IMaterial, renderer: WebGLRenderer): void {
  91. if (this.isDisabled()) return
  92. const {toneMapping, toneMappingExposure} = renderer
  93. this._rendererState.toneMapping = toneMapping
  94. this._rendererState.toneMappingExposure = toneMappingExposure
  95. renderer.toneMapping = this.toneMapping
  96. renderer.toneMappingExposure = this.exposure
  97. material.toneMapped = true
  98. material.needsUpdate = true
  99. }
  100. onAfterRender(_: Object3D, _1: IMaterial, renderer: WebGLRenderer): void {
  101. renderer.toneMapping = this._rendererState.toneMapping
  102. renderer.toneMappingExposure = this._rendererState.toneMappingExposure
  103. }
  104. fromJSON(data: any, meta?: any): this|null|Promise<this|null> {
  105. // legacy
  106. if (data.extension) {
  107. if (data.clipBackground !== undefined) {
  108. if (this._viewer) this._viewer.renderManager.screenPass.clipBackground = data.clipBackground
  109. else console.warn('TonemapPlugin: no viewer attached, clipBackground ignored')
  110. delete data.clipBackground
  111. }
  112. }
  113. return super.fromJSON(data, meta)
  114. }
  115. // TODO: add gBufferData or just tonemapEnabled to the scene material UI with an extension like bloom
  116. updateGBufferFlags(data: Vector4, c: GBufferUpdaterContext): void {
  117. const x = (c.material.userData.gBufferData?.tonemapEnabled ?? c.material?.userData.postTonemap) === false ? 0 : 1
  118. data.w = updateBit(data.w, 1, x) // 2nd Bit
  119. super.updateGBufferFlags(data, c)
  120. }
  121. static {
  122. // Add support for Uncharted2 tone mapping
  123. ShaderChunk.tonemapping_pars_fragment = ShaderChunk.tonemapping_pars_fragment.replace('vec3 CustomToneMapping( vec3 color ) { return color; }', Uncharted2ToneMappingShader)
  124. }
  125. }