From 5211bdff229f1e21804ca7b02fd792989f48d724 Mon Sep 17 00:00:00 2001 From: MbfloydIR <144718558+MbfloydIR@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:20:22 -0700 Subject: [PATCH] Michael/ir 1983 registry - Post Processing and Highlight (#10313) * added color average effect * added the outline effect to the post processing * added smaa effect for post processing * added ChromaticAberrationEffect to post processing * added DotScreenEffect post processing effect * added TiltShiftEffect post processing effect * Added motion blur post processing effect * added Glitch Effect to post processing * added lineWidth GodRaysEffect and GridEffect to post processing * added LUT1DEffect to post processing * added LUT3DEffect post processing effect * added LUT1DEffect post processing effect * added NoiseEffect to post processing * added the PixelationEffect to post processing * added ScanlineEffect to post processing effects * added FXAAEffect and ShockWaveEffect to post processing * added TextureEffect to post processing effects * added LensDistortionEffect to post processing * corrected error with duplicate case option * moved the effects composer code into the post processing component so we can use texture loading hooks * committing latest progress for the post processing effects * updated the post processing editor to use the lut path * example * moved the effect logic into individual use effects * added effect options conditional check * removed the associative array map to the effects class since it is not being used in a for loop any longer * connected the post processing effects up to the individual useEffect * removed the effectMap refs that are no longer needed * updated default values for post processing effects commented out SSGIEffect since it required the composure before it is available * created child reactor to wait for the useScene to return a render Entity * moving composure declaration so it can be used in an effect constructor for SSREffect * alphabetized the effects in the editor * added missing SSGIEffect ref * fix render pass * added highlights to editor items that are selected * Added highlight system to add highlights to selected objects * implemented the outline effect for the highlight selection implemented the smaa default effect removed the outline and smaa effects from the post processing editor options since they are always on * updated the outline effect ref * put the highlight query map ref back to correct typescript issue * initial attempt at unity test * updated the unity test to loop through the effects of the post processing and set its various properties using a master data obj * Move postprocessing * added unit test for the effect composer * Updated the composure ref to use a factory function for the initial state of this reactor, so it stops creating a new composer and render pass every time the renderer reactor re-runs * removed the query in the body of a reactor removal of the highlight component, is now down by returning a cleanup function in the use effect * updated highlight state and play mode connection to rely on the data of the selection state * removed obsolete commented out code * updated the post processesing component to not have a use effect for each effect * corrected effect schema issue * removed dev logs * updated the post processing to avoid adding extra passes * removed dev logs * removed unwanted entry from the tsconfig * corrected lint issues * removed configure effect composure since it is no longer used * updated to fix lint errors * corrected the lint error * Add SMAAEffect to default * I fixed badly * Implement registry method from @josh * separated effect reactor to be it's own file created file to populate effects registry Added bloom effect * added Brightness Contrast Effect * added color average effect * added color depth effect * added depth of field effect added dot screen effect added fxaa effect added Glitch effect * added grid effect added hue saturation effect * added LUT1D Effect added LUT3D Effect added Lens Distortion Effect added Linear tos RGB Effect added Motion Blur Effect added Noise Effect added Outline Effect added Pixelation Effect added Scan line Effect * removed effects that are completed from list * added smaa, ssao, ssr, and shockwave effects * added texture effect restructured the initialization of the effects population * added effects: TRAA, Tilt Shift, Tone mapping, Vignette * corrected the LUT1d and LUT 3d effects * passed down the scene and composer refs for the effects that needed them * re added the highlight /outline effect on selected items * updated the unit test for post processing * removed premature return so the highlight effect will work * add postprocessing to spatial deps * corrected lint errors * corrected error caught in npm check errors * addressed PR change requests * corrected missing ref type error * corrected type issue --------- Co-authored-by: Josh Field Co-authored-by: Michael Estes Co-authored-by: Daniel Belmes Co-authored-by: Daniel Belmes <3631206+DanielBelmes@users.noreply.github.com> --- packages/ecs/src/ComponentFunctions.ts | 2 +- packages/editor/package.json | 1 + packages/editor/src/EditorModule.ts | 2 + .../PostProcessingSettingsEditor.tsx | 269 ++----- .../components/toolbar/tools/PlayModeTool.tsx | 4 + .../src/functions/EditorControlFunctions.ts | 2 +- .../editor/src/systems/HighlightSystem.ts | 53 ++ packages/engine/src/EngineModule.ts | 1 + .../engine/src/postprocessing/BloomEffect.tsx | 114 +++ .../BrightnessContrastEffect.tsx | 91 +++ .../ChromaticAberrationEffect.tsx | 93 +++ .../src/postprocessing/ColorAverageEffect.tsx | 88 +++ .../src/postprocessing/ColorDepthEffect.tsx | 90 +++ .../src/postprocessing/DepthOfFieldEffect.tsx | 103 +++ .../src/postprocessing/DotScreenEffect.tsx | 92 +++ .../engine/src/postprocessing/FXAAEffect.tsx | 88 +++ .../src/postprocessing/GlitchEffect.tsx | 105 +++ .../engine/src/postprocessing/GridEffect.tsx | 92 +++ .../postprocessing/HueSaturationEffect.tsx | 92 +++ .../engine/src/postprocessing/LUT1DEffect.tsx | 97 +++ .../engine/src/postprocessing/LUT3DEffect.tsx | 97 +++ .../postprocessing/LensDistortionEffect.tsx | 95 +++ .../src/postprocessing/LinearTosRGBEffect.tsx | 85 +++ .../src/postprocessing/MotionBlurEffect.tsx | 98 +++ .../engine/src/postprocessing/NoiseEffect.tsx | 90 +++ .../src/postprocessing/OutlineEffect.tsx | 110 +++ .../src/postprocessing/PixelationEffect.tsx | 88 +++ .../postprocessing/PopulateEffectRegistry.tsx | 98 +++ .../postprocessing/PostProcessingRegister.tsx | 40 + .../engine/src/postprocessing/SMAAEffect.tsx | 84 ++ .../engine/src/postprocessing/SSAOEffect.tsx | 116 +++ .../engine/src/postprocessing/SSGIEffect.tsx | 131 ++++ .../engine/src/postprocessing/SSREffect.tsx | 141 ++++ .../src/postprocessing/ScanlineEffect.tsx | 92 +++ .../src/postprocessing/ShockWaveEffect.tsx | 97 +++ .../engine/src/postprocessing/TRAAEffect.tsx | 112 +++ .../src/postprocessing/TextureEffect.tsx | 100 +++ .../src/postprocessing/TiltShiftEffect.tsx | 106 +++ .../src/postprocessing/ToneMappingEffect.tsx | 113 +++ .../src/postprocessing/VignetteEffect.tsx | 101 +++ .../passes/CustomNormalPass.js | 2 +- packages/spatial/package.json | 5 +- .../PostProcessingComponent.test.tsx | 201 +++++ .../components/PostProcessingComponent.tsx | 138 +++- .../src/renderer/effects/EffectRegistry.ts | 66 ++ .../src/renderer/effects/PostProcessing.ts | 716 ------------------ .../functions/configureEffectComposer.ts | 170 ----- 47 files changed, 3672 insertions(+), 1099 deletions(-) create mode 100644 packages/editor/src/systems/HighlightSystem.ts create mode 100644 packages/engine/src/postprocessing/BloomEffect.tsx create mode 100644 packages/engine/src/postprocessing/BrightnessContrastEffect.tsx create mode 100644 packages/engine/src/postprocessing/ChromaticAberrationEffect.tsx create mode 100644 packages/engine/src/postprocessing/ColorAverageEffect.tsx create mode 100644 packages/engine/src/postprocessing/ColorDepthEffect.tsx create mode 100644 packages/engine/src/postprocessing/DepthOfFieldEffect.tsx create mode 100644 packages/engine/src/postprocessing/DotScreenEffect.tsx create mode 100644 packages/engine/src/postprocessing/FXAAEffect.tsx create mode 100644 packages/engine/src/postprocessing/GlitchEffect.tsx create mode 100644 packages/engine/src/postprocessing/GridEffect.tsx create mode 100644 packages/engine/src/postprocessing/HueSaturationEffect.tsx create mode 100644 packages/engine/src/postprocessing/LUT1DEffect.tsx create mode 100644 packages/engine/src/postprocessing/LUT3DEffect.tsx create mode 100644 packages/engine/src/postprocessing/LensDistortionEffect.tsx create mode 100644 packages/engine/src/postprocessing/LinearTosRGBEffect.tsx create mode 100644 packages/engine/src/postprocessing/MotionBlurEffect.tsx create mode 100644 packages/engine/src/postprocessing/NoiseEffect.tsx create mode 100644 packages/engine/src/postprocessing/OutlineEffect.tsx create mode 100644 packages/engine/src/postprocessing/PixelationEffect.tsx create mode 100644 packages/engine/src/postprocessing/PopulateEffectRegistry.tsx create mode 100644 packages/engine/src/postprocessing/PostProcessingRegister.tsx create mode 100644 packages/engine/src/postprocessing/SMAAEffect.tsx create mode 100644 packages/engine/src/postprocessing/SSAOEffect.tsx create mode 100644 packages/engine/src/postprocessing/SSGIEffect.tsx create mode 100644 packages/engine/src/postprocessing/SSREffect.tsx create mode 100644 packages/engine/src/postprocessing/ScanlineEffect.tsx create mode 100644 packages/engine/src/postprocessing/ShockWaveEffect.tsx create mode 100644 packages/engine/src/postprocessing/TRAAEffect.tsx create mode 100644 packages/engine/src/postprocessing/TextureEffect.tsx create mode 100644 packages/engine/src/postprocessing/TiltShiftEffect.tsx create mode 100644 packages/engine/src/postprocessing/ToneMappingEffect.tsx create mode 100644 packages/engine/src/postprocessing/VignetteEffect.tsx rename packages/{spatial/src/renderer => engine/src/postprocessing}/passes/CustomNormalPass.js (98%) create mode 100644 packages/spatial/src/renderer/components/PostProcessingComponent.test.tsx mode change 100755 => 100644 packages/spatial/src/renderer/components/PostProcessingComponent.tsx create mode 100644 packages/spatial/src/renderer/effects/EffectRegistry.ts delete mode 100644 packages/spatial/src/renderer/effects/PostProcessing.ts delete mode 100644 packages/spatial/src/renderer/functions/configureEffectComposer.ts diff --git a/packages/ecs/src/ComponentFunctions.ts b/packages/ecs/src/ComponentFunctions.ts index e5dd5ad9fd..7589e73441 100755 --- a/packages/ecs/src/ComponentFunctions.ts +++ b/packages/ecs/src/ComponentFunctions.ts @@ -132,7 +132,7 @@ export interface ComponentPartial< * `@todo` Explain what reactive is in this context * `@todo` Explain this function */ - reactor?: React.FC + reactor?: any // previously breaks types /** * @todo Explain ComponentPartial.errors[] */ diff --git a/packages/editor/package.json b/packages/editor/package.json index 65efc2db67..2931b30281 100755 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -43,6 +43,7 @@ "i18next": "21.6.16", "lodash": "4.17.21", "potpack": "^2.0.0", + "postprocessing": "6.34.1", "rc-dock": "3.2.18", "rc-slider": "10.5.0", "react": "18.2.0", diff --git a/packages/editor/src/EditorModule.ts b/packages/editor/src/EditorModule.ts index 133e04e108..0e167ba31b 100644 --- a/packages/editor/src/EditorModule.ts +++ b/packages/editor/src/EditorModule.ts @@ -28,6 +28,7 @@ import { RenderInfoSystem } from '@etherealengine/spatial/src/renderer/RenderInf import { EditorInstanceNetworkingSystem } from './components/realtime/EditorInstanceNetworkingSystem' import { EditorControlSystem } from './systems/EditorControlSystem' import { GizmoSystem } from './systems/GizmoSystem' +import { HighlightSystem } from './systems/HighlightSystem' import { ModelHandlingSystem } from './systems/ModelHandlingSystem' import { ObjectGridSnapSystem } from './systems/ObjectGridSnapSystem' import { UploadRequestSystem } from './systems/UploadRequestSystem' @@ -36,6 +37,7 @@ export { EditorInstanceNetworkingSystem, EditorControlSystem, GizmoSystem, + HighlightSystem, ModelHandlingSystem, ObjectGridSnapSystem, UploadRequestSystem, diff --git a/packages/editor/src/components/properties/PostProcessingSettingsEditor.tsx b/packages/editor/src/components/properties/PostProcessingSettingsEditor.tsx index 318f8b8a70..ca1cf45b5a 100755 --- a/packages/editor/src/components/properties/PostProcessingSettingsEditor.tsx +++ b/packages/editor/src/components/properties/PostProcessingSettingsEditor.tsx @@ -23,26 +23,30 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ +import { BlendFunction, SMAAPreset, VignetteTechnique } from 'postprocessing' +import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Color, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, LinearSRGBColorSpace, SRGBColorSpace } from 'three' + +import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' import Checkbox from '@mui/material/Checkbox' import Collapse from '@mui/material/Collapse' import IconButton from '@mui/material/IconButton' -import { BlendFunction, VignetteTechnique } from 'postprocessing' -import React, { useState } from 'react' -import { useTranslation } from 'react-i18next' -import { Color } from 'three' -import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { getState } from '@etherealengine/hyperflux' import { PostProcessingComponent } from '@etherealengine/spatial/src/renderer/components/PostProcessingComponent' -import { Effects } from '@etherealengine/spatial/src/renderer/effects/PostProcessing' - +import { PostProcessingEffectState } from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' import BooleanInput from '../inputs/BooleanInput' import ColorInput from '../inputs/ColorInput' import CompoundNumericInput from '../inputs/CompoundNumericInput' import InputGroup from '../inputs/InputGroup' import SelectInput from '../inputs/SelectInput' +import TexturePreviewInput from '../inputs/TexturePreviewInput' +import Vector2Input from '../inputs/Vector2Input' +import Vector3Input from '../inputs/Vector3Input' import styles from '../styles.module.scss' import PropertyGroup from './PropertyGroup' import { commitProperties, commitProperty, EditorComponentType, updateProperty } from './Util' @@ -52,159 +56,20 @@ enum PropertyTypes { Number, Boolean, Color, + ColorSpace, KernelSize, SMAAPreset, EdgeDetectionMode, PredicationMode, + Texture, + Vector2, + Vector3, VignetteTechnique } -type EffectPropertyDetail = { propertyType: PropertyTypes; name: string; min?: number; max?: number; step?: number } -type EffectPropertiesType = { [key: string]: EffectPropertyDetail } -type EffectOptionsType = { [key in keyof typeof Effects]: EffectPropertiesType } - -const EffectsOptions: Partial = { - SSAOEffect: { - blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, - distanceScaling: { propertyType: PropertyTypes.Boolean, name: 'Distance Scaling' }, - depthAwareUpsampling: { propertyType: PropertyTypes.Boolean, name: 'Depth Aware Upsampling' }, - samples: { propertyType: PropertyTypes.Number, name: 'Samples', min: 1, max: 32, step: 1 }, - rings: { propertyType: PropertyTypes.Number, name: 'Rings', min: -1, max: 1, step: 0.01 }, - - rangeThreshold: { propertyType: PropertyTypes.Number, name: 'Range Threshold', min: -1, max: 1, step: 0.0001 }, // Occlusion proximity of ~0.3 world units - rangeFalloff: { propertyType: PropertyTypes.Number, name: 'Range Falloff', min: -1, max: 1, step: 0.0001 }, // with ~0.1 units of falloff. - // Render up to a distance of ~20 world units. - distanceThreshold: { propertyType: PropertyTypes.Number, name: 'Distance Threshold', min: -1, max: 1, step: 0.01 }, - luminanceInfluence: { - propertyType: PropertyTypes.Number, - name: 'Luminance Influence', - min: -1, - max: 1, - step: 0.01 - }, - // with an additional ~2.5 units of falloff. - distanceFalloff: { propertyType: PropertyTypes.Number, name: 'Distance Falloff', min: -1, max: 1, step: 0.001 }, - minRadiusScale: { propertyType: PropertyTypes.Number, name: 'Min Radius Scale', min: -1, max: 1, step: 0.01 }, - bias: { propertyType: PropertyTypes.Number, name: 'Bias', min: -1, max: 1, step: 0.01 }, - radius: { propertyType: PropertyTypes.Number, name: 'Radius', min: -1, max: 1, step: 0.01 }, - intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: 0, max: 10, step: 0.01 }, - fade: { propertyType: PropertyTypes.Number, name: 'Fade', min: -1, max: 1, step: 0.01 }, - resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: -10, max: 10, step: 0.01 }, - kernelSize: { propertyType: PropertyTypes.Number, name: 'Kerne Size', min: 1, max: 5, step: 1 }, - blur: { propertyType: PropertyTypes.Boolean, name: 'Blur' } - }, - SSREffect: { - distance: { propertyType: PropertyTypes.Number, name: 'Distance', min: 0.001, max: 10, step: 0.01 }, - thickness: { propertyType: PropertyTypes.Number, name: 'Thickness', min: 0, max: 5, step: 0.01 }, - denoiseIterations: { propertyType: PropertyTypes.Number, name: 'Denoise Iterations', min: 0, max: 5, step: 1 }, - denoiseKernel: { propertyType: PropertyTypes.Number, name: 'Denoise Kernel', min: 1, max: 5, step: 1 }, - denoiseDiffuse: { propertyType: PropertyTypes.Number, name: 'Denoise Diffuse', min: 0, max: 50, step: 0.01 }, - denoiseSpecular: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, - radius: { propertyType: PropertyTypes.Number, name: 'Radius', min: 0, max: 50, step: 0.01 }, - phi: { propertyType: PropertyTypes.Number, name: 'Phi', min: 0, max: 50, step: 0.01 }, - lumaPhi: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, - depthPhi: { propertyType: PropertyTypes.Number, name: 'luminosity Phi', min: 0, max: 15, step: 0.001 }, - normalPhi: { propertyType: PropertyTypes.Number, name: 'Normal Phi', min: 0, max: 50, step: 0.001 }, - roughnessPhi: { propertyType: PropertyTypes.Number, name: 'Roughness Phi', min: 0, max: 100, step: 0.001 }, - specularPhi: { propertyType: PropertyTypes.Number, name: 'Specular Phi', min: 0, max: 50, step: 0.01 }, - envBlur: { propertyType: PropertyTypes.Number, name: 'Environment Blur', min: 0, max: 1, step: 0.01 }, - importanceSampling: { propertyType: PropertyTypes.Boolean, name: 'Importance Sampling' }, - steps: { propertyType: PropertyTypes.Number, name: 'Steps', min: 0, max: 256, step: 1 }, - refineSteps: { propertyType: PropertyTypes.Number, name: 'Refine Steps', min: 0, max: 16, step: 1 }, - resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 0.25, max: 1, step: 0.25 }, - missedRays: { propertyType: PropertyTypes.Boolean, name: 'Missed Rays' } - }, - DepthOfFieldEffect: { - blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, - bokehScale: { propertyType: PropertyTypes.Number, name: 'Bokeh Scale', min: -10, max: 10, step: 0.01 }, - focalLength: { propertyType: PropertyTypes.Number, name: 'Focal Length', min: 0, max: 1, step: 0.01 }, - focalRange: { propertyType: PropertyTypes.Number, name: 'Focal Range', min: 0, max: 1, step: 0.01 }, - focusDistance: { propertyType: PropertyTypes.Number, name: 'Focus Distance', min: 0, max: 1, step: 0.01 }, - resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: -10, max: 10, step: 0.01 } - }, - BloomEffect: { - blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, - kernelSize: { propertyType: PropertyTypes.KernelSize, name: 'Kernel Size' }, - intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: 0, max: 10, step: 0.01 }, - luminanceSmoothing: { - propertyType: PropertyTypes.Number, - name: 'Luminance Smoothing', - min: 0, - max: 1, - step: 0.01 - }, - luminanceThreshold: { propertyType: PropertyTypes.Number, name: 'Luminance Threshold', min: 0, max: 1, step: 0.01 }, - mipmapBlur: { propertyType: PropertyTypes.Boolean, name: 'Mipmap Blur' }, - radius: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 0, max: 10, step: 0.01 }, - levels: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 1, max: 10, step: 1 } - }, - ToneMappingEffect: { - blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, - adaptive: { propertyType: PropertyTypes.Boolean, name: 'Adaptive' }, - adaptationRate: { propertyType: PropertyTypes.Number, name: 'Adaptation Rate', min: -1, max: 1, step: 0.01 }, - averageLuminance: { propertyType: PropertyTypes.Number, name: 'Average Luminance', min: -1, max: 1, step: 0.01 }, - maxLuminance: { propertyType: PropertyTypes.Number, name: 'Max Luminance', min: -1, max: 1, step: 0.01 }, - middleGrey: { propertyType: PropertyTypes.Number, name: 'Middle Grey', min: -1, max: 1, step: 0.01 }, - resolution: { propertyType: PropertyTypes.Number, name: 'Resolution' }, - whitePoint: { propertyType: PropertyTypes.Number, name: 'Resolution' }, - minLuminance: { propertyType: PropertyTypes.Number, name: 'Resolution' } - }, - BrightnessContrastEffect: { - brightness: { propertyType: PropertyTypes.Number, name: 'Brightness', min: -1, max: 1, step: 0.01 }, - contrast: { propertyType: PropertyTypes.Number, name: 'Contrast', min: -1, max: 1, step: 0.01 } - }, - HueSaturationEffect: { - hue: { propertyType: PropertyTypes.Number, name: 'Hue', min: -1, max: 1, step: 0.01 }, - saturation: { propertyType: PropertyTypes.Number, name: 'Saturation', min: -1, max: 1, step: 0.01 } - }, - ColorDepthEffect: { - bits: { propertyType: PropertyTypes.Number, name: 'Bits', min: -1, max: 1, step: 0.01 } - }, - LinearTosRGBEffect: {}, - SSGIEffect: { - distance: { propertyType: PropertyTypes.Number, name: 'Distance', min: 0.001, max: 10, step: 0.01 }, - thickness: { propertyType: PropertyTypes.Number, name: 'Thickness', min: 0, max: 5, step: 0.01 }, - denoiseIterations: { propertyType: PropertyTypes.Number, name: 'Denoise Iterations', min: 0, max: 5, step: 1 }, - denoiseKernel: { propertyType: PropertyTypes.Number, name: 'Denoise Kernel', min: 1, max: 5, step: 1 }, - denoiseDiffuse: { propertyType: PropertyTypes.Number, name: 'Denoise Diffuse', min: 0, max: 50, step: 0.01 }, - denoiseSpecular: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, - radius: { propertyType: PropertyTypes.Number, name: 'Radius', min: 0, max: 50, step: 0.01 }, - phi: { propertyType: PropertyTypes.Number, name: 'Phi', min: 0, max: 50, step: 0.01 }, - lumaPhi: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, - depthPhi: { propertyType: PropertyTypes.Number, name: 'luminosity Phi', min: 0, max: 15, step: 0.001 }, - normalPhi: { propertyType: PropertyTypes.Number, name: 'Normal Phi', min: 0, max: 50, step: 0.001 }, - roughnessPhi: { propertyType: PropertyTypes.Number, name: 'Roughness Phi', min: 0, max: 100, step: 0.001 }, - specularPhi: { propertyType: PropertyTypes.Number, name: 'Specular Phi', min: 0, max: 50, step: 0.01 }, - envBlur: { propertyType: PropertyTypes.Number, name: 'Environment Blur', min: 0, max: 1, step: 0.01 }, - importanceSampling: { propertyType: PropertyTypes.Boolean, name: 'Importance Sampling' }, - steps: { propertyType: PropertyTypes.Number, name: 'Steps', min: 0, max: 256, step: 1 }, - refineSteps: { propertyType: PropertyTypes.Number, name: 'Refine Steps', min: 0, max: 16, step: 1 }, - resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 0.25, max: 1, step: 0.25 }, - missedRays: { propertyType: PropertyTypes.Boolean, name: 'Missed Rays' } - }, - TRAAEffect: { - blend: { propertyType: PropertyTypes.Number, name: 'Blend', min: 0, max: 1, step: 0.001 }, - constantBlend: { propertyType: PropertyTypes.Boolean, name: 'Constant Blend' }, - dilation: { propertyType: PropertyTypes.Boolean, name: 'Dilation' }, - blockySampling: { propertyType: PropertyTypes.Boolean, name: 'Blocky Sampling' }, - logTransform: { propertyType: PropertyTypes.Boolean, name: 'Log Transform' }, - depthDistance: { propertyType: PropertyTypes.Number, name: 'Depth Distance', min: 0.01, max: 100, step: 0.01 }, - worldDistance: { propertyType: PropertyTypes.Number, name: 'World Distance', min: 0.01, max: 100, step: 0.01 }, - neighborhoodClamping: { propertyType: PropertyTypes.Boolean, name: 'Neighborhood Clamping' } - }, - MotionBlurEffect: { - intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: 0, max: 10, step: 0.01 }, - jitter: { propertyType: PropertyTypes.Number, name: 'Jitter', min: 0, max: 10, step: 0.01 }, - samples: { propertyType: PropertyTypes.Number, name: 'Samples', min: 1, max: 64, step: 1 } - }, - VignetteEffect: { - blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, - technique: { propertyType: PropertyTypes.VignetteTechnique, name: 'Technique' }, - eskil: { propertyType: PropertyTypes.Boolean, name: 'Eskil' }, - offset: { propertyType: PropertyTypes.Number, name: 'Offset', min: 0, max: 10, step: 0.1 }, - darkness: { propertyType: PropertyTypes.Number, name: 'Darkness', min: 0, max: 10, step: 0.1 } - } -} +const SMAAPresetSelect = Object.entries(SMAAPreset).map(([label, value]) => { + return { label, value } +}) const BlendFunctionSelect = Object.entries(BlendFunction).map(([label, value]) => { return { label, value } @@ -214,6 +79,14 @@ const VignetteTechniqueSelect = Object.entries(VignetteTechnique).map(([label, v return { label, value } }) +const ColorSpaceSelect = [ + { label: 'NONE', value: '' }, + { label: 'SRGB', value: SRGBColorSpace }, + { label: 'SRGB LINEAR', value: LinearSRGBColorSpace }, + { label: 'DISPLAY P3', value: DisplayP3ColorSpace }, + { label: 'DISPLAY P3 LINEAR', value: LinearDisplayP3ColorSpace } +] + const KernelSizeSelect = [ { label: 'VERY_SMALL', value: 0 }, { label: 'SMALL', value: 1 }, @@ -223,13 +96,6 @@ const KernelSizeSelect = [ { label: 'HUGE', value: 5 } ] -const SMAAPreset = [ - { label: 'LOW', value: 0 }, - { label: 'MEDIUM', value: 1 }, - { label: 'HIGH', value: 2 }, - { label: 'ULTRA', value: 3 } -] - const EdgeDetectionMode = [ { label: 'DEPTH', value: 0 }, { label: 'LUMA', value: 1 }, @@ -246,26 +112,23 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { const { t } = useTranslation() const [openSettings, setOpenSettings] = useState(false) + const effects = getState(PostProcessingEffectState) const postprocessing = useComponent(props.entity, PostProcessingComponent) - const renderProperty = ( - propertyDetail: EffectPropertyDetail, - effectName: keyof typeof Effects, - property: string, - index: number - ) => { - const effectSettingState = postprocessing.effects[effectName][property] + const renderProperty = (effectName: string, property: string, index: number) => { + const effectSettingState = effects[effectName].schema[property] + const effectSettingValue = postprocessing.effects[effectName][property].value let renderVal = <> - switch (propertyDetail.propertyType) { + switch (effectSettingState.propertyType) { case PropertyTypes.Number: renderVal = ( @@ -276,7 +139,17 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { renderVal = ( + ) + break + + case PropertyTypes.SMAAPreset: + renderVal = ( + ) break @@ -286,7 +159,7 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { ) break @@ -296,11 +169,37 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { + ) + break + + case PropertyTypes.Vector2: + renderVal = ( + + ) + break + + case PropertyTypes.Vector3: + renderVal = ( + ) break + case PropertyTypes.Texture: + renderVal = ( + + ) + break case PropertyTypes.Color: renderVal = ( { ) break - case PropertyTypes.SMAAPreset: + case PropertyTypes.ColorSpace: renderVal = ( ) break @@ -340,7 +239,7 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { ) break @@ -350,7 +249,7 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { ) break @@ -367,7 +266,7 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { alignItems: 'center' }} > - + {renderVal} @@ -375,12 +274,12 @@ export const PostProcessingSettingsEditor: EditorComponentType = (props) => { } const renderEffectsTypes = (effectName) => { - const effect = EffectsOptions[effectName] - return Object.keys(effect).map((prop, index) => renderProperty(effect[prop], effectName, prop, index)) + const effect = getState(PostProcessingEffectState)[effectName].schema + return Object.keys(effect).map((prop, index) => renderProperty(effectName, prop, index)) } const renderEffects = () => { - const items = Object.keys(EffectsOptions).map((effect: keyof typeof Effects) => { + const items = Object.keys(getState(PostProcessingEffectState)).map((effect) => { return (
{ visualScriptQuery().forEach((entity) => dispatchAction(VisualScriptActions.execute({ entity }))) transformGizmoControlledQuery().forEach((entity) => removeComponent(entity, TransformGizmoControlledComponent)) //just remove all gizmo in the scene + + //just clear selection to remove all higlights in the scene + SelectionState.updateSelection([]) } } diff --git a/packages/editor/src/functions/EditorControlFunctions.ts b/packages/editor/src/functions/EditorControlFunctions.ts index 0068993a27..c3b5cd13e5 100644 --- a/packages/editor/src/functions/EditorControlFunctions.ts +++ b/packages/editor/src/functions/EditorControlFunctions.ts @@ -49,7 +49,6 @@ import { ComponentJsonType } from '@etherealengine/engine/src/scene/types/SceneT import { dispatchAction, getMutableState, getState } from '@etherealengine/hyperflux' import { DirectionalLightComponent, HemisphereLightComponent } from '@etherealengine/spatial' import { MAT4_IDENTITY } from '@etherealengine/spatial/src/common/constants/MathConstants' -import { PostProcessingComponent } from '@etherealengine/spatial/src/renderer/components/PostProcessingComponent' import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent' import { getMaterial } from '@etherealengine/spatial/src/renderer/materials/materialFunctions' import { @@ -61,6 +60,7 @@ import { import { TransformComponent } from '@etherealengine/spatial/src/transform/components/TransformComponent' import { computeTransformMatrix } from '@etherealengine/spatial/src/transform/systems/TransformSystem' +import { PostProcessingComponent } from '@etherealengine/spatial/src/renderer/components/PostProcessingComponent' import { EditorHelperState } from '../services/EditorHelperState' import { EditorState } from '../services/EditorServices' import { SelectionState } from '../services/SelectionServices' diff --git a/packages/editor/src/systems/HighlightSystem.ts b/packages/editor/src/systems/HighlightSystem.ts new file mode 100644 index 0000000000..ed49fc0643 --- /dev/null +++ b/packages/editor/src/systems/HighlightSystem.ts @@ -0,0 +1,53 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { removeComponent, setComponent } from '@etherealengine/ecs' +import { defineSystem } from '@etherealengine/ecs/src/SystemFunctions' +import { AnimationSystemGroup } from '@etherealengine/ecs/src/SystemGroups' +import { HighlightComponent } from '@etherealengine/spatial/src/renderer/components/HighlightComponent' +import { useEffect } from 'react' +import { SelectionState } from '../services/SelectionServices' + +const reactor = () => { + const selectedEntities = SelectionState.useSelectedEntities() + + useEffect(() => { + if (!selectedEntities) return + const lastSelection = selectedEntities[selectedEntities.length - 1] + if (!lastSelection) return + setComponent(lastSelection, HighlightComponent) + return () => { + removeComponent(lastSelection, HighlightComponent) + } + }, [selectedEntities]) + + return null +} + +export const HighlightSystem = defineSystem({ + uuid: 'ee.editor.HighlightSystem', + insert: { with: AnimationSystemGroup }, + reactor +}) diff --git a/packages/engine/src/EngineModule.ts b/packages/engine/src/EngineModule.ts index 71ed525d71..079e26e80a 100644 --- a/packages/engine/src/EngineModule.ts +++ b/packages/engine/src/EngineModule.ts @@ -31,6 +31,7 @@ export * from './avatar/AvatarModule' export * from './interaction/InteractionModule' export * from './interaction/MediaModule' export * from './mocap/MocapModule' +export * from './postprocessing/PopulateEffectRegistry' export * from './recording/RecordingModule' export * from './scene/SceneModule' export * from './visualscript/VisualScriptModule' diff --git a/packages/engine/src/postprocessing/BloomEffect.tsx b/packages/engine/src/postprocessing/BloomEffect.tsx new file mode 100644 index 0000000000..b5b5e3bfa0 --- /dev/null +++ b/packages/engine/src/postprocessing/BloomEffect.tsx @@ -0,0 +1,114 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, BloomEffect, KernelSize } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + BloomEffect: BloomEffect + } +} + +const effectKey = 'BloomEffect' + +export const BloomEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new BloomEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const bloomAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: BloomEffectProcessReactor, + defaultValues: { + isActive: true, + blendFunction: BlendFunction.SCREEN, + kernelSize: KernelSize.LARGE, + luminanceThreshold: 0.9, + luminanceSmoothing: 0.025, + mipmapBlur: false, + intensity: 1.0, + radius: 0.85, + levels: 8 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + kernelSize: { propertyType: PropertyTypes.KernelSize, name: 'Kernel Size' }, + intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: 0, max: 10, step: 0.01 }, + luminanceSmoothing: { + propertyType: PropertyTypes.Number, + name: 'Luminance Smoothing', + min: 0, + max: 1, + step: 0.01 + }, + luminanceThreshold: { + propertyType: PropertyTypes.Number, + name: 'Luminance Threshold', + min: 0, + max: 1, + step: 0.01 + }, + mipmapBlur: { propertyType: PropertyTypes.Boolean, name: 'Mipmap Blur' }, + radius: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 0, max: 10, step: 0.01 }, + levels: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 1, max: 10, step: 1 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/BrightnessContrastEffect.tsx b/packages/engine/src/postprocessing/BrightnessContrastEffect.tsx new file mode 100644 index 0000000000..2016e33f54 --- /dev/null +++ b/packages/engine/src/postprocessing/BrightnessContrastEffect.tsx @@ -0,0 +1,91 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, BrightnessContrastEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + BrightnessContrastEffect: BrightnessContrastEffect + } +} + +const effectKey = 'BrightnessContrastEffect' + +export const BrightnessContrastEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new BrightnessContrastEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const brightnessContrastAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: BrightnessContrastEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SRC, + brightness: 0.0, + contrast: 0.0 + }, + schema: { + brightness: { propertyType: PropertyTypes.Number, name: 'Brightness', min: -1, max: 1, step: 0.01 }, + contrast: { propertyType: PropertyTypes.Number, name: 'Contrast', min: -1, max: 1, step: 0.01 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/ChromaticAberrationEffect.tsx b/packages/engine/src/postprocessing/ChromaticAberrationEffect.tsx new file mode 100644 index 0000000000..a394294e8b --- /dev/null +++ b/packages/engine/src/postprocessing/ChromaticAberrationEffect.tsx @@ -0,0 +1,93 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { ChromaticAberrationEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { Vector2 } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + ChromaticAberrationEffect: ChromaticAberrationEffect + } +} + +const effectKey = 'ChromaticAberrationEffect' + +export const ChromaticAberrationEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new ChromaticAberrationEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const chromaticAberrationAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: ChromaticAberrationEffectProcessReactor, + defaultValues: { + isActive: false, + offset: new Vector2(1e-3, 5e-4), + radialModulation: false, + modulationOffset: 0.15 + }, + schema: { + offset: { propertyType: PropertyTypes.Vector2, name: 'Offset' }, + radialModulation: { propertyType: PropertyTypes.Boolean, name: 'Radial Modulation' }, + modulationOffset: { propertyType: PropertyTypes.Number, name: 'Modulation Offset', min: 0, max: 10, step: 0.01 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/ColorAverageEffect.tsx b/packages/engine/src/postprocessing/ColorAverageEffect.tsx new file mode 100644 index 0000000000..84d6239bf0 --- /dev/null +++ b/packages/engine/src/postprocessing/ColorAverageEffect.tsx @@ -0,0 +1,88 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, ColorAverageEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + ColorAverageEffect: ColorAverageEffect + } +} + +const effectKey = 'ColorAverageEffect' + +export const ColorAverageEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new ColorAverageEffect(effectData[effectKey].value.blendFunction) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const colorAverageAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: ColorAverageEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/ColorDepthEffect.tsx b/packages/engine/src/postprocessing/ColorDepthEffect.tsx new file mode 100644 index 0000000000..268af45c2f --- /dev/null +++ b/packages/engine/src/postprocessing/ColorDepthEffect.tsx @@ -0,0 +1,90 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, ColorDepthEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + ColorDepthEffect: ColorDepthEffect + } +} + +const effectKey = 'ColorDepthEffect' + +export const ColorDepthEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new ColorDepthEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const colorDepthAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: ColorDepthEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + bits: 16 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + bits: { propertyType: PropertyTypes.Number, name: 'Bits', min: 8, max: 256, step: 8 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/DepthOfFieldEffect.tsx b/packages/engine/src/postprocessing/DepthOfFieldEffect.tsx new file mode 100644 index 0000000000..2fa09fd1fc --- /dev/null +++ b/packages/engine/src/postprocessing/DepthOfFieldEffect.tsx @@ -0,0 +1,103 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity, useComponent } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, DepthOfFieldEffect, Resolution } from 'postprocessing' +import React, { useEffect } from 'react' +import { ArrayCamera } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + DepthOfFieldEffect: DepthOfFieldEffect + } +} + +const effectKey = 'DepthOfFieldEffect' + +export const DepthOfFieldEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const camera = useComponent(rendererEntity, CameraComponent) + const eff = new DepthOfFieldEffect(camera.value as ArrayCamera, effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const depthOfFieldAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: DepthOfFieldEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + focusDistance: 0.0, + focalLength: 0.1, + focusRange: 0.1, + bokehScale: 1.0, + resolutionScale: 1.0, + resolutionX: Resolution.AUTO_SIZE, + resolutionY: Resolution.AUTO_SIZE + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + bokehScale: { propertyType: PropertyTypes.Number, name: 'Bokeh Scale', min: -10, max: 10, step: 0.01 }, + focalLength: { propertyType: PropertyTypes.Number, name: 'Focal Length', min: 0, max: 1, step: 0.01 }, + focalRange: { propertyType: PropertyTypes.Number, name: 'Focal Range', min: 0, max: 1, step: 0.01 }, + focusDistance: { propertyType: PropertyTypes.Number, name: 'Focus Distance', min: 0, max: 1, step: 0.01 }, + resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: -10, max: 10, step: 0.01 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/DotScreenEffect.tsx b/packages/engine/src/postprocessing/DotScreenEffect.tsx new file mode 100644 index 0000000000..eb475231c4 --- /dev/null +++ b/packages/engine/src/postprocessing/DotScreenEffect.tsx @@ -0,0 +1,92 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, DotScreenEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + DotScreenEffect: DotScreenEffect + } +} + +const effectKey = 'DotScreenEffect' + +export const DotScreenEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new DotScreenEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const dotScreenAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: DotScreenEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + angle: Math.PI * 0.5, + scale: 1.0 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + angle: { propertyType: PropertyTypes.Number, name: 'Angle', min: 0, max: 360, step: 0.1 }, + scale: { propertyType: PropertyTypes.Number, name: 'Scale', min: 0, max: 10, step: 0.1 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/FXAAEffect.tsx b/packages/engine/src/postprocessing/FXAAEffect.tsx new file mode 100644 index 0000000000..f2e47bba76 --- /dev/null +++ b/packages/engine/src/postprocessing/FXAAEffect.tsx @@ -0,0 +1,88 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, FXAAEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + FXAAEffect: FXAAEffect + } +} + +const effectKey = 'FXAAEffect' + +export const FXAAEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new FXAAEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const fxaaAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: FXAAEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SRC + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/GlitchEffect.tsx b/packages/engine/src/postprocessing/GlitchEffect.tsx new file mode 100644 index 0000000000..dad2ab6ac8 --- /dev/null +++ b/packages/engine/src/postprocessing/GlitchEffect.tsx @@ -0,0 +1,105 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, GlitchEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { Vector2 } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + GlitchEffect: GlitchEffect + } +} + +const effectKey = 'GlitchEffect' + +export const GlitchEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new GlitchEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const glitchAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: GlitchEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + chromaticAberrationOffset: undefined, + delay: new Vector2(1.5, 3.5), + duration: new Vector2(0.6, 1.0), + strength: new Vector2(0.3, 1.0), + perturbationMap: undefined, + dtSize: 64, + columns: 0.05, + ratio: 0.85 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + chromaticAberrationOffset: { propertyType: PropertyTypes.Vector2, name: 'Chromatic Aberration Offset' }, + delay: { propertyType: PropertyTypes.Vector2, name: 'Delay' }, + duration: { propertyType: PropertyTypes.Vector2, name: 'Duration' }, + strength: { propertyType: PropertyTypes.Vector2, name: 'Strength' }, + perturbationMap: { propertyType: PropertyTypes.Texture, name: 'Perturbation Map' }, + dtSize: { propertyType: PropertyTypes.Number, name: 'DT Size', min: 0, max: 10, step: 0.1 }, + columns: { propertyType: PropertyTypes.Number, name: 'Columns', min: 0, max: 10, step: 0.1 }, + ratio: { propertyType: PropertyTypes.Number, name: 'Ratio', min: 0, max: 10, step: 0.1 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/GridEffect.tsx b/packages/engine/src/postprocessing/GridEffect.tsx new file mode 100644 index 0000000000..07db1d08b2 --- /dev/null +++ b/packages/engine/src/postprocessing/GridEffect.tsx @@ -0,0 +1,92 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, GridEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + GridEffect: GridEffect + } +} + +const effectKey = 'GridEffect' + +export const GridEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new GridEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const gridAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: GridEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.OVERLAY, + scale: 1.0, + lineWidth: 0.0 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + scale: { propertyType: PropertyTypes.Number, name: 'Scale', min: 0, max: 10, step: 0.1 }, + lineWidth: { propertyType: PropertyTypes.Number, name: 'Line Width', min: 0, max: 10, step: 0.1 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/HueSaturationEffect.tsx b/packages/engine/src/postprocessing/HueSaturationEffect.tsx new file mode 100644 index 0000000000..a0034a432c --- /dev/null +++ b/packages/engine/src/postprocessing/HueSaturationEffect.tsx @@ -0,0 +1,92 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, HueSaturationEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + HueSaturationEffect: HueSaturationEffect + } +} + +const effectKey = 'HueSaturationEffect' + +export const HueSaturationEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new HueSaturationEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const hueSaturationAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: HueSaturationEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SRC, + hue: 0, + saturation: 0.0 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + hue: { propertyType: PropertyTypes.Number, name: 'Hue', min: -1, max: 1, step: 0.01 }, + saturation: { propertyType: PropertyTypes.Number, name: 'Saturation', min: -1, max: 1, step: 0.01 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/LUT1DEffect.tsx b/packages/engine/src/postprocessing/LUT1DEffect.tsx new file mode 100644 index 0000000000..a1f496bfb5 --- /dev/null +++ b/packages/engine/src/postprocessing/LUT1DEffect.tsx @@ -0,0 +1,97 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, LUT1DEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { useTexture } from '../assets/functions/resourceLoaderHooks' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + LUT1DEffect: LUT1DEffect + } +} + +const effectKey = 'LUT1DEffect' + +export const LUT1DEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + const [lut1DEffectTexture, lut1DEffectTextureError] = useTexture(effectData[effectKey].value?.lutPath!) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + if (lut1DEffectTexture) { + const eff = new LUT1DEffect(lut1DEffectTexture, effectData[effectKey].value) + effects[effectKey].set(eff) + } + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey], lut1DEffectTexture]) + + return null +} + +export const lut1DAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: LUT1DEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SRC, + lutPath: undefined, + lut: undefined + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + lutPath: { propertyType: PropertyTypes.Texture, name: 'LUT' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/LUT3DEffect.tsx b/packages/engine/src/postprocessing/LUT3DEffect.tsx new file mode 100644 index 0000000000..8e4a80b95f --- /dev/null +++ b/packages/engine/src/postprocessing/LUT3DEffect.tsx @@ -0,0 +1,97 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, LUT3DEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { useTexture } from '../assets/functions/resourceLoaderHooks' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + LUT3DEffect: LUT3DEffect + } +} + +const effectKey = 'LUT3DEffect' + +export const LUT3DEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + const [lut3DEffectTexture, lut3DEffectTextureError] = useTexture(effectData[effectKey].value?.lutPath!) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + if (lut3DEffectTexture) { + const eff = new LUT3DEffect(lut3DEffectTexture, effectData[effectKey].value) + effects[effectKey].set(eff) + } + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey], lut3DEffectTexture]) + + return null +} + +export const lut3DAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: LUT3DEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SRC, + lutPath: undefined, + lut: undefined + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + lutPath: { propertyType: PropertyTypes.Texture, name: 'LUT' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/LensDistortionEffect.tsx b/packages/engine/src/postprocessing/LensDistortionEffect.tsx new file mode 100644 index 0000000000..ea714ed091 --- /dev/null +++ b/packages/engine/src/postprocessing/LensDistortionEffect.tsx @@ -0,0 +1,95 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { LensDistortionEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { Vector2 } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + LensDistortionEffect: LensDistortionEffect + } +} + +const effectKey = 'LensDistortionEffect' + +export const LensDistortionEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new LensDistortionEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const lensDistortionAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: LensDistortionEffectProcessReactor, + defaultValues: { + isActive: false, + distortion: new Vector2(0, 0), + principalPoint: new Vector2(0, 0), + focalLength: new Vector2(1, 1), + skew: 0 + }, + schema: { + distortion: { propertyType: PropertyTypes.Vector2, name: 'Distortion' }, + principalPoint: { propertyType: PropertyTypes.Vector2, name: 'Principal Point' }, + focalLength: { propertyType: PropertyTypes.Vector2, name: 'Focal Length' }, + skew: { propertyType: PropertyTypes.Number, name: 'Skew', min: 0, max: 10, step: 0.05 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/LinearTosRGBEffect.tsx b/packages/engine/src/postprocessing/LinearTosRGBEffect.tsx new file mode 100644 index 0000000000..f483556140 --- /dev/null +++ b/packages/engine/src/postprocessing/LinearTosRGBEffect.tsx @@ -0,0 +1,85 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { LinearTosRGBEffect } from '@etherealengine/spatial/src/renderer/effects/LinearTosRGBEffect' +import React, { useEffect } from 'react' + +declare module 'postprocessing' { + interface EffectComposer { + LinearTosRGBEffect: LinearTosRGBEffect + } +} + +const effectKey = 'LinearTosRGBEffect' + +export const LinearTosRGBEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new LinearTosRGBEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const linearTosRGBAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: LinearTosRGBEffectProcessReactor, + defaultValues: { + isActive: false, + skew: 0 + }, + schema: {} + } + }) +} diff --git a/packages/engine/src/postprocessing/MotionBlurEffect.tsx b/packages/engine/src/postprocessing/MotionBlurEffect.tsx new file mode 100644 index 0000000000..664d78efb2 --- /dev/null +++ b/packages/engine/src/postprocessing/MotionBlurEffect.tsx @@ -0,0 +1,98 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity, useComponent } from '@etherealengine/ecs' +import { getMutableState, getState, none, useHookstate } from '@etherealengine/hyperflux' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import React, { useEffect } from 'react' +import { MotionBlurEffect, VelocityDepthNormalPass } from 'realism-effects' +import { Scene } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + MotionBlurEffect: MotionBlurEffect + } +} + +const effectKey = 'MotionBlurEffect' + +export const MotionBlurEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + const camera = useComponent(rendererEntity, CameraComponent) + const scene = useHookstate(() => new Scene()) + const velocityDepthNormalPass = useHookstate(new VelocityDepthNormalPass(scene, camera)) + const useVelocityDepthNormalPass = useHookstate(false) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new MotionBlurEffect(velocityDepthNormalPass, effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const motionBlurAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: MotionBlurEffectProcessReactor, + defaultValues: { + isActive: false, + intensity: 1, + jitter: 1, + samples: 16 + }, + schema: { + intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: 0, max: 10, step: 0.01 }, + jitter: { propertyType: PropertyTypes.Number, name: 'Jitter', min: 0, max: 10, step: 0.01 }, + samples: { propertyType: PropertyTypes.Number, name: 'Samples', min: 1, max: 64, step: 1 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/NoiseEffect.tsx b/packages/engine/src/postprocessing/NoiseEffect.tsx new file mode 100644 index 0000000000..9f7686f71f --- /dev/null +++ b/packages/engine/src/postprocessing/NoiseEffect.tsx @@ -0,0 +1,90 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, NoiseEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + NoiseEffect: NoiseEffect + } +} + +const effectKey = 'NoiseEffect' + +export const NoiseEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new NoiseEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const noiseAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: NoiseEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SCREEN, + premultiply: false + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + premultiply: { propertyType: PropertyTypes.Boolean, name: 'Premultiply' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/OutlineEffect.tsx b/packages/engine/src/postprocessing/OutlineEffect.tsx new file mode 100644 index 0000000000..38bf738d18 --- /dev/null +++ b/packages/engine/src/postprocessing/OutlineEffect.tsx @@ -0,0 +1,110 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, KernelSize, OutlineEffect, Resolution } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +const effectKey = 'OutlineEffect' + +export const OutlineEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new OutlineEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const outlineAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: OutlineEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SCREEN, + patternScale: 1.0, + edgeStrength: 1.0, + pulseSpeed: 0.0, + visibleEdgeColor: 0xffffff, + hiddenEdgeColor: 0x22090a, + multisampling: 0, + resolutionScale: 0.5, + resolutionX: Resolution.AUTO_SIZE, + resolutionY: Resolution.AUTO_SIZE, + width: Resolution.AUTO_SIZE, + height: 480, + kernelSize: KernelSize.VERY_SMALL, + blur: false, + xRay: true + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + patternScale: { propertyType: PropertyTypes.Number, name: 'Pattern Scale', min: 0, max: 10, step: 0.01 }, + edgeStrength: { propertyType: PropertyTypes.Number, name: 'Edge Strength', min: 0, max: 10, step: 0.01 }, + pulseSpeed: { propertyType: PropertyTypes.Number, name: 'Pulse Speed', min: 0, max: 10, step: 0.01 }, + visibleEdgeColor: { propertyType: PropertyTypes.Color, name: 'Visible Edge Color' }, + hiddenEdgeColor: { propertyType: PropertyTypes.Color, name: 'Hidden Edge Color' }, + multisampling: { propertyType: PropertyTypes.Number, name: 'Multisampling', min: 0, max: 10, step: 0.01 }, + resolutionScale: { propertyType: PropertyTypes.Number, name: 'ResolutionScale', min: 0, max: 10, step: 0.01 }, + blur: { propertyType: PropertyTypes.Boolean, name: 'Blur' }, + xRay: { propertyType: PropertyTypes.Boolean, name: 'xRay' }, + kernelSize: { propertyType: PropertyTypes.KernelSize, name: 'KernelSize' } + //resolutionX: Resolution.AUTO_SIZE, + //resolutionY: Resolution.AUTO_SIZE, + //width: Resolution.AUTO_SIZE, + //height: 480, + } + } + }) +} diff --git a/packages/engine/src/postprocessing/PixelationEffect.tsx b/packages/engine/src/postprocessing/PixelationEffect.tsx new file mode 100644 index 0000000000..ea5bfe504d --- /dev/null +++ b/packages/engine/src/postprocessing/PixelationEffect.tsx @@ -0,0 +1,88 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { PixelationEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + PixelationEffect: PixelationEffect + } +} + +const effectKey = 'PixelationEffect' + +export const PixelationEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new PixelationEffect(effectData[effectKey].granularity.value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const pixelationAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: PixelationEffectProcessReactor, + defaultValues: { + isActive: false, + granularity: 30 + }, + schema: { + granularity: { propertyType: PropertyTypes.Number, name: 'Granularity', min: 0, max: 1000, step: 1 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/PopulateEffectRegistry.tsx b/packages/engine/src/postprocessing/PopulateEffectRegistry.tsx new file mode 100644 index 0000000000..feb31869c8 --- /dev/null +++ b/packages/engine/src/postprocessing/PopulateEffectRegistry.tsx @@ -0,0 +1,98 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { PresentationSystemGroup } from '@etherealengine/ecs' +import { defineSystem } from '@etherealengine/ecs/src/SystemFunctions' +import { useEffect } from 'react' +import { bloomAddToEffectRegistry } from './BloomEffect' +import { brightnessContrastAddToEffectRegistry } from './BrightnessContrastEffect' +import { chromaticAberrationAddToEffectRegistry } from './ChromaticAberrationEffect' +import { colorAverageAddToEffectRegistry } from './ColorAverageEffect' +import { colorDepthAddToEffectRegistry } from './ColorDepthEffect' +import { depthOfFieldAddToEffectRegistry } from './DepthOfFieldEffect' +import { dotScreenAddToEffectRegistry } from './DotScreenEffect' +import { fxaaAddToEffectRegistry } from './FXAAEffect' +import { glitchAddToEffectRegistry } from './GlitchEffect' +import { gridAddToEffectRegistry } from './GridEffect' +import { hueSaturationAddToEffectRegistry } from './HueSaturationEffect' +import { lut1DAddToEffectRegistry } from './LUT1DEffect' +import { lut3DAddToEffectRegistry } from './LUT3DEffect' +import { lensDistortionAddToEffectRegistry } from './LensDistortionEffect' +import { linearTosRGBAddToEffectRegistry } from './LinearTosRGBEffect' +import { motionBlurAddToEffectRegistry } from './MotionBlurEffect' +import { noiseAddToEffectRegistry } from './NoiseEffect' +import { pixelationAddToEffectRegistry } from './PixelationEffect' +import { ssaoAddToEffectRegistry } from './SSAOEffect' +import { ssgiAddToEffectRegistry } from './SSGIEffect' +import { ssrAddToEffectRegistry } from './SSREffect' +import { scanlineAddToEffectRegistry } from './ScanlineEffect' +import { shockWaveAddToEffectRegistry } from './ShockWaveEffect' +import { traaAddToEffectRegistry } from './TRAAEffect' +import { textureAddToEffectRegistry } from './TextureEffect' +import { tiltShiftAddToEffectRegistry } from './TiltShiftEffect' +import { toneMappingAddToEffectRegistry } from './ToneMappingEffect' +import { vignetteAddToEffectRegistry } from './VignetteEffect' + +export const populateEffectRegistry = () => { + // registers the effects + bloomAddToEffectRegistry() + brightnessContrastAddToEffectRegistry() + chromaticAberrationAddToEffectRegistry() + colorAverageAddToEffectRegistry() + colorDepthAddToEffectRegistry() + depthOfFieldAddToEffectRegistry() + dotScreenAddToEffectRegistry() + fxaaAddToEffectRegistry() + glitchAddToEffectRegistry() + //GodRaysEffect + gridAddToEffectRegistry() + hueSaturationAddToEffectRegistry() + lensDistortionAddToEffectRegistry() + linearTosRGBAddToEffectRegistry() + lut1DAddToEffectRegistry() + lut3DAddToEffectRegistry() + motionBlurAddToEffectRegistry() + noiseAddToEffectRegistry() + pixelationAddToEffectRegistry() + scanlineAddToEffectRegistry() + shockWaveAddToEffectRegistry() + ssaoAddToEffectRegistry() + ssrAddToEffectRegistry() + ssgiAddToEffectRegistry() + textureAddToEffectRegistry() + tiltShiftAddToEffectRegistry() + toneMappingAddToEffectRegistry() + traaAddToEffectRegistry() + vignetteAddToEffectRegistry() +} + +export const PostProcessingRegisterSystem = defineSystem({ + uuid: 'ee.engine.PostProcessingRegisterSystem', + insert: { before: PresentationSystemGroup }, + reactor: () => { + useEffect(() => populateEffectRegistry(), []) + return null + } +}) diff --git a/packages/engine/src/postprocessing/PostProcessingRegister.tsx b/packages/engine/src/postprocessing/PostProcessingRegister.tsx new file mode 100644 index 0000000000..cabe696f3c --- /dev/null +++ b/packages/engine/src/postprocessing/PostProcessingRegister.tsx @@ -0,0 +1,40 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +export enum PropertyTypes { + BlendFunction, + Number, + Boolean, + Color, + ColorSpace, + KernelSize, + SMAAPreset, + EdgeDetectionMode, + PredicationMode, + Texture, + Vector2, + Vector3, + VignetteTechnique +} diff --git a/packages/engine/src/postprocessing/SMAAEffect.tsx b/packages/engine/src/postprocessing/SMAAEffect.tsx new file mode 100644 index 0000000000..a89c39e99a --- /dev/null +++ b/packages/engine/src/postprocessing/SMAAEffect.tsx @@ -0,0 +1,84 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { EdgeDetectionMode, PredicationMode, SMAAEffect, SMAAPreset } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +const effectKey = 'SMAAEffect' + +export const SMAAEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new SMAAEffect(effectData[effectKey].granularity.value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const smaaAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: SMAAEffectProcessReactor, + defaultValues: { + isActive: false, + preset: SMAAPreset.MEDIUM, + edgeDetectionMode: EdgeDetectionMode.COLOR, + predicationMode: PredicationMode.DISABLED + }, + schema: { + preset: { propertyType: PropertyTypes.SMAAPreset, name: 'Preset' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/SSAOEffect.tsx b/packages/engine/src/postprocessing/SSAOEffect.tsx new file mode 100644 index 0000000000..7856026c98 --- /dev/null +++ b/packages/engine/src/postprocessing/SSAOEffect.tsx @@ -0,0 +1,116 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity, useComponent } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, Resolution, SSAOEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { ArrayCamera } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + SSAOEffect: SSAOEffect + } +} + +const effectKey = 'SSAOEffect' + +export const SSAOEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const camera = useComponent(rendererEntity, CameraComponent) + const eff = new SSAOEffect(camera.value as ArrayCamera, effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const ssaoAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: SSAOEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.MULTIPLY, + distanceScaling: true, + depthAwareUpsampling: true, + normalDepthBuffer: undefined, + samples: 9, + rings: 7, + // worldDistanceThreshold: 0.97, + // worldDistanceFalloff: 0.03, + // worldProximityThreshold: 0.0005, + // worldProximityFalloff: 0.001, + distanceThreshold: 0.97, // Render up to a distance of ~20 world units + distanceFalloff: 0.03, // with an additional ~2.5 units of falloff. + rangeThreshold: 0.0005, + rangeFalloff: 0.001, + minRadiusScale: 0.1, + luminanceInfluence: 0.7, + bias: 0.025, + radius: 0.1825, + intensity: 1.0, + fade: 0.01, + color: undefined, + resolutionScale: 1.0, + resolutionX: Resolution.AUTO_SIZE, + resolutionY: Resolution.AUTO_SIZE, + width: Resolution.AUTO_SIZE, + height: Resolution.AUTO_SIZE + }, + schema: { + preset: { propertyType: PropertyTypes.SMAAPreset, name: 'Preset' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/SSGIEffect.tsx b/packages/engine/src/postprocessing/SSGIEffect.tsx new file mode 100644 index 0000000000..47f9a305d5 --- /dev/null +++ b/packages/engine/src/postprocessing/SSGIEffect.tsx @@ -0,0 +1,131 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import React, { useEffect } from 'react' +import { SSGIEffect } from 'realism-effects' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + SSGIEffect: SSGIEffect + } +} + +const effectKey = 'SSGIEffect' + +export const SSGIEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + const eff = new SSGIEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const ssgiAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: SSGIEffectProcessReactor, + defaultValues: { + isActive: false, + distance: 10, + thickness: 10, + denoiseIterations: 1, + denoiseKernel: 2, + denoiseDiffuse: 10, + denoiseSpecular: 10, + radius: 3, + phi: 0.5, + lumaPhi: 5, + depthPhi: 2, + normalPhi: 50, + roughnessPhi: 50, + specularPhi: 50, + envBlur: 0.5, + importanceSampling: true, + steps: 20, + refineSteps: 5, + resolutionScale: 1, + missedRays: false + }, + schema: { + distance: { propertyType: PropertyTypes.Number, name: 'Distance', min: 0.001, max: 10, step: 0.01 }, + thickness: { propertyType: PropertyTypes.Number, name: 'Thickness', min: 0, max: 5, step: 0.01 }, + denoiseIterations: { propertyType: PropertyTypes.Number, name: 'Denoise Iterations', min: 0, max: 5, step: 1 }, + denoiseKernel: { propertyType: PropertyTypes.Number, name: 'Denoise Kernel', min: 1, max: 5, step: 1 }, + denoiseDiffuse: { propertyType: PropertyTypes.Number, name: 'Denoise Diffuse', min: 0, max: 50, step: 0.01 }, + denoiseSpecular: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, + radius: { propertyType: PropertyTypes.Number, name: 'Radius', min: 0, max: 50, step: 0.01 }, + phi: { propertyType: PropertyTypes.Number, name: 'Phi', min: 0, max: 50, step: 0.01 }, + lumaPhi: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, + depthPhi: { propertyType: PropertyTypes.Number, name: 'luminosity Phi', min: 0, max: 15, step: 0.001 }, + normalPhi: { propertyType: PropertyTypes.Number, name: 'Normal Phi', min: 0, max: 50, step: 0.001 }, + roughnessPhi: { propertyType: PropertyTypes.Number, name: 'Roughness Phi', min: 0, max: 100, step: 0.001 }, + specularPhi: { propertyType: PropertyTypes.Number, name: 'Specular Phi', min: 0, max: 50, step: 0.01 }, + envBlur: { propertyType: PropertyTypes.Number, name: 'Environment Blur', min: 0, max: 1, step: 0.01 }, + importanceSampling: { propertyType: PropertyTypes.Boolean, name: 'Importance Sampling' }, + steps: { propertyType: PropertyTypes.Number, name: 'Steps', min: 0, max: 256, step: 1 }, + refineSteps: { propertyType: PropertyTypes.Number, name: 'Refine Steps', min: 0, max: 16, step: 1 }, + resolutionScale: { + propertyType: PropertyTypes.Number, + name: 'Resolution Scale', + min: 0.25, + max: 1, + step: 0.25 + }, + missedRays: { propertyType: PropertyTypes.Boolean, name: 'Missed Rays' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/SSREffect.tsx b/packages/engine/src/postprocessing/SSREffect.tsx new file mode 100644 index 0000000000..22912d2709 --- /dev/null +++ b/packages/engine/src/postprocessing/SSREffect.tsx @@ -0,0 +1,141 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity, useComponent } from '@etherealengine/ecs' +import { getMutableState, getState, none, useHookstate } from '@etherealengine/hyperflux' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { EffectComposer } from 'postprocessing' +import React, { useEffect } from 'react' +import { SSREffect, VelocityDepthNormalPass } from 'realism-effects' +import { Scene } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + SSREffect: SSREffect + } +} + +const effectKey = 'SSREffect' + +export const SSREffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects + composer: EffectComposer + scene: Scene +}) => { + const { isActive, rendererEntity, effectData, effects, composer, scene } = props + const effectState = getState(PostProcessingEffectState) + + const camera = useComponent(rendererEntity, CameraComponent) + const velocityDepthNormalPass = useHookstate(new VelocityDepthNormalPass(scene, camera)) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new SSREffect(composer, scene, camera.value, { + ...effectData[effectKey].value, + velocityDepthNormalPass + }) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const ssrAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: SSREffectProcessReactor, + defaultValues: { + isActive: false, + distance: 10, + thickness: 10, + denoiseIterations: 1, + denoiseKernel: 2, + denoiseDiffuse: 10, + denoiseSpecular: 10, + radius: 3, + phi: 0.5, + lumaPhi: 5, + depthPhi: 2, + normalPhi: 50, + roughnessPhi: 50, + specularPhi: 50, + envBlur: 0.5, + importanceSampling: true, + steps: 20, + refineSteps: 5, + resolutionScale: 1, + missedRays: false + }, + schema: { + distance: { propertyType: PropertyTypes.Number, name: 'Distance', min: 0.001, max: 10, step: 0.01 }, + thickness: { propertyType: PropertyTypes.Number, name: 'Thickness', min: 0, max: 5, step: 0.01 }, + denoiseIterations: { propertyType: PropertyTypes.Number, name: 'Denoise Iterations', min: 0, max: 5, step: 1 }, + denoiseKernel: { propertyType: PropertyTypes.Number, name: 'Denoise Kernel', min: 1, max: 5, step: 1 }, + denoiseDiffuse: { propertyType: PropertyTypes.Number, name: 'Denoise Diffuse', min: 0, max: 50, step: 0.01 }, + denoiseSpecular: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, + radius: { propertyType: PropertyTypes.Number, name: 'Radius', min: 0, max: 50, step: 0.01 }, + phi: { propertyType: PropertyTypes.Number, name: 'Phi', min: 0, max: 50, step: 0.01 }, + lumaPhi: { propertyType: PropertyTypes.Number, name: 'Denoise Specular', min: 0, max: 50, step: 0.01 }, + depthPhi: { propertyType: PropertyTypes.Number, name: 'luminosity Phi', min: 0, max: 15, step: 0.001 }, + normalPhi: { propertyType: PropertyTypes.Number, name: 'Normal Phi', min: 0, max: 50, step: 0.001 }, + roughnessPhi: { propertyType: PropertyTypes.Number, name: 'Roughness Phi', min: 0, max: 100, step: 0.001 }, + specularPhi: { propertyType: PropertyTypes.Number, name: 'Specular Phi', min: 0, max: 50, step: 0.01 }, + envBlur: { propertyType: PropertyTypes.Number, name: 'Environment Blur', min: 0, max: 1, step: 0.01 }, + importanceSampling: { propertyType: PropertyTypes.Boolean, name: 'Importance Sampling' }, + steps: { propertyType: PropertyTypes.Number, name: 'Steps', min: 0, max: 256, step: 1 }, + refineSteps: { propertyType: PropertyTypes.Number, name: 'Refine Steps', min: 0, max: 16, step: 1 }, + resolutionScale: { + propertyType: PropertyTypes.Number, + name: 'Resolution Scale', + min: 0.25, + max: 1, + step: 0.25 + }, + missedRays: { propertyType: PropertyTypes.Boolean, name: 'Missed Rays' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/ScanlineEffect.tsx b/packages/engine/src/postprocessing/ScanlineEffect.tsx new file mode 100644 index 0000000000..e9dde840be --- /dev/null +++ b/packages/engine/src/postprocessing/ScanlineEffect.tsx @@ -0,0 +1,92 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, ScanlineEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + ScanlineEffect: ScanlineEffect + } +} + +const effectKey = 'ScanlineEffect' + +export const ScanlineEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new ScanlineEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const scanlineAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: ScanlineEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.OVERLAY, + density: 1.25, + scrollSpeed: 0.0 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + density: { propertyType: PropertyTypes.Number, name: 'Density', min: 0, max: 10, step: 0.05 }, + scrollSpeed: { propertyType: PropertyTypes.Number, name: 'Scroll Speed', min: 0, max: 10, step: 0.05 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/ShockWaveEffect.tsx b/packages/engine/src/postprocessing/ShockWaveEffect.tsx new file mode 100644 index 0000000000..f62d3a9cd8 --- /dev/null +++ b/packages/engine/src/postprocessing/ShockWaveEffect.tsx @@ -0,0 +1,97 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { ShockWaveEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { Vector3 } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + ShockWaveEffect: ShockWaveEffect + } +} + +const effectKey = 'ShockWaveEffect' + +export const ShockWaveEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new ShockWaveEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} + +export const shockWaveAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: ShockWaveEffectProcessReactor, + defaultValues: { + isActive: false, + position: new Vector3(0, 0, 0), + speed: 2.0, + maxRadius: 1.0, + waveSize: 0.2, + amplitude: 0.05 + }, + schema: { + position: { propertyType: PropertyTypes.Vector3, name: 'Position' }, + speed: { propertyType: PropertyTypes.Number, name: 'Speed', min: 0, max: 10, step: 0.05 }, + maxRadius: { propertyType: PropertyTypes.Number, name: 'Max Radius', min: 0, max: 10, step: 0.05 }, + waveSize: { propertyType: PropertyTypes.Number, name: 'Wave Size', min: 0, max: 10, step: 0.05 }, + amplitude: { propertyType: PropertyTypes.Number, name: 'Amplitude', min: 0, max: 10, step: 0.05 } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/TRAAEffect.tsx b/packages/engine/src/postprocessing/TRAAEffect.tsx new file mode 100644 index 0000000000..843e5ec297 --- /dev/null +++ b/packages/engine/src/postprocessing/TRAAEffect.tsx @@ -0,0 +1,112 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity, useComponent } from '@etherealengine/ecs' +import { getMutableState, getState, none, useHookstate } from '@etherealengine/hyperflux' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import React, { useEffect } from 'react' +import { TRAAEffect, VelocityDepthNormalPass } from 'realism-effects' +import { Scene } from 'three' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + TRAAEffect: TRAAEffect + } +} + +const effectKey = 'TRAAEffect' + +export const TRAAEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects + scene: Scene +}) => { + const { isActive, rendererEntity, effectData, effects, scene } = props + const effectState = getState(PostProcessingEffectState) + const camera = useComponent(rendererEntity, CameraComponent) + const velocityDepthNormalPass = useHookstate(new VelocityDepthNormalPass(scene, camera)) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + // todo support more than 1 texture + const textureCount = 1 + + const eff = new TRAAEffect(scene, camera.value, velocityDepthNormalPass, textureCount, effectData[effectKey].value) + effects[effectKey].set(eff) + + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey], scene, velocityDepthNormalPass]) + + return null +} + +export const traaAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: TRAAEffectProcessReactor, + defaultValues: { + isActive: false, + blend: 0.8, + constantBlend: true, + dilation: true, + blockySampling: false, + logTransform: false, // ! TODO: check if can use logTransform withoutt artifacts + depthDistance: 10, + worldDistance: 5, + neighborhoodClamping: true + }, + schema: { + blend: { propertyType: PropertyTypes.Number, name: 'Blend', min: 0, max: 1, step: 0.001 }, + constantBlend: { propertyType: PropertyTypes.Boolean, name: 'Constant Blend' }, + dilation: { propertyType: PropertyTypes.Boolean, name: 'Dilation' }, + blockySampling: { propertyType: PropertyTypes.Boolean, name: 'Blocky Sampling' }, + logTransform: { propertyType: PropertyTypes.Boolean, name: 'Log Transform' }, + depthDistance: { propertyType: PropertyTypes.Number, name: 'Depth Distance', min: 0.01, max: 100, step: 0.01 }, + worldDistance: { propertyType: PropertyTypes.Number, name: 'World Distance', min: 0.01, max: 100, step: 0.01 }, + neighborhoodClamping: { propertyType: PropertyTypes.Boolean, name: 'Neighborhood Clamping' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/TextureEffect.tsx b/packages/engine/src/postprocessing/TextureEffect.tsx new file mode 100644 index 0000000000..3ea1c24a99 --- /dev/null +++ b/packages/engine/src/postprocessing/TextureEffect.tsx @@ -0,0 +1,100 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { NO_PROXY, getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, TextureEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { useTexture } from '../assets/functions/resourceLoaderHooks' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + TextureEffect: TextureEffect + } +} + +const effectKey = 'TextureEffect' + +export const TextureEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + const [textureEffectTexture, textureEffectTextureError] = useTexture(effectData[effectKey].value?.texturePath!) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + if (textureEffectTexture) { + let data = effectData[effectKey].get(NO_PROXY) + data.texture = textureEffectTexture + const eff = new TextureEffect(data) + effects[effectKey].set(eff) + } + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey], textureEffectTexture]) + + return null +} + +export const textureAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: TextureEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + texturePath: undefined, + texture: undefined, + aspectCorrection: false + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + texturePath: { propertyType: PropertyTypes.Texture, name: 'Texture' }, + aspectCorrection: { propertyType: PropertyTypes.Boolean, name: 'Aspect Correction' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/TiltShiftEffect.tsx b/packages/engine/src/postprocessing/TiltShiftEffect.tsx new file mode 100644 index 0000000000..835418bad4 --- /dev/null +++ b/packages/engine/src/postprocessing/TiltShiftEffect.tsx @@ -0,0 +1,106 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, KernelSize, Resolution, TiltShiftEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + TiltShiftEffect: TiltShiftEffect + } +} + +const effectKey = 'TiltShiftEffect' + +export const TiltShiftEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + const eff = new TiltShiftEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey]]) + + return null +} + +export const tiltShiftAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: TiltShiftEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + offset: 0.0, + rotation: 0.0, + focusArea: 0.4, + feather: 0.3, + kernelSize: KernelSize.MEDIUM, + resolutionScale: 0.5, + resolutionX: Resolution.AUTO_SIZE, + resolutionY: Resolution.AUTO_SIZE + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + offset: { propertyType: PropertyTypes.Number, name: 'Offset', min: 0, max: 10, step: 0.1 }, + rotation: { propertyType: PropertyTypes.Number, name: 'Rotation', min: 0, max: 360, step: 0.1 }, + focusArea: { propertyType: PropertyTypes.Number, name: 'Focus Area', min: 0, max: 10, step: 0.1 }, + feather: { propertyType: PropertyTypes.Number, name: 'Feather', min: 0, max: 10, step: 0.1 }, + kernelSize: { propertyType: PropertyTypes.KernelSize, name: 'KernelSize' }, + resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 0, max: 10, step: 0.1 } + //resolutionX: Resolution.AUTO_SIZE, + //resolutionY: Resolution.AUTO_SIZE + } + } + }) +} diff --git a/packages/engine/src/postprocessing/ToneMappingEffect.tsx b/packages/engine/src/postprocessing/ToneMappingEffect.tsx new file mode 100644 index 0000000000..4fd6560250 --- /dev/null +++ b/packages/engine/src/postprocessing/ToneMappingEffect.tsx @@ -0,0 +1,113 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, ToneMappingEffect, ToneMappingMode } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + ToneMappingEffect: ToneMappingEffect + } +} + +const effectKey = 'ToneMappingEffect' + +export const ToneMappingEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + const eff = new ToneMappingEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey]]) + + return null +} + +export const toneMappingAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: ToneMappingEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.SRC, + adaptive: false, + mode: ToneMappingMode.AGX, + resolution: 256, + maxLuminance: 4.0, + whitePoint: 4.0, + middleGrey: 0.6, + minLuminance: 0.01, + averageLuminance: 1.0, + adaptationRate: 1.0 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + adaptive: { propertyType: PropertyTypes.Boolean, name: 'Adaptive' }, + adaptationRate: { propertyType: PropertyTypes.Number, name: 'Adaptation Rate', min: -1, max: 1, step: 0.01 }, + averageLuminance: { + propertyType: PropertyTypes.Number, + name: 'Average Luminance', + min: -1, + max: 1, + step: 0.01 + }, + maxLuminance: { propertyType: PropertyTypes.Number, name: 'Max Luminance', min: -1, max: 1, step: 0.01 }, + middleGrey: { propertyType: PropertyTypes.Number, name: 'Middle Grey', min: -1, max: 1, step: 0.01 }, + resolution: { propertyType: PropertyTypes.Number, name: 'Resolution' }, + whitePoint: { propertyType: PropertyTypes.Number, name: 'Resolution' }, + minLuminance: { propertyType: PropertyTypes.Number, name: 'Resolution' } + } + } + }) +} diff --git a/packages/engine/src/postprocessing/VignetteEffect.tsx b/packages/engine/src/postprocessing/VignetteEffect.tsx new file mode 100644 index 0000000000..8609aae834 --- /dev/null +++ b/packages/engine/src/postprocessing/VignetteEffect.tsx @@ -0,0 +1,101 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Entity } from '@etherealengine/ecs' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { + EffectReactorProps, + PostProcessingEffectState +} from '@etherealengine/spatial/src/renderer/effects/EffectRegistry' +import { BlendFunction, VignetteEffect, VignetteTechnique } from 'postprocessing' +import React, { useEffect } from 'react' +import { PropertyTypes } from './PostProcessingRegister' + +declare module 'postprocessing' { + interface EffectComposer { + VignetteEffect: VignetteEffect + } +} + +const effectKey = 'VignetteEffect' + +export const VignetteEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + + // todo support more than 1 texture + const textureCount = 1 + + const eff = new VignetteEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + + return () => { + effects[effectKey].set(none) + } + }, [isActive, effectData[effectKey]]) + + return null +} + +export const vignetteAddToEffectRegistry = () => { + // registers the effect + + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: VignetteEffectProcessReactor, + defaultValues: { + isActive: false, + blendFunction: BlendFunction.NORMAL, + technique: VignetteTechnique.DEFAULT, + eskil: false, + offset: 0.5, + darkness: 0.5 + }, + schema: { + blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, + technique: { propertyType: PropertyTypes.VignetteTechnique, name: 'Technique' }, + eskil: { propertyType: PropertyTypes.Boolean, name: 'Eskil' }, + offset: { propertyType: PropertyTypes.Number, name: 'Offset', min: 0, max: 10, step: 0.1 }, + darkness: { propertyType: PropertyTypes.Number, name: 'Darkness', min: 0, max: 10, step: 0.1 } + } + } + }) +} diff --git a/packages/spatial/src/renderer/passes/CustomNormalPass.js b/packages/engine/src/postprocessing/passes/CustomNormalPass.js similarity index 98% rename from packages/spatial/src/renderer/passes/CustomNormalPass.js rename to packages/engine/src/postprocessing/passes/CustomNormalPass.js index 58a2fc86e8..424ef3bc85 100644 --- a/packages/spatial/src/renderer/passes/CustomNormalPass.js +++ b/packages/engine/src/postprocessing/passes/CustomNormalPass.js @@ -27,7 +27,7 @@ Ethereal Engine. All Rights Reserved. import { Color, MeshNormalMaterial, NearestFilter, WebGLRenderTarget } from "three"; import { Resolution, RenderPass, Pass } from "postprocessing"; -import { ObjectLayers } from "../constants/ObjectLayers"; +import { ObjectLayers } from "@etherealengine/spatial/src/renderer/constants/ObjectLayers"; /** * A pass that renders the normals of a given scene. */ diff --git a/packages/spatial/package.json b/packages/spatial/package.json index d2b06a44ca..fc888776a6 100755 --- a/packages/spatial/package.json +++ b/packages/spatial/package.json @@ -24,14 +24,13 @@ "bitecs": "0.3.40", "lodash": "4.17.21", "noisejs": "2.1.0", - "postprocessing": "6.34.1", "react": "18.2.0", "react-dom": "18.2.0", - "realism-effects": "1.1.2", "three": "0.158.0", "three-mesh-bvh": "^0.7.1", "ts-matches": "5.3.0", - "web-worker": "1.2.0" + "web-worker": "1.2.0", + "postprocessing": "6.34.1" }, "devDependencies": { "@types/mocha": "10.0.1", diff --git a/packages/spatial/src/renderer/components/PostProcessingComponent.test.tsx b/packages/spatial/src/renderer/components/PostProcessingComponent.test.tsx new file mode 100644 index 0000000000..65d3dc1e19 --- /dev/null +++ b/packages/spatial/src/renderer/components/PostProcessingComponent.test.tsx @@ -0,0 +1,201 @@ +// /* +// CPAL-1.0 License + +// The contents of this file are subject to the Common Public Attribution License +// Version 1.0. (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +// The License is based on the Mozilla Public License Version 1.1, but Sections 14 +// and 15 have been added to cover use of software over a computer network and +// provide for limited attribution for the Original Developer. In addition, +// Exhibit A has been modified to be consistent with Exhibit B. + +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +// specific language governing rights and limitations under the License. + +// The Original Code is Ethereal Engine. + +// The Original Developer is the Initial Developer. The Initial Developer of the +// Original Code is the Ethereal Engine team. + +// All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +// Ethereal Engine. All Rights Reserved. +// */ + +import assert from 'assert' +import { MathUtils } from 'three' + +import { + Entity, + EntityUUID, + UUIDComponent, + getComponent, + getMutableComponent, + hasComponent, + setComponent +} from '@etherealengine/ecs' +import { destroyEngine } from '@etherealengine/ecs/src/Engine' +import { createEntity, removeEntity } from '@etherealengine/ecs/src/EntityFunctions' +import { getMutableState, getState, none } from '@etherealengine/hyperflux' +import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent' +import { createEngine } from '@etherealengine/spatial/src/initializeEngine' +import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem' +import { SceneComponent } from '@etherealengine/spatial/src/renderer/components/SceneComponents' +import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree' +import { act, render } from '@testing-library/react' +import { BlendFunction, EffectComposer, NoiseEffect } from 'postprocessing' +import React, { useEffect } from 'react' +import { RendererState } from '../RendererState' +import { EffectReactorProps, PostProcessingEffectState } from '../effects/EffectRegistry' +import { PostProcessingComponent } from './PostProcessingComponent' + +describe('PostProcessingComponent', () => { + let rootEntity: Entity + let entity: Entity + + const mockCanvas = () => { + return { + getDrawingBufferSize: () => 0 + } as any as HTMLCanvasElement + } + + beforeEach(() => { + createEngine() + + rootEntity = createEntity() + setComponent(rootEntity, UUIDComponent, MathUtils.generateUUID() as EntityUUID) + setComponent(rootEntity, EntityTreeComponent) + setComponent(rootEntity, CameraComponent) + setComponent(rootEntity, SceneComponent) + setComponent(rootEntity, RendererComponent, { canvas: mockCanvas() }) + + entity = createEntity() + setComponent(entity, UUIDComponent, MathUtils.generateUUID() as EntityUUID) + getMutableState(RendererState).usePostProcessing.set(true) + setComponent(entity, PostProcessingComponent, { enabled: true }) + setComponent(entity, EntityTreeComponent) + + //set data to test + setComponent(rootEntity, SceneComponent, { children: [entity] }) + + //override addpass to test data without dependency on Browser + let addPassCount = 0 + EffectComposer.prototype.addPass = () => { + addPassCount++ + } + }) + + afterEach(() => { + return destroyEngine() + }) + + it('Create default post processing component', () => { + const postProcessingComponent = getComponent(entity, PostProcessingComponent) + assert(postProcessingComponent, 'post processing component exists') + }) + + it('Test Effect Composure amd Highlight Effect', async () => { + const effectKey = 'OutlineEffect' + + //force nested reactors to run + const { rerender, unmount } = render(<>) + + const postProcessingComponent = getMutableComponent(entity, PostProcessingComponent) + await act(() => rerender(<>)) + + const effectComposer = getComponent(rootEntity, RendererComponent).effectComposer + //test that the effect composer is setup + assert(getComponent(rootEntity, RendererComponent).effectComposer, 'effect composer is setup') + + //test that the effect pass has the the effect set + // @ts-ignore + const effects = getComponent(rootEntity, RendererComponent).effectComposer.EffectPass.effects + assert(effects.find((el) => el.name == effectKey)) + + unmount() + }) + + it('Test Effect Add and Remove', async () => { + const effectKey = 'NoiseEffect' + getMutableState(PostProcessingEffectState).merge({ + [effectKey]: { + reactor: NoiseEffectProcessReactor, + defaultValues: { + isActive: true, + blendFunction: BlendFunction.SCREEN, + premultiply: false + }, + schema: { + blendFunction: { propertyType: 0, name: 'Blend Function' }, + premultiply: { propertyType: 2, name: 'Premultiply' } + } + } + }) + + const { rerender, unmount } = render(<>) + + await act(() => { + rerender(<>) + }) + + assert(hasComponent(entity, PostProcessingComponent)) + + const postProcessingComponent = getMutableComponent(entity, PostProcessingComponent) + postProcessingComponent.effects[effectKey]['isActive'].set(true) + + await act(() => { + rerender(<>) + }) + + // @ts-ignore + let effects = getComponent(rootEntity, RendererComponent).effectComposer.EffectPass.effects + assert( + effects.find((el) => el.name == effectKey), + ' Effect turned on' + ) + + postProcessingComponent.effects[effectKey]['isActive'].set(false) + + await act(() => { + rerender(<>) + }) + + // @ts-ignore + effects = getComponent(rootEntity, RendererComponent).effectComposer.EffectPass.effects + assert(!effects.find((el) => el.name == effectKey), ' Effect turned off') + + removeEntity(entity) + unmount() + }) +}) + +const effectKey = 'NoiseEffect' +export const NoiseEffectProcessReactor: React.FC = (props: { + isActive + rendererEntity: Entity + effectData + effects +}) => { + const { isActive, rendererEntity, effectData, effects } = props + const effectState = getState(PostProcessingEffectState) + + useEffect(() => { + if (effectData[effectKey].value) return + effectData[effectKey].set(effectState[effectKey].defaultValues) + }, []) + + useEffect(() => { + if (!isActive?.value) { + if (effects[effectKey].value) effects[effectKey].set(none) + return + } + const eff = new NoiseEffect(effectData[effectKey].value) + effects[effectKey].set(eff) + return () => { + effects[effectKey].set(none) + } + }, [isActive]) + + return null +} diff --git a/packages/spatial/src/renderer/components/PostProcessingComponent.tsx b/packages/spatial/src/renderer/components/PostProcessingComponent.tsx old mode 100755 new mode 100644 index e5ea82faa5..2a04e642e0 --- a/packages/spatial/src/renderer/components/PostProcessingComponent.tsx +++ b/packages/spatial/src/renderer/components/PostProcessingComponent.tsx @@ -23,15 +23,40 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import { useEffect } from 'react' - -import { defineComponent, useComponent, useEntityContext } from '@etherealengine/ecs' -import { NO_PROXY_STEALTH } from '@etherealengine/hyperflux' - -import { defaultPostProcessingSchema, EffectPropsSchema } from '../effects/PostProcessing' -import { configureEffectComposer } from '../functions/configureEffectComposer' +import { Entity, defineComponent, useComponent, useEntityContext } from '@etherealengine/ecs' +import { ErrorBoundary, NO_PROXY, getState, useHookstate } from '@etherealengine/hyperflux' +import { + EdgeDetectionMode, + Effect, + EffectComposer, + EffectPass, + OutlineEffect, + RenderPass, + SMAAEffect +} from 'postprocessing' +import React, { Suspense, useEffect } from 'react' +import { ArrayCamera, Scene, WebGLRenderer } from 'three' +import { CameraComponent } from '../../camera/components/CameraComponent' +import { HighlightState } from '../HighlightState' +import { RendererState } from '../RendererState' +import { RenderSettingsState, RendererComponent } from '../WebGLRendererSystem' +import { ObjectLayers } from '../constants/ObjectLayers' +import { PostProcessingEffectState } from '../effects/EffectRegistry' import { useScene } from './SceneComponents' +declare module 'postprocessing' { + interface EffectComposer { + // passes + EffectPass: EffectPass + // effects + SMAAEffect: SMAAEffect + OutlineEffect: OutlineEffect + } + interface Effect { + isActive: boolean + } +} + export const PostProcessingComponent = defineComponent({ name: 'PostProcessingComponent', jsonID: 'EE_postprocessing', @@ -39,13 +64,12 @@ export const PostProcessingComponent = defineComponent({ onInit(entity) { return { enabled: false, - effects: defaultPostProcessingSchema + effects: {} as Record // effect name, parameters } }, onSet: (entity, component, json) => { if (!json) return - if (typeof json.enabled === 'boolean') component.enabled.set(json.enabled) if (typeof json.effects === 'object') component.merge({ effects: json.effects }) }, @@ -61,18 +85,88 @@ export const PostProcessingComponent = defineComponent({ reactor: () => { const entity = useEntityContext() const rendererEntity = useScene(entity) - const postprocessingComponent = useComponent(entity, PostProcessingComponent) - - useEffect(() => { - if (!rendererEntity) return - configureEffectComposer( - rendererEntity, - postprocessingComponent.enabled.value - ? (postprocessingComponent.effects.get(NO_PROXY_STEALTH) as EffectPropsSchema) - : undefined - ) - }, [rendererEntity, postprocessingComponent.enabled, postprocessingComponent.effects]) - - return null + + if (!rendererEntity) return null + + return } }) + +const PostProcessingReactor = (props: { entity: Entity; rendererEntity: Entity }) => { + const { entity, rendererEntity } = props + const postProcessingComponent = useComponent(entity, PostProcessingComponent) + const EffectRegistry = getState(PostProcessingEffectState) + const effects = useHookstate>({}) + const renderer = useComponent(rendererEntity, RendererComponent) + const renderSettings = getState(RendererState) + const camera = useComponent(rendererEntity, CameraComponent) + const scene = new Scene() + const composer = new EffectComposer(renderer.value.renderer as WebGLRenderer) + + useEffect(() => { + renderer.effectComposer.set(composer) + const renderPass = new RenderPass() + renderer.value.effectComposer.addPass(renderPass) + renderer.renderPass.set(renderPass) + }, []) + + useEffect(() => { + const effectsVal = effects.get(NO_PROXY) as Record + + if (renderSettings.usePostProcessing && postProcessingComponent.enabled.value) { + for (const key in effectsVal) { + const val = effectsVal[key] + renderer.value.effectComposer[key] = val + } + } else { + renderer.value.effectComposer.removePass(renderer.value.effectComposer.EffectPass as EffectPass) + } + + //always have the smaa effect + const smaaPreset = getState(RenderSettingsState).smaaPreset + const smaaEffect = new SMAAEffect({ + preset: smaaPreset, + edgeDetectionMode: EdgeDetectionMode.COLOR + }) + effectsVal['SMAAEffect'] = smaaEffect + renderer.effectComposer['SMAAEffect'].set(smaaEffect) + + // //always have the outline effect for the highlight selection + const outlineEffect = new OutlineEffect(scene as Scene, camera.value as ArrayCamera, getState(HighlightState)) + outlineEffect.selectionLayer = ObjectLayers.HighlightEffect + effectsVal['OutlineEffect'] = outlineEffect + renderer.effectComposer['OutlineEffect'].set(outlineEffect) + + if (renderer.value.effectComposer.EffectPass) { + renderer.value.effectComposer.removePass(renderer.value.effectComposer.EffectPass as EffectPass) + } + + const effectArray = Object.values(effectsVal) + renderer.effectComposer.EffectPass.set(new EffectPass(camera.value as ArrayCamera, ...effectArray)) + renderer.value.effectComposer.addPass(renderer.value.effectComposer.EffectPass as EffectPass) + }, [effects, postProcessingComponent.enabled]) + + // for each effect specified in our postProcessingComponent, we mount a sub-reactor based on the effect registry for that effect ID + return ( + <> + {Object.keys(EffectRegistry).map((key) => { + const effect = EffectRegistry[key] // get effect registry entry + if (!effect) return null + return ( + + + + + + ) + })} + + ) +} diff --git a/packages/spatial/src/renderer/effects/EffectRegistry.ts b/packages/spatial/src/renderer/effects/EffectRegistry.ts new file mode 100644 index 0000000000..135a19094f --- /dev/null +++ b/packages/spatial/src/renderer/effects/EffectRegistry.ts @@ -0,0 +1,66 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import { Entity } from '@etherealengine/ecs' +import { State, defineState } from '@etherealengine/hyperflux' +import { EffectComposer } from 'postprocessing' +import React from 'react' +import { Scene } from 'three' + +export type EffectReactorProps = { + isActive: State + rendererEntity: Entity + effectData: any + effects: any + composer: EffectComposer + scene: Scene +} + +/** Interface for dynamic effect Registry + * @param reactor reactor for effect + * @param defaultValues specifies the default values for the effect adhering to schema + * @param schema specifies a schema for the editor to generate UI for each effect. (@todo Eventually can generate from default values) + * @example + * { + reactor: ChromaticAberrationEffectProcessReactor, + defaultValues: { + hue: 1, + saturation: 1 + }, + schema: { + hue: { propertyType: PropertyTypes.Number, name: 'Hue', min: -1, max: 1, step: 0.01 }, + saturation: { propertyType: PropertyTypes.Number, name: 'Saturation', min: -1, max: 1, step: 0.01 } + } + } + */ +export interface EffectRegistryEntry { + reactor: React.FC + defaultValues: any + schema: any +} + +export const PostProcessingEffectState = defineState({ + name: 'PostProcessingEffectState', + initial: {} as Record +}) diff --git a/packages/spatial/src/renderer/effects/PostProcessing.ts b/packages/spatial/src/renderer/effects/PostProcessing.ts deleted file mode 100644 index 43400e621a..0000000000 --- a/packages/spatial/src/renderer/effects/PostProcessing.ts +++ /dev/null @@ -1,716 +0,0 @@ -/* -CPAL-1.0 License - -The contents of this file are subject to the Common Public Attribution License -Version 1.0. (the "License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at -https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. -The License is based on the Mozilla Public License Version 1.1, but Sections 14 -and 15 have been added to cover use of software over a computer network and -provide for limited attribution for the Original Developer. In addition, -Exhibit A has been modified to be consistent with Exhibit B. - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the -specific language governing rights and limitations under the License. - -The Original Code is Ethereal Engine. - -The Original Developer is the Initial Developer. The Initial Developer of the -Original Code is the Ethereal Engine team. - -All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 -Ethereal Engine. All Rights Reserved. -*/ - -import { - BlendFunction, - BloomEffect, - BloomEffectOptions, - BrightnessContrastEffect, - ColorDepthEffect, - DepthOfFieldEffect, - EdgeDetectionMode, - HueSaturationEffect, - KernelSize, - OutlineEffect, - PredicationMode, - Resolution, - SMAAEffect, - SMAAPreset, - SSAOEffect, - ToneMappingEffect, - ToneMappingMode, - VignetteEffect, - VignetteTechnique -} from 'postprocessing' -import { Color, ColorSpace, Texture, TextureEncoding, Vector2 } from 'three' - -import { LinearTosRGBEffect } from '../../renderer/effects/LinearTosRGBEffect' - -export const Effects = { - SMAAEffect: 'SMAAEffect' as const, - OutlineEffect: 'OutlineEffect' as const, - SSAOEffect: 'SSAOEffect' as const, - SSREffect: 'SSREffect' as const, - DepthOfFieldEffect: 'DepthOfFieldEffect' as const, - BloomEffect: 'BloomEffect' as const, - ToneMappingEffect: 'ToneMappingEffect' as const, - BrightnessContrastEffect: 'BrightnessContrastEffect' as const, - HueSaturationEffect: 'HueSaturationEffect' as const, - ColorDepthEffect: 'ColorDepthEffect' as const, - LinearTosRGBEffect: 'LinearTosRGBEffect' as const, - SSGIEffect: 'SSGIEffect' as const, - TRAAEffect: 'TRAAEffect' as const, - // ChromaticAberrationEffect: 'ChromaticAberrationEffect' as const, - MotionBlurEffect: 'MotionBlurEffect' as const, - // ColorAverageEffect: 'ColorAverageEffect' as const, - // DotScreenEffect: 'DotScreenEffect' as const, - // TiltShiftEffect: 'TiltShiftEffect' as const, - // GlitchEffect: 'GlitchEffect' as const, - // GodRaysEffect: 'GodRaysEffect' as const, - // GridEffect: 'GridEffect' as const, - // LUT1DEffect: 'LUT1DEffect' as const, - // LUT3DEffect: 'LUT3DEffect' as const, - // NoiseEffect: 'NoiseEffect' as const, - // PixelationEffect: 'PixelationEffect' as const, - // ScanlineEffect: 'ScanlineEffect' as const, - // ShockWaveEffect: 'ShockWaveEffect' as const, - // FXAAEffect: 'FXAAEffect' as const, - // TextureEffect: 'TextureEffect' as const, - VignetteEffect: 'VignetteEffect' as const - // LensDistortionEffect: 'LensDistortionEffect' as const -} - -export const EffectMap = { - [Effects.SMAAEffect]: SMAAEffect, - [Effects.OutlineEffect]: OutlineEffect, - [Effects.SSAOEffect]: SSAOEffect, - [Effects.DepthOfFieldEffect]: DepthOfFieldEffect, - [Effects.BloomEffect]: BloomEffect, - [Effects.ToneMappingEffect]: ToneMappingEffect, - [Effects.BrightnessContrastEffect]: BrightnessContrastEffect, - [Effects.HueSaturationEffect]: HueSaturationEffect, - [Effects.ColorDepthEffect]: ColorDepthEffect, - [Effects.LinearTosRGBEffect]: LinearTosRGBEffect, - [Effects.VignetteEffect]: VignetteEffect -} - -declare module 'postprocessing' { - interface EffectComposer { - // passes - EffectPass: EffectPass - // effects - SMAAEffect: SMAAEffect - OutlineEffect: OutlineEffect - SSAOEffect: SSAOEffect - DepthOfFieldEffect: DepthOfFieldEffect - BloomEffect: BloomEffect - ToneMappingEffect: ToneMappingEffect - BrightnessContrastEffect: BrightnessContrastEffect - HueSaturationEffect: HueSaturationEffect - ColorDepthEffect: ColorDepthEffect - LinearTosRGBEffect: LinearTosRGBEffect - VignetteEffect: VignetteEffect - } -} - -export type EffectMapType = (typeof EffectMap)[keyof typeof EffectMap] - -export type EffectProps = { - isActive: boolean -} - -export type SMAAEffectProps = EffectProps & { - preset?: SMAAPreset - edgeDetectionMode?: EdgeDetectionMode - predicationMode?: PredicationMode -} - -export type OutlineEffectProps = EffectProps & { - blendFunction?: BlendFunction - patternTexture?: Texture - patternScale?: number - edgeStrength?: number - pulseSpeed?: number - visibleEdgeColor?: number - hiddenEdgeColor?: number - multisampling?: number - resolutionScale?: number - resolutionX?: number - resolutionY?: number - width?: number - height?: number - kernelSize?: KernelSize - blur?: boolean - xRay?: boolean -} - -export type SSAOEffectProps = EffectProps & { - blendFunction?: BlendFunction - distanceScaling?: boolean - depthAwareUpsampling?: boolean - normalDepthBuffer?: Texture - samples?: number - rings?: number - worldDistanceThreshold?: number - worldDistanceFalloff?: number - worldProximityThreshold?: number - worldProximityFalloff?: number - distanceThreshold?: number - distanceFalloff?: number - rangeThreshold?: number - rangeFalloff?: number - minRadiusScale?: number - luminanceInfluence?: number - radius?: number - intensity?: number - bias?: number - fade?: number - color?: Color - resolutionScale?: number - resolutionX?: number - resolutionY?: number - width?: number - height?: number - blur?: boolean - kernelSize?: KernelSize -} - -const defaultSSROptions = { - distance: 10, - thickness: 10, - denoiseIterations: 1, - denoiseKernel: 2, - denoiseDiffuse: 10, - denoiseSpecular: 10, - radius: 3, - phi: 0.5, - lumaPhi: 5, - depthPhi: 2, - normalPhi: 50, - roughnessPhi: 50, - specularPhi: 50, - envBlur: 0.5, - importanceSampling: true, - steps: 20, - refineSteps: 5, - resolutionScale: 1, - missedRays: false -} - -export type SSREffectProps = EffectProps & typeof defaultSSROptions - -export type DepthOfFieldEffectProps = EffectProps & { - blendFunction?: BlendFunction - focusDistance?: number - focalLength?: number - focusRange?: number - bokehScale?: number - resolutionScale?: number - resolutionX?: number - resolutionY?: number -} - -export type BloomEffectProps = EffectProps & BloomEffectOptions - -export type ToneMappingEffectProps = EffectProps & { - blendFunction?: BlendFunction - adaptive?: boolean - mode?: ToneMappingMode - resolution?: number - maxLuminance?: number - whitePoint?: number - middleGrey?: number - minLuminance?: number - averageLuminance?: number - adaptationRate?: number -} - -export type BrightnessContrastEffectProps = EffectProps & { - blendFunction?: BlendFunction - brightness?: number - contrast?: number -} - -export type HueSaturationEffectProps = EffectProps & { - blendFunction?: BlendFunction - hue?: number - saturation?: number -} - -export type ColorDepthEffectProps = EffectProps & { - blendFunction?: BlendFunction - bits?: number -} - -export type LinearTosRGBEffectProps = EffectProps & { blendFunction?: BlendFunction } - -export type SSGIEffectProps = EffectProps & { - distance: number - thickness: number - denoiseIterations: number - denoiseKernel: number - denoiseDiffuse: number - denoiseSpecular: number - radius: number - phi: number - lumaPhi: number - depthPhi: number - normalPhi: number - roughnessPhi: number - specularPhi: number - envBlur: number - importanceSampling: boolean - steps: number - refineSteps: number - resolutionScale: number - missedRays: boolean -} - -export type TRAAEffectProps = EffectProps & { - blend: number - constantBlend: boolean - dilation: boolean - blockySampling: boolean - logTransform: boolean - depthDistance: number - worldDistance: number - neighborhoodClamping: boolean -} - -export type MotionBlurEffectProps = EffectProps & { - intensity: 1 - jitter: 1 - samples: 16 -} - -export type ChromaticAberrationEffectProps = EffectProps & { - blendFunction?: BlendFunction - offset?: Vector2 - radialModulation: boolean - modulationOffset: number -} -export type ColorAverageEffectProps = EffectProps & { - blendFunction?: BlendFunction - bits?: number -} -export type DotScreenEffectProps = EffectProps & { - blendFunction?: BlendFunction - angle?: number - scale?: number -} -export type TiltShiftEffectProps = EffectProps & { - blendFunction?: BlendFunction - offset?: number - rotation?: number - focusArea?: number - feather?: number - bias?: number - kernelSize?: KernelSize - resolutionScale?: number - resolutionX?: number - resolutionY?: number -} -export type GlitchEffectProps = EffectProps & { - blendFunction?: BlendFunction - chromaticAberrationOffset?: Vector2 - delay?: Vector2 - duration?: Vector2 - strength?: Vector2 - perturbationMap?: Texture - dtSize?: number - columns?: number - ratio?: number -} -export type GodRaysEffectProps = EffectProps & { - blendFunction?: BlendFunction - samples?: number - density?: number - decay?: number - weight?: number - exposure?: number - clampMax?: number - resolutionScale?: number - resolutionX?: number - resolutionY?: number - width?: number - height?: number - kernelSize?: KernelSize - blur?: boolean -} -export type GridEffectProps = EffectProps & { - blendFunction?: BlendFunction - scale?: number - lineWidth?: number -} -export type LUT1DEffectProps = EffectProps & { blendFunction?: BlendFunction } -export type LUT3DEffectProps = EffectProps & { - blendFunction?: BlendFunction - tetrahedralInterpolation?: boolean - inputEncoding?: TextureEncoding - inputColorSpace?: ColorSpace -} -export type NoiseEffectProps = EffectProps & { - blendFunction?: BlendFunction - premultiply?: boolean -} -export type PixelationEffectProps = EffectProps & { granularity?: number } -export type ScanlineEffectProps = EffectProps & { - blendFunction?: BlendFunction - density?: number -} -export type ShockWaveEffectProps = EffectProps & { - speed?: number - maxRadius?: number - waveSize?: number - amplitude?: number -} -export type FXAAEffectProps = EffectProps & { blendFunction?: BlendFunction } -export type TextureEffectProps = EffectProps & { - blendFunction?: BlendFunction - texture?: Texture - aspectCorrection?: boolean -} -export type VignetteEffectProps = EffectProps & { - blendFunction?: BlendFunction - technique?: VignetteTechnique - eskil?: boolean - offset?: number - darkness?: number -} -export type LensDistortionEffectProps = EffectProps & { - distortion: Vector2 - principalPoint: Vector2 - focalLength: Vector2 - skew?: number -} - -export type EffectPropsSchema = { - // [Effects.SMAAEffect]: SMAAEffectProps - // [Effects.OutlineEffect]: OutlineEffectProps - [Effects.SSAOEffect]: SSAOEffectProps - [Effects.SSREffect]: SSREffectProps - [Effects.DepthOfFieldEffect]: DepthOfFieldEffectProps - [Effects.BloomEffect]: BloomEffectProps - [Effects.ToneMappingEffect]: ToneMappingEffectProps - [Effects.BrightnessContrastEffect]: BrightnessContrastEffectProps - [Effects.HueSaturationEffect]: HueSaturationEffectProps - [Effects.ColorDepthEffect]: ColorDepthEffectProps - [Effects.LinearTosRGBEffect]: LinearTosRGBEffectProps - [Effects.SSGIEffect]: SSGIEffectProps - [Effects.TRAAEffect]: TRAAEffectProps - [Effects.MotionBlurEffect]: MotionBlurEffectProps - // [Effects.ChromaticAberrationEffect]: ChromaticAberrationEffectProps - // [Effects.ColorAverageEffect]: ColorAverageEffectProps - // [Effects.DotScreenEffect]: DotScreenEffectProps - // [Effects.TiltShiftEffect]: TiltShiftEffectProps - // [Effects.GlitchEffect]: GlitchEffectProps - // [Effects.GodRaysEffect]: GodRaysEffectProps - // [Effects.GridEffect]: GridEffectProps - // [Effects.LUT1DEffect]: LUT1DEffectProps - // [Effects.LUT3DEffect]: LUT3DEffectProps - // [Effects.NoiseEffect]: NoiseEffectProps - // [Effects.PixelationEffect]: PixelationEffectProps - // [Effects.ScanlineEffect]: ScanlineEffectProps - // [Effects.ShockWaveEffect]: ShockWaveEffectProps - // [Effects.FXAAEffect]: FXAAEffectProps - // [Effects.TextureEffect]: TextureEffectProps - [Effects.VignetteEffect]: VignetteEffectProps - // [Effects.LensDistortionEffect]: LensDistortionEffectProps -} - -export type EffectPropsSchemaType = (typeof defaultPostProcessingSchema)[keyof typeof defaultPostProcessingSchema] - -export const defaultPostProcessingSchema: EffectPropsSchema = { - // [Effects.SMAAEffect]: { - // isActive: false, - // preset: SMAAPreset.MEDIUM, - // edgeDetectionMode: EdgeDetectionMode.COLOR, - // predicationMode: PredicationMode.DISABLED - // }, - // [Effects.OutlineEffect]: { - // isActive: false, - // blendFunction: BlendFunction.SCREEN, - // patternScale: 1.0, - // edgeStrength: 1.0, - // pulseSpeed: 0.0, - // visibleEdgeColor: 0xffffff, - // hiddenEdgeColor: 0x22090a, - // multisampling: 0, - // resolutionScale: 0.5, - // resolutionX: Resolution.AUTO_SIZE, - // resolutionY: Resolution.AUTO_SIZE, - // width: Resolution.AUTO_SIZE, - // height: 480, - // kernelSize: KernelSize.VERY_SMALL, - // blur: false, - // xRay: true - // }, - [Effects.SSREffect]: { - isActive: false, - ...defaultSSROptions - }, - [Effects.SSGIEffect]: { - isActive: false, - distance: 10, - thickness: 10, - denoiseIterations: 1, - denoiseKernel: 2, - denoiseDiffuse: 10, - denoiseSpecular: 10, - radius: 3, - phi: 0.5, - lumaPhi: 5, - depthPhi: 2, - normalPhi: 50, - roughnessPhi: 50, - specularPhi: 50, - envBlur: 0.5, - importanceSampling: true, - steps: 20, - refineSteps: 5, - resolutionScale: 1, - missedRays: false - }, - [Effects.SSAOEffect]: { - isActive: false, - blendFunction: BlendFunction.MULTIPLY, - distanceScaling: true, - depthAwareUpsampling: true, - normalDepthBuffer: undefined, - samples: 9, - rings: 7, - // worldDistanceThreshold: 0.97, - // worldDistanceFalloff: 0.03, - // worldProximityThreshold: 0.0005, - // worldProximityFalloff: 0.001, - distanceThreshold: 0.125, // Render up to a distance of ~20 world units - distanceFalloff: 0.02, // with an additional ~2.5 units of falloff. - rangeThreshold: 0.0005, - rangeFalloff: 0.001, - minRadiusScale: 0.1, - luminanceInfluence: 0.7, - bias: 0.025, - radius: 0.1825, - intensity: 1.0, - fade: 0.01, - color: undefined, - resolutionScale: 1.0, - resolutionX: Resolution.AUTO_SIZE, - resolutionY: Resolution.AUTO_SIZE, - width: Resolution.AUTO_SIZE, - height: Resolution.AUTO_SIZE, - kernelSize: KernelSize.SMALL, - blur: true - }, - [Effects.DepthOfFieldEffect]: { - isActive: false, - blendFunction: BlendFunction.NORMAL, - focusDistance: 0.1, - focalLength: 0.1, - focusRange: 0.1, - bokehScale: 1.0, - resolutionScale: 1.0, - resolutionX: Resolution.AUTO_SIZE, - resolutionY: Resolution.AUTO_SIZE - }, - [Effects.BloomEffect]: { - isActive: true, - blendFunction: BlendFunction.SCREEN, - kernelSize: KernelSize.LARGE, - luminanceThreshold: 0.9, - luminanceSmoothing: 0.025, - mipmapBlur: false, - intensity: 1.0, - radius: 0.85, - levels: 8 - }, - [Effects.ToneMappingEffect]: { - isActive: false, - blendFunction: BlendFunction.NORMAL, - adaptive: false, - mode: ToneMappingMode.ACES_FILMIC, - resolution: 256, - maxLuminance: 4.0, - whitePoint: 4.0, - middleGrey: 0.6, - minLuminance: 0.01, - averageLuminance: 1.0, - adaptationRate: 1.0 - }, - [Effects.BrightnessContrastEffect]: { - isActive: false, - blendFunction: BlendFunction.NORMAL, - brightness: 0.0, - contrast: 0.0 - }, - [Effects.HueSaturationEffect]: { - isActive: false, - blendFunction: BlendFunction.NORMAL, - hue: 0, - saturation: 0.0 - }, - [Effects.ColorDepthEffect]: { - isActive: false, - blendFunction: BlendFunction.NORMAL, - bits: 16 - }, - [Effects.LinearTosRGBEffect]: { - isActive: false - }, - [Effects.TRAAEffect]: { - isActive: false, - blend: 0.8, - constantBlend: true, - dilation: true, - blockySampling: false, - logTransform: false, // ! TODO: check if can use logTransform withoutt artifacts - depthDistance: 10, - worldDistance: 5, - neighborhoodClamping: true - }, - [Effects.MotionBlurEffect]: { - isActive: false, - intensity: 1, - jitter: 1, - samples: 16 - }, - // [Effects.ChromaticAberrationEffect]: { - // isActive: false, - // blendFunction: BlendFunction.NORMAL, - // offset: undefined, - // radialModulation: false, - // modulationOffset: 0.15 - // }, - // [Effects.ColorAverageEffect]: { - // isActive: false, - // blendFunction: BlendFunction.NORMAL - // }, - // [Effects.DotScreenEffect]: { isActive: false, blendFunction: BlendFunction.NORMAL, angle: 1.57, scale: 1.0 }, - // [Effects.TiltShiftEffect]: { - // isActive: false, - // blendFunction: BlendFunction.NORMAL, - // offset: 0.0, - // rotation: 0.0, - // focusArea: 0.4, - // feather: 0.3, - // bias: 0.06, - // kernelSize: KernelSize.MEDIUM, - // resolutionScale: 0.5, - // resolutionX: Resolution.AUTO_SIZE, - // resolutionY: Resolution.AUTO_SIZE - // }, - // [Effects.GlitchEffect]: { - // isActive: false, - // blendFunction: BlendFunction.NORMAL, - // chromaticAberrationOffset: undefined, - // delay: undefined, - // duration: undefined, - // strength: undefined, - // perturbationMap: undefined, - // dtSize: 64, - // columns: 0.05, - // ratio: 0.85 - // }, - // [Effects.GodRaysEffect]: { - // isActive: false, - // blendFunction: BlendFunction.SCREEN, - // samples: 60.0, - // density: 0.96, - // decay: 0.9, - // weight: 0.4, - // exposure: 0.6, - // clampMax: 1.0, - // resolutionScale: 0.5, - // resolutionX: Resolution.AUTO_SIZE, - // resolutionY: Resolution.AUTO_SIZE, - // width: Resolution.AUTO_SIZE, - // height: Resolution.AUTO_SIZE, - // kernelSize: KernelSize.SMALL, - // blur: true - // }, - // [Effects.GridEffect]: { - // isActive: false, - // blendFunction: BlendFunction.OVERLAY, - // scale: 1.0, - // lineWidth: 0.0 - // }, - // [Effects.LUT1DEffect]: { isActive: false, blendFunction: BlendFunction.SET }, - // [Effects.LUT3DEffect]: { isActive: false, blendFunction: BlendFunction.SET, tetrahedralInterpolation: false, inputEncoding: sRGBEncoding, inputColorSpace: SRGBColorSpace }, - // [Effects.NoiseEffect]: { isActive: false, blendFunction: BlendFunction.SCREEN, premultiply: false }, - // [Effects.PixelationEffect]: { isActive: false, granularity: 30 }, - // [Effects.ScanlineEffect]: { isActive: false, blendFunction: BlendFunction.OVERLAY, density: 1.25}, - // [Effects.ShockWaveEffect]: { - // isActive: false, - // speed: 2.0, - // maxRadius: 1.0, - // waveSize: 0.2, - // amplitude: 0.05, - // }, - // [Effects.FXAAEffect]: { isActive: false, blendFunction: BlendFunction.SRC }, - // [Effects.TextureEffect]: { - // isActive: false, - // blendFunction: BlendFunction.NORMAL, - // texture: undefined, - // aspectCorrection: false - // }, - [Effects.VignetteEffect]: { - isActive: false, - blendFunction: BlendFunction.NORMAL, - technique: VignetteTechnique.DEFAULT, - eskil: false, - offset: 0.5, - darkness: 0.5 - } - // [Effects.LensDistortionEffect]: { - // isActive: false, - // distortion: new Vector2(0,0), - // principalPoint: new Vector2(0,0), - // focalLength: new Vector2(0,0), - // skew: 0 - // } -} - -/** - * Based on Unity's post processing pipelines - * - https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.0/manual/rendering-excecution-order.html - * - https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@17.0/manual/EffectList.html - * - https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@17.0/manual - */ - -/** 1. input aliasing */ -// Effects.SMAAEffect, -// Effects.OutlineEffect, - -export const effectInOrder = [ - /** 2. world effects */ - // Effects.PaniniProjection, - Effects.DepthOfFieldEffect, - Effects.SSAOEffect, // TODO- add option to use HBAO - Effects.SSREffect, - Effects.SSGIEffect, - // Effects.GodRaysEffect, - - /** 3. camera effects */ - // Effects.LensDistortionEffect, - //Effects.LensFlareEffect, - // Effects.ChromaticAberrationEffect, - Effects.MotionBlurEffect, - Effects.BloomEffect, - Effects.VignetteEffect, - - /** 4. color grading */ - Effects.ToneMappingEffect, - // maybe replace with Shadows/Midtones/Highlights ? - Effects.BrightnessContrastEffect, - Effects.HueSaturationEffect, - Effects.ColorDepthEffect, - // Effects.LUT1DEffect, - // Effects.LUT3DEffect, - - /** 5. final fix, aliasing and noise passes */ - Effects.LinearTosRGBEffect, // should this just be always on? - Effects.TRAAEffect - // Effects.FXAAEffect -] diff --git a/packages/spatial/src/renderer/functions/configureEffectComposer.ts b/packages/spatial/src/renderer/functions/configureEffectComposer.ts deleted file mode 100644 index 34c6cca823..0000000000 --- a/packages/spatial/src/renderer/functions/configureEffectComposer.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* -CPAL-1.0 License - -The contents of this file are subject to the Common Public Attribution License -Version 1.0. (the "License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at -https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. -The License is based on the Mozilla Public License Version 1.1, but Sections 14 -and 15 have been added to cover use of software over a computer network and -provide for limited attribution for the Original Developer. In addition, -Exhibit A has been modified to be consistent with Exhibit B. - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the -specific language governing rights and limitations under the License. - -The Original Code is Ethereal Engine. - -The Original Developer is the Initial Developer. The Initial Developer of the -Original Code is the Ethereal Engine team. - -All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 -Ethereal Engine. All Rights Reserved. -*/ - -import { - BlendFunction, - DepthDownsamplingPass, - EdgeDetectionMode, - EffectComposer, - EffectPass, - OutlineEffect, - RenderPass, - SMAAEffect, - TextureEffect -} from 'postprocessing' -import { VelocityDepthNormalPass } from 'realism-effects' -import { Scene } from 'three' - -import { Entity, getComponent } from '@etherealengine/ecs' -import { getState } from '@etherealengine/hyperflux' - -import { CameraComponent } from '../../camera/components/CameraComponent' -import { EngineState } from '../../EngineState' -import { ObjectLayers } from '../../renderer/constants/ObjectLayers' -import { defaultPostProcessingSchema, EffectMap, Effects } from '../effects/PostProcessing' -import { HighlightState } from '../HighlightState' -import { CustomNormalPass } from '../passes/CustomNormalPass' -import { RendererState } from '../RendererState' -import { RendererComponent, RenderSettingsState } from '../WebGLRendererSystem' -import { changeRenderMode } from './changeRenderMode' - -export const configureEffectComposer = (entity: Entity, schema?: typeof defaultPostProcessingSchema): void => { - const renderer = getComponent(entity, RendererComponent) - const camera = getComponent(entity, CameraComponent) - if (!renderer || !camera) return - - const scene = new Scene() - - if (renderer.effectComposer) { - renderer.effectComposer.dispose() - renderer.renderPass = null! - } - - const composer = new EffectComposer(renderer.renderer) - renderer.effectComposer = composer - - // we always want to have at least the render pass enabled - const renderPass = new RenderPass() - renderer.effectComposer.addPass(renderPass) - renderer.renderPass = renderPass - - const renderSettings = getState(RendererState) - if (!renderSettings.usePostProcessing) return - - const effects: any[] = [] - - const smaaPreset = getState(RenderSettingsState).smaaPreset - const smaaEffect = new SMAAEffect({ - preset: smaaPreset, - edgeDetectionMode: EdgeDetectionMode.COLOR - }) - composer.SMAAEffect = smaaEffect - - const outlineEffect = new OutlineEffect(scene, camera, getState(HighlightState)) - outlineEffect.selectionLayer = ObjectLayers.HighlightEffect - composer.OutlineEffect = outlineEffect - - const SmaaEffectPass = new EffectPass(camera, smaaEffect) - const OutlineEffectPass = new EffectPass(camera, outlineEffect) - composer.addPass(OutlineEffectPass) //outlines don't follow camera - composer.addPass(SmaaEffectPass) - - const effectKeys = Object.keys(EffectMap) - - const normalPass = new CustomNormalPass(scene, camera) - - const depthDownsamplingPass = new DepthDownsamplingPass({ - normalBuffer: normalPass.texture, - resolutionScale: 0.5 - }) - - if (!schema) return - - const velocityDepthNormalPass = new VelocityDepthNormalPass(scene, camera) - let useVelocityDepthNormalPass = false - let useDepthDownsamplingPass = false - - for (const key of effectKeys) { - const effectOptions = schema[key] - - if (!effectOptions || !effectOptions.isActive) continue - const EffectClass = EffectMap[key] - - if (!EffectClass) continue - - if (key === Effects.SSAOEffect) { - const eff = new EffectClass(camera, normalPass.texture, { - ...effectOptions, - normalDepthBuffer: depthDownsamplingPass.texture - }) - useDepthDownsamplingPass = true - composer[key] = eff - effects.push(eff) - } else if (key === Effects.SSREffect || key === Effects.SSGIEffect) { - // SSR is just a mode of SSGI, and can't both be run at the same time - const eff = new EffectClass(composer, scene, camera, { ...effectOptions, velocityDepthNormalPass }) - useVelocityDepthNormalPass = true - composer[key] = eff - effects.push(eff) - } else if (key === Effects.DepthOfFieldEffect) { - const eff = new EffectClass(camera, effectOptions) - composer[key] = eff - effects.push(eff) - } else if (key === Effects.TRAAEffect) { - // todo support more than 1 texture - const textureCount = 1 - const eff = new EffectClass(scene, camera, velocityDepthNormalPass, textureCount, effectOptions) - useVelocityDepthNormalPass = true - composer[key] = eff - effects.push(eff) - } else if (key === Effects.MotionBlurEffect) { - const eff = new EffectClass(velocityDepthNormalPass, effectOptions) - useVelocityDepthNormalPass = true - composer[key] = eff - effects.push(eff) - } else { - const eff = new EffectClass(effectOptions) - composer[key] = eff - effects.push(eff) - } - } - if (effects.length) { - if (useVelocityDepthNormalPass) composer.addPass(velocityDepthNormalPass) - - if (useDepthDownsamplingPass) { - composer.addPass(normalPass) - composer.addPass(depthDownsamplingPass) - const textureEffect = new TextureEffect({ - blendFunction: BlendFunction.SKIP, - texture: depthDownsamplingPass.texture - }) - effects.push(textureEffect) - } - - composer.EffectPass = new EffectPass(camera, ...effects) - composer.addPass(composer.EffectPass) - } - if (getState(EngineState).isEditor) changeRenderMode() -}