Kaynağa Gözat

Add TonemapPlugin by default to ThreeViewer, add examples for hdr testing with and without rgbm.

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

+ 159
- 3
README.md Dosyayı Görüntüle

@@ -17,7 +17,7 @@ ThreePipe is a 3D framework built on top of [three.js](https://threejs.org/) in

Key features include:
- Simple, intuitive API for creating 3D model viewers/configurators/editors on web pages, with many built-in presets for common workflows and use-cases.
- Companion [editor](TODO) to create, edit and configure 3D scenes in the browser.
- Companion [editor](https://threepipe.org/examples/tweakpane-editor/) to create, edit and configure 3D scenes in the browser.
- Modular architecture that allows you to easily extend the viewer, scene objects, materials, shaders, rendering, post-processing and serialization with custom functionality.
- Simple plugin system along with a rich library of built-in plugins that allows you to easily add new features to the viewer.
- [uiconfig](https://github.com/repalash/uiconfig.js) compatibility to automatically generate configuration UIs in the browser.
@@ -107,7 +107,7 @@ Contributions to ThreePipe are welcome and encouraged! Feel free to open issues

## File Formats

ThreePipe Asset Manager supports the import of following file formats out of the box:
ThreePipe Asset Manager supports the import of the following file formats out of the box:
* Models:
* gltf, glb
* obj, mtl
@@ -124,7 +124,7 @@ ThreePipe Asset Manager supports the import of following file formats out of the
* zip
* txt

Additional formats can be added by plugins:
Plugins can add additional formats:
* Models
* 3dm - Using [Rhino3dmLoadPlugin](#Rhino3dmLoadPlugin)

@@ -132,6 +132,70 @@ Additional formats can be added by plugins:

ThreePipe has a simple plugin system that allows you to easily add new features to the viewer. Plugins can be added to the viewer using the `addPlugin` and `addPluginSync` methods. The plugin system is designed to be modular and extensible. Plugins can be added to the viewer at any time and can be removed using the `removePlugin` and `removePluginSync` methods.

### TonemapPlugin

todo: image

Example: https://threepipe.org/examples/#tonemap-plugin/

Source Code: [src/plugins/postprocessing/TonemapPlugin.ts](./src/plugins/postprocessing/TonemapPlugin.ts)

API Reference: [TonemapPlugin](https://threepipe.org/docs/classes/TonemapPlugin.html)

TonemapPlugin adds a post-processing material extension to the ScreenPass in render manager
that applies tonemapping to the color. The tonemapping operator can be changed
by setting the `toneMapping` property of the plugin. The default tonemapping operator is `ACESFilmicToneMapping`.

Other Tonemapping properties can be like `exposure`, `contrast` and `saturation`

TonemapPlugin is added by default in ThreeViewer unless `tonemap` is set to `false` in the options.

### DropzonePlugin

todo: image

Example: https://threepipe.org/examples/#dropzone-plugin/

Source Code: [src/plugins/interaction/DropzonePlugin.ts](./src/plugins/interaction/DropzonePlugin.ts)

API Reference: [DropzonePlugin](https://threepipe.org/docs/classes/DropzonePlugin.html)

DropzonePlugin adds support for drag and drop of local files to automatically import, process and load them into the viewer.

DropzonePlugin can be added by default in ThreeViewer
by setting the `dropzone` property to `true` or an object of `DropzonePluginOptions` in the options.

```typescript
import {DropzonePlugin, ThreeViewer} from 'threepipe'
const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
dropzone: true, // just set to true to enable drag drop functionatility in the viewer
})
```

To set custom options,
pass an object of [DropzonePluginOptions](https://threepipe.org/docs/interfaces/DropzonePluginOptions.html) type to the `dropzone` property.
```typescript
import {DropzonePlugin, ThreeViewer} from 'threepipe'
const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
dropzone: { // this can also be set to true and configured by getting a reference to the DropzonePlugin
allowedExtensions: ['gltf', 'glb', 'hdr', 'png', 'jpg', 'json', 'fbx', 'obj'], // only allow these file types. If undefined, all files are allowed.
addOptions: {
disposeSceneObjects: true, // auto dispose of old scene objects
autoSetEnvironment: true, // when hdr is dropped
autoSetBackground: true, // when any image is dropped
autoCenter: true, // auto center the object
autoScale: true, // auto scale according to radius
autoScaleRadius: 2,
license: 'Imported from dropzone', // Any license to set on imported objects
importConfig: true, // import config from file
},
// check more options in the DropzonePluginOptions interface
},
})
```

### DepthBufferPlugin

todo: image
@@ -140,6 +204,8 @@ Example: https://threepipe.org/examples/#depth-buffer-plugin/

Source Code: [src/plugins/pipeline/DepthBufferPlugin.ts](./src/plugins/pipeline/DepthBufferPlugin.ts)

API Reference: [DepthBufferPlugin](https://threepipe.org/docs/classes/DepthBufferPlugin.html)

Depth Buffer Plugin adds a pre-render pass to the render manager and renders a depth buffer to a target. The render target can be accessed by other plugins throughout the rendering pipeline to create effects like depth of field, SSAO, SSR, etc.

```typescript
@@ -164,6 +230,8 @@ Example: https://threepipe.org/examples/#normal-buffer-plugin/

Source Code: [src/plugins/pipeline/NormalBufferPlugin.ts](./src/plugins/pipeline/NormalBufferPlugin.ts)

API Reference: [NormalBufferPlugin](https://threepipe.org/docs/classes/NormalBufferPlugin.html)

Normal Buffer Plugin adds a pre-render pass to the render manager and renders a normal buffer to a target. The render target can be accessed by other plugins throughout the rendering pipeline to create effects like SSAO, SSR, etc.

Note: Use [`DepthNormalBufferPlugin`](#DepthNormalBufferPlugin) if using both `DepthBufferPlugin` and `NormalBufferPlugin` to render both depth and normal buffers in a single pass.
@@ -194,6 +262,8 @@ Example: https://threepipe.org/examples/#render-target-preview/

Source Code: [src/plugins/ui/RenderTargetPreviewPlugin.ts](./src/plugins/ui/RenderTargetPreviewPlugin.ts)

API Reference: [RenderTargetPreviewPlugin](https://threepipe.org/docs/classes/RenderTargetPreviewPlugin.html)

RenderTargetPreviewPlugin is a useful development and debugging plugin that renders any registered render-target to the screen in small collapsable panels.

```typescript
@@ -215,4 +285,90 @@ Example: https://threepipe.org/examples/#rhino3dm-load/

Source Code: [src/plugins/import/Rhino3dmLoadPlugin.ts](./src/plugins/import/Rhino3dmLoadPlugin.ts)

API Reference: [Rhino3dmLoadPlugin](https://threepipe.org/docs/classes/Rhino3dmLoadPlugin.html)

Adds support for loading .3dm files generated by [Rhino 3D](https://www.rhino3d.com/). This plugin includes some changes with how 3dm files are loaded in three.js. The changes are around loading layer and primitive properties when set as reference in the 3dm files.

## Additional Plugins

Additional plugins can be found in the [plugins](plugins/) directory.
These add support for integrating with other libraries, adding new features, and other functionality with different licenses.

### Tweakpane Ui plugin

todo: image

Example: https://threepipe.org/examples/#viewer-uiconfig/

Source Code: [plugins/tweakpane/src/TweakpaneUiPlugin.ts](plugins/tweakpane/src/TweakpaneUiPlugin.ts)

API Reference: [TweakpaneUiPlugin](https://threepipe.org/plugins/tweakpane/docs/classes/TweakpaneUiPlugin.html)

NPM: `npm install @threepipe/plugin-tweakpane`

CDN: https://threepipe.org/plugins/tweakpane/dist/index.mjs

TweakpaneUiPlugin adds support for using [uiconfig-tweakpane](https://github.com/repalash/uiconfig-tweakpane)
to create a configuration UI in applications using the [Tweakpane](https://cocopon.github.io/tweakpane/) library.

The plugin takes the [uiconfig](https://github.com/repalash/uiconfig.js)
that's defined in the viewer and all the objects to automatically render a UI in the browser.
```typescript
import {IObject3D, ThreeViewer, TonemapPlugin} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

const viewer = new ThreeViewer({...})

// Add the plugin
const plugin = viewer.addPluginSync(new TweakpaneUiPlugin(true)) // true to show expanded the UI by default

// Add the UI for the viewer
plugin.appendChild(viewer.uiConfig)
// Add UI for some plugins
plugin.setupPlugins(TonemapPlugin, DropzonePlugin)
```

### Tweakpane Editor Plugin

todo: image

Example: https://threepipe.org/examples/#tweakpane-editor/

Source Code: [plugins/tweakpane-editor/src/TweakpaneEditorPlugin.ts](plugins/tweakpane-editor/src/TweakpaneEditorPlugin.ts)

API Reference: [TweakpaneEditorPlugin](https://threepipe.org/plugins/tweakpane-editor/docs/classes/TweakpaneEditorPlugin.html)

NPM: `npm install @threepipe/plugin-tweakpane-editor`

CDN: https://threepipe.org/plugins/tweakpane-editor/dist/index.mjs

TweakpaneEditorPlugin uses TweakpaneUiPlugin to create an editor for editing viewer,
plugins, model and material configurations in the browser.

```typescript
import {IObject3D, ThreeViewer, TonemapPlugin} from 'threepipe'
import {TweakpaneEditorPlugin} from '@threepipe/plugin-tweakpane-editor'

const viewer = new ThreeViewer({...})

viewer.addPluginSync(new TweakpaneUiPlugin(true))
const editor = viewer.addPluginSync(new TweakpaneEditorPlugin())

// Add some plugins to the viewer
await viewer.addPlugins([
new ViewerUiConfigPlugin(),
// new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin
new DepthBufferPlugin(HalfFloatType, true, true),
new NormalBufferPlugin(HalfFloatType, false),
new RenderTargetPreviewPlugin(false),
])

// Load the plugin UI in the editor and tweakpane ui with categories.
editor.loadPlugins({
['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin],
['GBuffer']: [DepthBufferPlugin, NormalBufferPlugin],
['Post-processing']: [TonemapPlugin],
['Debug']: [RenderTargetPreviewPlugin],
})
```

+ 35
- 0
examples/half-float-hdr-test/index.html Dosyayı Görüntüle

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Half float HDR Test</title>
<!-- 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>

+ 48
- 0
examples/half-float-hdr-test/script.ts Dosyayı Görüntüle

@@ -0,0 +1,48 @@
import {_testFinish, BoxGeometry, Color, Mesh, PhysicalMaterial, ThreeViewer, TonemapPlugin} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
rgbm: false,
dropzone: {
allowedExtensions: ['gltf', 'glb', 'hdr'],
addOptions: {
disposeSceneObjects: true,
autoSetEnvironment: true, // when hdr is dropped
autoSetBackground: true,
},
},
})
viewer.scene.backgroundColor = new Color().set('black')
viewer.getPlugin(TonemapPlugin)!.exposure = 0.04
// viewer.renderManager.screenPass.outputColorSpace = LinearSRGBColorSpace

const box = new BoxGeometry(0.5, 0.5, 0.5)
const material = new PhysicalMaterial({
color: 'white',
emissive: 'white',
emissiveIntensity: 1,
})
const n = 5
for (let i = 0; i < n * n; i++) {
const mesh = new Mesh()
const mat = material.clone()
mat.emissiveIntensity = (1 - i / (n * n)) * 16
mesh.material = mat
mesh.geometry = box
mesh.position.x = Math.floor(n / 2) - Math.floor(i % n)
mesh.position.y = Math.floor(i / n) - Math.floor(n / 2)
mesh.position.multiplyScalar(0.5)
viewer.scene.addObject(mesh)
console.log(mat.emissiveIntensity)
}

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPluginUi(TonemapPlugin)
ui.appendChild(viewer.renderManager.screenPass.uiConfig)

}

init().then(_testFinish)

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

@@ -268,6 +268,8 @@
<h2 class="category">Tests</h2>
<ul>
<li><a href="./gltf-transmission-test/">GLTF Transmission Test </a></li>
<li><a href="./uint8-rgbm-hdr-test/">Uint8 RGBM HDR Test </a></li>
<li><a href="./half-float-hdr-test/">Half-float HDR Test </a></li>
<li><a href="./sphere-rgbm-test/">RGBM Test </a></li>
<li><a href="./sphere-half-float-test/">Half Float Test </a></li>
<li><a href="./sphere-msaa-test/">MSAA Test </a></li>

+ 1
- 1
examples/tonemap-plugin/script.ts Dosyayı Görüntüle

@@ -5,11 +5,11 @@ async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
tonemap: true, // this is true by default
})

// A GBuffer(depth buffer here) is required for the `tonemapBackground` flag in TonemapPlugin to work
viewer.addPluginSync(new DepthBufferPlugin(UnsignedByteType, true))
viewer.addPluginSync(new TonemapPlugin())

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')

+ 1
- 2
examples/tweakpane-editor/script.ts Dosyayı Görüntüle

@@ -33,11 +33,10 @@ async function init() {

await viewer.addPlugins([
new ViewerUiConfigPlugin(),
// new SceneUiConfigPlugin(),
// new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin
new DepthBufferPlugin(HalfFloatType, true, true),
new NormalBufferPlugin(HalfFloatType, false),
new RenderTargetPreviewPlugin(false),
new TonemapPlugin(),
])

const rt = viewer.getOrAddPluginSync(RenderTargetPreviewPlugin)

+ 35
- 0
examples/uint8-rgbm-hdr-test/index.html Dosyayı Görüntüle

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Uint8 RGBM HDR Test</title>
<!-- 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>

+ 48
- 0
examples/uint8-rgbm-hdr-test/script.ts Dosyayı Görüntüle

@@ -0,0 +1,48 @@
import {_testFinish, BoxGeometry, Color, Mesh, PhysicalMaterial, ThreeViewer, TonemapPlugin} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
rgbm: true,
dropzone: {
allowedExtensions: ['gltf', 'glb', 'hdr'],
addOptions: {
disposeSceneObjects: true,
autoSetEnvironment: true, // when hdr is dropped
autoSetBackground: true,
},
},
})
viewer.scene.backgroundColor = new Color().set('black')
viewer.getPlugin(TonemapPlugin)!.exposure = 0.04
// viewer.renderManager.screenPass.outputColorSpace = LinearSRGBColorSpace

const box = new BoxGeometry(0.5, 0.5, 0.5)
const material = new PhysicalMaterial({
color: 'white',
emissive: 'white',
emissiveIntensity: 1,
})
const n = 5
for (let i = 0; i < n * n; i++) {
const mesh = new Mesh()
const mat = material.clone()
mat.emissiveIntensity = (1 - i / (n * n)) * 16
mesh.material = mat
mesh.geometry = box
mesh.position.x = Math.floor(n / 2) - Math.floor(i % n)
mesh.position.y = Math.floor(i / n) - Math.floor(n / 2)
mesh.position.multiplyScalar(0.5)
viewer.scene.addObject(mesh)
console.log(mat.emissiveIntensity)
}

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPluginUi(TonemapPlugin)
ui.appendChild(viewer.renderManager.screenPass.uiConfig)

}

init().then(_testFinish)

+ 1
- 1
package.json Dosyayı Görüntüle

@@ -1,6 +1,6 @@
{
"name": "threepipe",
"version": "0.0.9",
"version": "0.0.10-dev.1",
"description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.",
"main": "src/index.ts",
"module": "dist/index.mjs",

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

@@ -10,7 +10,6 @@ import {
ReinhardToneMapping,
Shader,
ShaderChunk,
SRGBColorSpace,
ToneMapping,
Vector4,
WebGLRenderer,
@@ -31,6 +30,15 @@ export interface GBufferUpdater {
// eslint-disable-next-line @typescript-eslint/naming-convention
export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping

/**
* Tonemap Plugin
*
* Adds an extension to {@link ScreenPass} material
* for applying tonemapping on the final buffer before rendering to screen.
*
* Also adds support for Uncharted2 tone-mapping.
* @category Plugins
*/
@uiFolderContainer('Tonemapping')
export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExtension, GBufferUpdater {
static readonly PluginType = 'Tonemap'
@@ -120,14 +128,12 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte

onObjectRender(_: Object3D, material: IMaterial, renderer: WebGLRenderer): void {
if (!this.enabled) return
const {toneMapping, toneMappingExposure, outputColorSpace} = renderer
const {toneMapping, toneMappingExposure} = renderer
this._rendererState.toneMapping = toneMapping
this._rendererState.toneMappingExposure = toneMappingExposure
this._rendererState.outputColorSpace = outputColorSpace

renderer.toneMapping = this.toneMapping
renderer.toneMappingExposure = this.exposure
renderer.outputColorSpace = SRGBColorSpace
material.toneMapped = true
material.needsUpdate = true
}
@@ -135,7 +141,6 @@ export class TonemapPlugin extends AViewerPluginSync<''> implements MaterialExte
onAfterRender(_: Object3D, _1: IMaterial, renderer: WebGLRenderer): void {
renderer.toneMapping = this._rendererState.toneMapping
renderer.toneMappingExposure = this._rendererState.toneMappingExposure
renderer.outputColorSpace = this._rendererState.outputColorSpace
}

getUiConfig(): any {

+ 5
- 3
src/postprocessing/ScreenPass.ts Dosyayı Görüntüle

@@ -11,9 +11,9 @@ import {
import {ICamera, IRenderManager, IScene, IWebGLRenderer, ShaderMaterial2} from '../core'
import {CopyShader} from 'three/examples/jsm/shaders/CopyShader.js'
import {IPassID, IPipelinePass} from './Pass'
import {uiFolderContainer, uiToggle} from 'uiconfig.js'
import {uiDropdown, uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js'
import {ViewerRenderManager} from '../viewer'
import {matDefine} from '../three'
import {matDefine, threeConstMappings} from '../three'
import ScreenPassShader from './ScreenPass.glsl'
import {shaderReplaceString} from '../utils'

@@ -22,6 +22,7 @@ export type TViewerScreenShader = TViewerScreenShaderFrag | ShaderMaterialParame

@uiFolderContainer('Screen Pass')
export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'screen'> {
uiConfig!: UiObjectConfig
readonly passId = 'screen'
after: IPassID[] = ['render']
required: IPassID[] = ['render']
@@ -34,7 +35,8 @@ export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'scr
this.material.addEventListener('materialUpdate', this.setDirty)
}

outputColorSpace: ColorSpace = SRGBColorSpace
@uiDropdown('Output Color Space', threeConstMappings.ColorSpace.uiConfig, (t: ScreenPass)=>({onChange: t.setDirty}))
outputColorSpace: ColorSpace = SRGBColorSpace

private _lastReadBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget


+ 14
- 2
src/viewer/ThreeViewer.ts Dosyayı Görüntüle

@@ -42,6 +42,7 @@ import {IViewerPlugin, IViewerPluginSync} from './IViewerPlugin'
import {DropzonePlugin, DropzonePluginOptions} from '../plugins/interaction/DropzonePlugin'
import {uiConfig, uiFolderContainer, UiObjectConfig} from 'uiconfig.js'
import {IRenderTarget} from '../rendering'
import {TonemapPlugin} from '../plugins'

export type IViewerEvent = BaseEvent & {
type: 'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled'
@@ -102,6 +103,12 @@ export interface ThreeViewerOptions {

debug?: boolean

/**
* TonemapPlugin is added to the viewer if this is true.
* @default true
*/
tonemap?: boolean

/**
* Options for the asset manager.
*/
@@ -128,10 +135,12 @@ export interface ThreeViewerOptions {
useGBufferDepth?: boolean
}

const VIEWER_VERSION = '0.0.1'
const VIEWER_VERSION = '0.0.10-dev.1'

/**
* The ThreeViewer is the main class in the framework.
* Three Viewer
*
* The ThreeViewer is the main class in the framework to manage a scene, render and add plugins to it.
* @category Viewer
*/
@uiFolderContainer('Viewer')
@@ -306,6 +315,9 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
if (options.dropzone) {
this.addPluginSync(new DropzonePlugin(typeof options.dropzone === 'object' ? options.dropzone : undefined))
}
if (options.tonemap !== false) {
this.addPluginSync(new TonemapPlugin())
}

this.console.log('ThreePipe Viewer instance initialized, version: ', ThreeViewer.VERSION)


Loading…
İptal
Kaydet