| @@ -98,6 +98,9 @@ To make changes and run the example, click on the CodePen button on the top righ | |||
| - [RenderTargetPreviewPlugin](#rendertargetpreviewplugin) - Preview any render target in a UI panel over the canvas | |||
| - [GeometryUVPreviewPlugin](#geometryuvpreviewplugin) - Preview UVs of any geometry in a UI panel over the canvas | |||
| - [FrameFadePlugin](#framefadeplugin) - Post-render pass to smoothly fade to a new rendered frame over time | |||
| - [VignettePlugin](#vignetteplugin) - Add Vignette effect by patching the final screen pass | |||
| - [ChromaticAberrationPlugin](#chromaticaberrationplugin) - Add Chromatic Aberration effect by patching the final screen pass | |||
| - [FilmicGrainPlugin](#filmicgrainplugin) - Add Filmic Grain effect by patching the final screen pass | |||
| - [HDRiGroundPlugin](#hdrigroundplugin) - Add support for ground projected hdri/skybox to the webgl background shader. | |||
| - [Rhino3dmLoadPlugin](#rhino3dmloadplugin) - Add support for loading .3dm files | |||
| - [PLYLoadPlugin](#plyloadplugin) - Add support for loading .ply files | |||
| @@ -2429,6 +2432,55 @@ vignettePlugin.color = new Color(0.5, 0, 0) | |||
| // vignettePlugin.color.set('#ff0000'); vignettePlugin.setDirty() // Call setDirty to tell the plugin that color has changed | |||
| ``` | |||
| ## ChromaticAberrationPlugin | |||
| [//]: # (todo: image) | |||
| Example: https://threepipe.org/examples/#chromatic-aberration-plugin/ | |||
| Source Code: [src/plugins/postprocessing/ChromaticAberrationPlugin.ts](./src/plugins/postprocessing/ChromaticAberrationPlugin.ts) | |||
| API Reference: [ChromaticAberrationPlugin](https://threepipe.org/docs/classes/ChromaticAberrationPlugin.html) | |||
| ChromaticAberrationPlugin adds a post-processing material extension to the ScreenPass in render manager | |||
| that applies a chromatic-aberration effect to the final render. The parameter `intensity` can be changed to customize the effect. | |||
| ```typescript | |||
| import {ThreeViewer, ChromaticAberrationPlugin} from 'threepipe' | |||
| const viewer = new ThreeViewer({...}) | |||
| const chromaticAberrationPlugin = viewer.addPluginSync(ChromaticAberrationPlugin) | |||
| // Change the chromaticAberration color | |||
| chromaticAberrationPlugin.intensity = 0.5 | |||
| ``` | |||
| ## FilmicGrainPlugin | |||
| [//]: # (todo: image) | |||
| Example: https://threepipe.org/examples/#filmic-grain-plugin/ | |||
| Source Code: [src/plugins/postprocessing/FilmicGrainPlugin.ts](./src/plugins/postprocessing/FilmicGrainPlugin.ts) | |||
| API Reference: [FilmicGrainPlugin](https://threepipe.org/docs/classes/FilmicGrainPlugin.html) | |||
| FilmicGrainPlugin adds a post-processing material extension to the ScreenPass in render manager | |||
| that applies a filmic-grain effect to the final render. The parameters `power` and `color` can be changed to customize the effect. | |||
| ```typescript | |||
| import {ThreeViewer, FilmicGrainPlugin} from 'threepipe' | |||
| const viewer = new ThreeViewer({...}) | |||
| const filmicGrainPlugin = viewer.addPluginSync(FilmicGrainPlugin) | |||
| // Change the filmicGrain color | |||
| filmicGrainPlugin.intensity = 10 | |||
| filmicGrainPlugin.multiply = false | |||
| ``` | |||
| ## HDRiGroundPlugin | |||
| [//]: # (todo: image) | |||
| @@ -0,0 +1,36 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Chromatic Aberration Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,20 @@ | |||
| import {_testFinish, ChromaticAberrationPlugin, IObject3D, ThreeViewer} from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| }) | |||
| viewer.addPluginSync(ChromaticAberrationPlugin) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf') | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||
| ui.setupPluginUi(ChromaticAberrationPlugin) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -0,0 +1,36 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Filmic Grain Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,20 @@ | |||
| import {_testFinish, FilmicGrainPlugin, IObject3D, ThreeViewer} from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| }) | |||
| viewer.addPluginSync(FilmicGrainPlugin) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf') | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||
| ui.setupPluginUi(FilmicGrainPlugin) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -221,6 +221,8 @@ | |||
| <ul> | |||
| <li><a href="./tonemap-plugin/">Tonemap Plugin </a></li> | |||
| <li><a href="./vignette-plugin/">Vignette Plugin </a></li> | |||
| <li><a href="./chromatic-aberration-plugin/">Chromatic Aberration Plugin </a></li> | |||
| <li><a href="./filmic-grain-plugin/">Filmic Grain Plugin </a></li> | |||
| <li><a href="./frame-fade-plugin/">Frame Fade Plugin </a></li> | |||
| </ul> | |||
| <h2 class="category">Rendering</h2> | |||
| @@ -1,8 +1,10 @@ | |||
| import { | |||
| _testFinish, | |||
| CameraViewPlugin, | |||
| ChromaticAberrationPlugin, | |||
| DepthBufferPlugin, | |||
| DropzonePlugin, | |||
| FilmicGrainPlugin, | |||
| FrameFadePlugin, | |||
| FullScreenPlugin, | |||
| GLTFAnimationPlugin, | |||
| @@ -59,8 +61,10 @@ async function init() { | |||
| new NormalBufferPlugin(HalfFloatType, false), | |||
| new RenderTargetPreviewPlugin(false), | |||
| new FrameFadePlugin(), | |||
| new VignettePlugin(), | |||
| new HDRiGroundPlugin(false, true), | |||
| new VignettePlugin(false), | |||
| new ChromaticAberrationPlugin(false), | |||
| new FilmicGrainPlugin(false), | |||
| KTX2LoadPlugin, | |||
| KTXLoadPlugin, | |||
| PLYLoadPlugin, | |||
| @@ -7,7 +7,6 @@ async function init() { | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| }) | |||
| // A GBuffer(depth buffer here) is required for the `tonemapBackground` flag in TonemapPlugin to work | |||
| viewer.addPluginSync(VignettePlugin) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| @@ -2,6 +2,7 @@ import {onChange} from 'ts-browser-helpers' | |||
| import {Importer, Rhino3dmLoader2} from '../../assetmanager' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| import {IUiConfigContainer, uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js' | |||
| import {ThreeViewer} from '../../viewer' | |||
| /** | |||
| * Adds support for loading Rhino `.3dm`, `model/vnd.3dm`, `model/3dm` files and data uris. | |||
| @@ -18,25 +19,25 @@ export class Rhino3dmLoadPlugin extends BaseImporterPlugin implements IUiConfigC | |||
| * Same as {@link Rhino3dmLoader2.ImportMaterials} | |||
| */ | |||
| @onChange(Rhino3dmLoadPlugin.prototype._refresh) @uiToggle() | |||
| importMaterials = Rhino3dmLoader2.ImportMaterials | |||
| importMaterials = true | |||
| /** | |||
| * Force layer materials even if material/color source is not from layer. Only works if {@link importMaterials} is true | |||
| * Same as {@link Rhino3dmLoader2.ForceLayerMaterials} | |||
| */ | |||
| @onChange(Rhino3dmLoadPlugin.prototype._refresh) @uiToggle() | |||
| forceLayerMaterials = Rhino3dmLoader2.ForceLayerMaterials | |||
| forceLayerMaterials = false | |||
| /** | |||
| * Replace meshes with instanced meshes if they have the same parent, geometry and material | |||
| * Same as {@link Rhino3dmLoader2.ReplaceWithInstancedMesh} | |||
| */ | |||
| @onChange(Rhino3dmLoadPlugin.prototype._refresh) @uiToggle() | |||
| replaceWithInstancedMesh = Rhino3dmLoader2.ReplaceWithInstancedMesh | |||
| replaceWithInstancedMesh = false | |||
| /** | |||
| * Hide all lines, line segments and points in the file | |||
| * Same as {@link Rhino3dmLoader2.HideLineMesh} | |||
| */ | |||
| @onChange(Rhino3dmLoadPlugin.prototype._refresh) @uiToggle() | |||
| hideLineMesh = Rhino3dmLoader2.HideLineMesh | |||
| hideLineMesh = false | |||
| protected _refresh() { | |||
| Rhino3dmLoader2.ImportMaterials = this.importMaterials | |||
| @@ -45,4 +46,9 @@ export class Rhino3dmLoadPlugin extends BaseImporterPlugin implements IUiConfigC | |||
| Rhino3dmLoader2.HideLineMesh = this.hideLineMesh | |||
| } | |||
| onAdded(viewer: ThreeViewer) { | |||
| super.onAdded(viewer) | |||
| this._refresh() | |||
| } | |||
| } | |||
| @@ -34,6 +34,8 @@ export {KTX2LoadPlugin} from './import/KTX2LoadPlugin' | |||
| export {AScreenPassExtensionPlugin} from './postprocessing/AScreenPassExtensionPlugin' | |||
| export {TonemapPlugin} from './postprocessing/TonemapPlugin' | |||
| export {VignettePlugin} from './postprocessing/VignettePlugin' | |||
| export {ChromaticAberrationPlugin} from './postprocessing/ChromaticAberrationPlugin' | |||
| export {FilmicGrainPlugin} from './postprocessing/FilmicGrainPlugin' | |||
| // animation | |||
| export {GLTFAnimationPlugin} from './animation/GLTFAnimationPlugin' | |||
| @@ -0,0 +1,61 @@ | |||
| import {uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js' | |||
| import {glsl, onChange, serialize} from 'ts-browser-helpers' | |||
| import {uniform} from '../../three' | |||
| import ChromaticAberration from './shaders/ChromaticAberrationPlugin.glsl' | |||
| import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin' | |||
| /** | |||
| * Chromatic Aberration Plugin | |||
| * Adds an extension to {@link ScreenPass} material | |||
| * for applying chromatic aberration effect on the final buffer before rendering to screen. | |||
| * The intensity of the aberration can be controlled with the `intensity`(previously aberrationIntensity) property. | |||
| * | |||
| * @category Plugins | |||
| */ | |||
| @uiFolderContainer('ChromaticAberration') | |||
| export class ChromaticAberrationPlugin extends AScreenPassExtensionPlugin<''> { | |||
| static readonly PluginType = 'ChromaticAberration' | |||
| readonly extraUniforms = { | |||
| aberrationIntensity: {value: 1}, | |||
| } as const | |||
| @onChange(ChromaticAberrationPlugin.prototype.setDirty) | |||
| @uiToggle('Enable') | |||
| @serialize() enabled: boolean | |||
| @uiSlider('Intensity', [0., 0.3], 0.001) | |||
| @uniform({propKey: 'aberrationIntensity'}) | |||
| @serialize('aberrationIntensity') intensity = 0.5 | |||
| /** | |||
| * The priority of the material extension when applied to the material in ScreenPass | |||
| * set to very low priority, so applied at the end | |||
| */ | |||
| priority = -50 | |||
| parsFragmentSnippet = () => { | |||
| if (!this.enabled) return '' | |||
| return glsl` | |||
| uniform float aberrationIntensity; | |||
| ${ChromaticAberration} | |||
| ` | |||
| } | |||
| protected _shaderPatch = 'diffuseColor = ChromaticAberration(diffuseColor);' | |||
| get aberrationIntensity() { | |||
| console.warn('ChromaticAberrationPlugin.aberrationIntensity is deprecated, use ChromaticAberrationPlugin.intensity instead') | |||
| return this.intensity | |||
| } | |||
| set aberrationIntensity(v) { | |||
| console.warn('ChromaticAberrationPlugin.aberrationIntensity is deprecated, use ChromaticAberrationPlugin.intensity instead') | |||
| this.intensity = v | |||
| } | |||
| constructor(enabled = true) { | |||
| super() | |||
| this.enabled = enabled | |||
| } | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| import {uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js' | |||
| import {glsl, onChange, serialize} from 'ts-browser-helpers' | |||
| import {uniform} from '../../three' | |||
| import FilmicGrain from './shaders/FilmicGrainPlugin.glsl' | |||
| import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin' | |||
| /** | |||
| * Filmic Grain Plugin | |||
| * Adds an extension to {@link ScreenPass} material | |||
| * for applying filmic grain effect on the final buffer before rendering to screen. | |||
| * The intensity of the grain can be controlled with the `intensity` property | |||
| * and the `multiply` property can be used to multiply the grain effect on the image instead of adding. | |||
| * | |||
| * @category Plugins | |||
| */ | |||
| @uiFolderContainer('FilmicGrain') | |||
| export class FilmicGrainPlugin extends AScreenPassExtensionPlugin<''> { | |||
| static readonly PluginType = 'FilmicGrain' | |||
| readonly extraUniforms = { | |||
| grainIntensity: {value: 1}, | |||
| grainMultiply: {value: false}, | |||
| } as const | |||
| @onChange(FilmicGrainPlugin.prototype.setDirty) | |||
| @uiToggle('Enable') | |||
| @serialize() enabled: boolean | |||
| @uiSlider('Intensity', [0., 20], 0.01) | |||
| @uniform({propKey: 'grainIntensity'}) | |||
| @serialize('grainIntensity') intensity = 10 | |||
| @uiToggle('Multiply') | |||
| @uniform({propKey: 'grainMultiply'}) | |||
| @serialize('grainMultiply') multiply = false | |||
| /** | |||
| * The priority of the material extension when applied to the material in ScreenPass | |||
| * set to very low priority, so applied at the end | |||
| */ | |||
| priority = -50 | |||
| parsFragmentSnippet = () => { | |||
| if (!this.enabled) return '' | |||
| return glsl` | |||
| uniform float grainIntensity; | |||
| uniform bool grainMultiply; | |||
| ${FilmicGrain} | |||
| ` | |||
| } | |||
| protected _shaderPatch = 'diffuseColor = FilmicGrain(diffuseColor);' | |||
| get grainIntensity() { | |||
| console.warn('FilmicGrainPlugin.grainIntensity is deprecated, use FilmicGrainPlugin.intensity instead') | |||
| return this.intensity | |||
| } | |||
| set grainIntensity(v) { | |||
| console.warn('FilmicGrainPlugin.grainIntensity is deprecated, use FilmicGrainPlugin.intensity instead') | |||
| this.intensity = v | |||
| } | |||
| constructor(enabled = true) { | |||
| super() | |||
| this.enabled = enabled | |||
| } | |||
| } | |||
| @@ -26,7 +26,7 @@ export class VignettePlugin extends AScreenPassExtensionPlugin<''> { | |||
| @onChange(VignettePlugin.prototype.setDirty) | |||
| @uiToggle('Enable') | |||
| @serialize() enabled = false | |||
| @serialize() enabled: boolean | |||
| @uiSlider('Power', [0.1, 4], 0.01) | |||
| @uniform({propKey: 'power'}) | |||
| @@ -62,4 +62,10 @@ export class VignettePlugin extends AScreenPassExtensionPlugin<''> { | |||
| console.warn('VignettePlugin.bgcolor is deprecated, use VignettePlugin.color instead') | |||
| this.color = v | |||
| } | |||
| constructor(enabled = true) { | |||
| super() | |||
| this.enabled = enabled | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| vec4 ChromaticAberration(in vec4 color) { | |||
| vec2 distFromCenter = vUv - 0.5; | |||
| vec2 aberrated = aberrationIntensity * pow(distFromCenter, vec2(2.0)); | |||
| vec4 outColor = vec4( | |||
| tDiffuseTexelToLinear (texture2D(tDiffuse, vUv + aberrated)).r, | |||
| color.g, | |||
| tDiffuseTexelToLinear (texture2D(tDiffuse, vUv - aberrated)).b, | |||
| color.a | |||
| ); | |||
| return outColor; | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| // https://www.shadertoy.com/view/4sXSWs | |||
| vec4 FilmicGrain(in vec4 color) { | |||
| float x = (vUv.x + 4.0 ) * (vUv.y + 4.0 ) * ( 10.0); | |||
| vec4 grain = vec4(mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01)-0.005) * grainIntensity; | |||
| return vec4( | |||
| grainMultiply ? | |||
| (color.rgb * vec3(1.-grain)) : | |||
| (color.rgb + vec3(grain)), | |||
| color.a); | |||
| } | |||