| - [@threepipe/plugin-network](https://threepipe.org/package/plugin-network.html) - Network/Cloud related plugin implementations for Threepipe | - [@threepipe/plugin-network](https://threepipe.org/package/plugin-network.html) - Network/Cloud related plugin implementations for Threepipe | ||||
| - [@threepipe/plugin-svg-renderer](https://threepipe.org/package/plugin-svg-renderer.html) - Add support for exporting a 3d scene as SVG using [three-svg-renderer](https://www.npmjs.com/package/three-svg-renderer) | - [@threepipe/plugin-svg-renderer](https://threepipe.org/package/plugin-svg-renderer.html) - Add support for exporting a 3d scene as SVG using [three-svg-renderer](https://www.npmjs.com/package/three-svg-renderer) | ||||
| - [@threepipe/plugin-3d-tiles-renderer](https://threepipe.org/package/plugin-3d-tiles-renderer.html) - Plugins for [3d-tiles-renderer](https://github.com/NASA-AMMOS/3DTilesRendererJS), b3dm, i3dm, cmpt, pnts, dzi, slippy maps importers. | - [@threepipe/plugin-3d-tiles-renderer](https://threepipe.org/package/plugin-3d-tiles-renderer.html) - Plugins for [3d-tiles-renderer](https://github.com/NASA-AMMOS/3DTilesRendererJS), b3dm, i3dm, cmpt, pnts, dzi, slippy maps importers. | ||||
| - [@threepipe/plugin-path-tracing](https://threepipe.org/package/plugin-path-tracing.html) - Plugins for [path-tracing](https://en.wikipedia.org/wiki/Path_tracing). Using [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) | |||||
| - [@threepipe/plugin-assimpjs](https://threepipe.org/package/plugin-assimpjs.html) - Plugin and helpers to load and use [assimpjs](https://github.com/kovacsv/assimpjs) (with fbx, other exporters) in the browser. | - [@threepipe/plugin-assimpjs](https://threepipe.org/package/plugin-assimpjs.html) - Plugin and helpers to load and use [assimpjs](https://github.com/kovacsv/assimpjs) (with fbx, other exporters) in the browser. | ||||
| ## Documentation | ## Documentation |
| <li><a href="./virtual-camera/">Virtual Camera (Animated) </a></li> | <li><a href="./virtual-camera/">Virtual Camera (Animated) </a></li> | ||||
| <li><a href="./basic-svg-renderer-plugin/">Basic SVG Renderer Plugin </a></li> | <li><a href="./basic-svg-renderer-plugin/">Basic SVG Renderer Plugin </a></li> | ||||
| <li><a href="./three-svg-renderer-plugin/">Three SVG Renderer Plugin </a></li> | <li><a href="./three-svg-renderer-plugin/">Three SVG Renderer Plugin </a></li> | ||||
| <li><a href="./three-gpu-pathtracer/">Three GPU Path Tracer </a></li> | |||||
| <li><a href="./3d-tiles-renderer/">3D Tiles Renderer </a></li> | <li><a href="./3d-tiles-renderer/">3D Tiles Renderer </a></li> | ||||
| </ul> | </ul> | ||||
| <h2 class="category">Realistic Rendering (<a class="brand-link webgi-link" href="https://webgi.dev/" target="_blank">webgi</a>)</h2> | <h2 class="category">Realistic Rendering (<a class="brand-link webgi-link" href="https://webgi.dev/" target="_blank">webgi</a>)</h2> |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>Three GPU Path Tracer</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", | |||||
| "@threepipe/plugin-path-tracing": "./../../plugins/path-tracing/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/global-loading.mjs"></script> | |||||
| <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> |
| import { | |||||
| _testFinish, | |||||
| _testStart, | |||||
| BaseGroundPlugin, | |||||
| CanvasSnapshotPlugin, | |||||
| IObject3D, | |||||
| LoadingScreenPlugin, | |||||
| PickingPlugin, | |||||
| ProgressivePlugin, | |||||
| ThreeViewer, | |||||
| TonemapPlugin, | |||||
| } from 'threepipe' | |||||
| import {ThreeGpuPathTracerPlugin} from '@threepipe/plugin-path-tracing' | |||||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: false, | |||||
| debug: true, | |||||
| renderScale: 'auto', | |||||
| dropzone: { | |||||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr', 'json'], | |||||
| addOptions: { | |||||
| disposeSceneObjects: true, | |||||
| autoSetEnvironment: true, // when hdr is dropped | |||||
| autoSetBackground: true, | |||||
| }, | |||||
| }, | |||||
| plugins: [LoadingScreenPlugin, PickingPlugin, ProgressivePlugin, BaseGroundPlugin, CanvasSnapshotPlugin, ThreeGpuPathTracerPlugin], | |||||
| }) | |||||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr', {setBackground: true}) | |||||
| const modelUrl = 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf' | |||||
| const result = await viewer.load<IObject3D>(modelUrl, { | |||||
| autoCenter: true, | |||||
| autoScale: true, | |||||
| }) | |||||
| console.log(result) | |||||
| const ground = viewer.getPlugin(BaseGroundPlugin)?.material | |||||
| if (ground) { | |||||
| // make reflective | |||||
| ground.roughness = 0.1 | |||||
| ground.metalness = 0.9 | |||||
| ground.color.set(0xffffff) | |||||
| ground.setDirty() | |||||
| } | |||||
| // optional | |||||
| // const controls = viewer.scene.mainCamera.controls as EnvironmentControls2 | |||||
| // result && controls.setTilesRenderer(result.tilesRenderer) | |||||
| const ui = viewer.addPluginSync(TweakpaneUiPlugin) | |||||
| // ui.appendChild(controls.uiConfig) | |||||
| ui.setupPluginUi(ThreeGpuPathTracerPlugin) | |||||
| ui.setupPluginUi(ProgressivePlugin) | |||||
| ui.setupPluginUi(CanvasSnapshotPlugin) | |||||
| ui.setupPluginUi(TonemapPlugin) | |||||
| ui.setupPluginUi(BaseGroundPlugin) | |||||
| ui.setupPluginUi(PickingPlugin) | |||||
| } | |||||
| _testStart() | |||||
| init().finally(_testFinish) |
| { | |||||
| "name": "@threepipe/plugin-path-tracing", | |||||
| "version": "0.1.0", | |||||
| "lockfileVersion": 3, | |||||
| "requires": true, | |||||
| "packages": { | |||||
| "": { | |||||
| "name": "@threepipe/plugin-path-tracing", | |||||
| "version": "0.1.0", | |||||
| "license": "Apache-2.0", | |||||
| "dependencies": { | |||||
| "threepipe": "file:./../../src/" | |||||
| }, | |||||
| "devDependencies": { | |||||
| "three-gpu-pathtracer": "^0.0.23" | |||||
| } | |||||
| }, | |||||
| "../../src": {}, | |||||
| "node_modules/three": { | |||||
| "version": "0.177.0", | |||||
| "resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz", | |||||
| "integrity": "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==", | |||||
| "dev": true, | |||||
| "license": "MIT", | |||||
| "peer": true | |||||
| }, | |||||
| "node_modules/three-gpu-pathtracer": { | |||||
| "version": "0.0.23", | |||||
| "resolved": "https://registry.npmjs.org/three-gpu-pathtracer/-/three-gpu-pathtracer-0.0.23.tgz", | |||||
| "integrity": "sha512-CjMX5YU3ajDklOv3hW1EQ4CCkPLiLR+jB31w6G5WLf6Nasemib62tmCj45vBK5ItKAD0aHG1NWc479gXzk6fOQ==", | |||||
| "dev": true, | |||||
| "license": "MIT", | |||||
| "peerDependencies": { | |||||
| "three": ">=0.151.0", | |||||
| "three-mesh-bvh": ">=0.7.4", | |||||
| "xatlas-web": "^0.1.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/three-mesh-bvh": { | |||||
| "version": "0.9.1", | |||||
| "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.9.1.tgz", | |||||
| "integrity": "sha512-WNT+m9jGQgtp4YdtwEnl4oFylNVifRf7iphlwWdJ4bJu7oNkY0xHIyntep9OzHuR1hpe/pyAP840gB/EsYDJfg==", | |||||
| "dev": true, | |||||
| "license": "MIT", | |||||
| "peer": true, | |||||
| "peerDependencies": { | |||||
| "three": ">= 0.159.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/threepipe": { | |||||
| "resolved": "../../src", | |||||
| "link": true | |||||
| }, | |||||
| "node_modules/xatlas-web": { | |||||
| "version": "0.1.0", | |||||
| "resolved": "https://registry.npmjs.org/xatlas-web/-/xatlas-web-0.1.0.tgz", | |||||
| "integrity": "sha512-PprVfuXbaIskxLTLBUQRaWfgSy9xUQqAMIRooOw0P6NYqwgh6T0voeer6+Z5M7AFt5SGXUybuww/uDGs1yw8vQ==", | |||||
| "dev": true, | |||||
| "license": "MIT", | |||||
| "peer": true | |||||
| } | |||||
| } | |||||
| } |
| { | |||||
| "name": "@threepipe/plugin-path-tracing", | |||||
| "description": "Path tracing plugin interfaces for Threepipe", | |||||
| "version": "0.1.0", | |||||
| "devDependencies": { | |||||
| "three-gpu-pathtracer": "^0.0.23" | |||||
| }, | |||||
| "dependencies": { | |||||
| "three": "file:./../../node_modules/three", | |||||
| "threepipe": "file:./../../src/" | |||||
| }, | |||||
| "overrides": { | |||||
| "threepipe": "$threepipe", | |||||
| "three": "$three", | |||||
| "@types/three": "$@types/three" | |||||
| }, | |||||
| "exports": { | |||||
| ".": { | |||||
| "types": "./dist/index.d.ts", | |||||
| "import": "./dist/index.mjs", | |||||
| "require": "./dist/index.js" | |||||
| }, | |||||
| "./dist/": { | |||||
| "import": "./dist/", | |||||
| "require": "./dist/" | |||||
| } | |||||
| }, | |||||
| "type": "module", | |||||
| "main": "dist/index.js", | |||||
| "module": "dist/index.mjs", | |||||
| "types": "dist/index.d.ts", | |||||
| "files": [ | |||||
| "dist", | |||||
| "src" | |||||
| ], | |||||
| "scripts": { | |||||
| "new:pack": "npm run prepare && clean-package && npm pack && clean-package restore", | |||||
| "new:publish": "git diff --exit-code --name-only HEAD * && npm run prepare && clean-package && npm publish --access public && clean-package restore && git tag $npm_package_name-$npm_package_version", | |||||
| "prepare": "npm run build && npm run docs", | |||||
| "build": "rimraf dist && vite build", | |||||
| "dev": "NODE_ENV=development vite build --watch", | |||||
| "docs": "rimraf docs && npx typedoc" | |||||
| }, | |||||
| "author": "repalash <palash@shaders.app>", | |||||
| "license": "Apache-2.0", | |||||
| "keywords": [ | |||||
| "three", | |||||
| "three.js", | |||||
| "threepipe", | |||||
| "path-tracing", | |||||
| "three-gpu-pathtracer", | |||||
| "rendering" | |||||
| ], | |||||
| "bugs": { | |||||
| "url": "https://github.com/repalash/threepipe/issues" | |||||
| }, | |||||
| "homepage": "https://github.com/repalash/threepipe#readme", | |||||
| "repository": { | |||||
| "type": "git", | |||||
| "url": "git://github.com/repalash/threepipe.git", | |||||
| "directory": "plugins/path-tracing" | |||||
| }, | |||||
| "clean-package": { | |||||
| "remove": [ | |||||
| "clean-package", | |||||
| "scripts", | |||||
| "devDependencies", | |||||
| "//", | |||||
| "markdown-to-html" | |||||
| ], | |||||
| "replace": { | |||||
| "dependencies": {}, | |||||
| "peerDependencies": { | |||||
| "threepipe": "^0.0.47" | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| import { | |||||
| AViewerPluginSync, | |||||
| Euler, | |||||
| NoColorSpace, | |||||
| onChange, | |||||
| ProgressivePlugin, | |||||
| Scene, | |||||
| serialize, | |||||
| ThreeViewer, | |||||
| uiButton, | |||||
| uiConfig, | |||||
| uiFolderContainer, | |||||
| uiSlider, | |||||
| uiToggle, | |||||
| } from 'threepipe' | |||||
| import {WebGLPathTracer} from 'three-gpu-pathtracer' | |||||
| import {WebGLPathTracer2} from './WebGLPathTracer2' | |||||
| // @ts-expect-error polyfill for new threejs | |||||
| Scene.prototype.backgroundRotation = new Euler(0, 0, 0, 'XYZ') | |||||
| // @ts-expect-error polyfill | |||||
| Scene.prototype.environmentRotation = new Euler(0, 0, 0, 'XYZ') | |||||
| /** | |||||
| * ThreeGpuPathTracerPlugin | |||||
| * | |||||
| * Path Tracing plugin for Threepipe using [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer). | |||||
| * | |||||
| * This plugin allows for GPU-accelerated path tracing in Three.js scenes. (using webgl2) | |||||
| * | |||||
| * It provides options to configure the path tracing parameters such as bounces, samples per frame, and more. | |||||
| * It also integrates with the Three.js scene and camera, updating materials and lights as needed. | |||||
| * Serialization and deserialization are supported for plugin state management. | |||||
| * It listens to scene updates, camera changes, and material updates to refresh the path tracing setup. | |||||
| * It can be enabled or disabled, and it automatically handles rendering to the screen or to a texture. | |||||
| * It supports progressive rendering, allowing for a smooth transition of rendered frames. | |||||
| * | |||||
| */ | |||||
| @uiFolderContainer('Path Tracing', {expanded: false}) | |||||
| export class ThreeGpuPathTracerPlugin extends AViewerPluginSync { | |||||
| public static readonly PluginType = 'ThreeGpuPathTracerPlugin' | |||||
| @uiToggle() | |||||
| @serialize() | |||||
| @onChange(ThreeGpuPathTracerPlugin.prototype._enableChange) | |||||
| enabled = true | |||||
| // public ptMaterial: any | |||||
| // public ptGenerator: any | |||||
| // public sceneInfo: any | |||||
| // @uiSlider('Environment Multiplier', [0, 10], 0.01) | |||||
| // @serialize() | |||||
| // @onChange(ThreeGpuPathTracerPlugin.prototype.reset) | |||||
| // environmentMultiplier = 1 | |||||
| // | |||||
| // @uiSlider('Max Iterations', [0, 10000], 1) | |||||
| // @serialize() | |||||
| // maxIterations = 100 | |||||
| // | |||||
| // @uiSlider('Bounces', [1, 30], 1) | |||||
| // @serialize() | |||||
| // @onChange(ThreeGpuPathTracerPlugin.prototype.reset) | |||||
| // bounces = 10 | |||||
| // | |||||
| // @uiSlider('filterGlossyFactor', [0, 1], 0.01) | |||||
| // @serialize() | |||||
| // @onChange(ThreeGpuPathTracerPlugin.prototype.reset) | |||||
| // filterGlossyFactor = 0.25 | |||||
| // @uiSlider('environmentBlur', [0, 1], 0.01) | |||||
| // @onChange(ThreeGpuPathTracerPlugin.prototype.reset) | |||||
| // environmentBlur = 0.0 | |||||
| @uiSlider('maxFrameCount', [16, 10000], 1) | |||||
| get maxFrameCount(): number { | |||||
| return this._viewer?.getPlugin(ProgressivePlugin)?.maxFrameCount ?? 32 | |||||
| } | |||||
| set maxFrameCount(value: number) { | |||||
| const p = this._viewer?.getPlugin(ProgressivePlugin) | |||||
| if (!p) { | |||||
| console.warn('ThreeGpuPathTracerPlugin: ProgressivePlugin not found, cannot set maxFrameCount') | |||||
| return | |||||
| } | |||||
| p.maxFrameCount = value | |||||
| } | |||||
| @uiSlider('samplesPerFrame', [1, 5], 1) | |||||
| samplesPerFrame = 1 | |||||
| @uiConfig() | |||||
| @serialize() | |||||
| tracer: WebGLPathTracer | undefined | |||||
| // @uiSlider('tiles', [1, 5], 1) | |||||
| // tiles = 2 | |||||
| // @uiMonitor() | |||||
| // private _lastEnvMap: any = null | |||||
| // private _lastBgMap: any = null | |||||
| dependencies = [ProgressivePlugin] | |||||
| constructor(enabled = true) { | |||||
| super() | |||||
| this.enabled = enabled | |||||
| this.refreshScene = this.refreshScene.bind(this) | |||||
| this.reset = this.reset.bind(this) | |||||
| this._enableChange = this._enableChange.bind(this) | |||||
| this._refreshScene = this._refreshScene.bind(this) | |||||
| } | |||||
| private async _enableChange() { | |||||
| if (this.enabled) this.refreshScene() | |||||
| if (this._viewer) { | |||||
| this._viewer.renderManager.defaultRenderToScreen = !this.enabled | |||||
| } | |||||
| this._viewer?.setDirty() | |||||
| } | |||||
| // todo: onremove and ondispose (dispose sceneInfo in that and remove event listeners) | |||||
| onAdded(viewer: ThreeViewer) { | |||||
| super.onAdded(viewer) | |||||
| // removed | |||||
| // if (viewer.renderer.useLegacyLights) { | |||||
| // viewer.console.warn('Set viewer.renderer.useLegacyLights to false to get consistent lighting') | |||||
| // } | |||||
| // todo diamond | |||||
| // if (viewer.getPluginByType<any>('Diamond')) | |||||
| // viewer.getPluginByType<any>('Diamond').forceSceneEnvMap = true // we dont have separate env map support in path tracing | |||||
| this.tracer = new WebGLPathTracer2(viewer.renderManager.webglRenderer) | |||||
| this.tracer.setScene(viewer.scene, viewer.scene.mainCamera) // todo: handle active camera change | |||||
| // this.ptMaterial = new PhysicalPathTracingMaterial() | |||||
| // this.ptRenderer.alpha = !this._viewer?.getBackground() // for transparent background | |||||
| // this.ptRenderer.camera = viewer.scene.activeCamera.cameraObject | |||||
| // this.ptRenderer.material = this.ptMaterial | |||||
| // this.ptRenderer.setSize(viewer.renderer.renderSize.width, viewer.renderer.renderSize.height) | |||||
| // this.ptRenderer.tiles.set(this.tiles, this.tiles) | |||||
| viewer.scene.addEventListener('sceneUpdate', () => { | |||||
| this.refreshScene() | |||||
| }) | |||||
| viewer.scene.addEventListener('mainCameraChange', () => { | |||||
| this.refreshScene() | |||||
| }) | |||||
| viewer.scene.addEventListener('materialUpdate', () => { | |||||
| if (!this.enabled || !this.tracer || this._refreshing) return | |||||
| this.tracer.updateMaterials() // todo do post frame? | |||||
| this.reset() | |||||
| }) | |||||
| viewer.scene.addEventListener('environmentChanged', () => { | |||||
| if (!this.enabled || !this.tracer || this._refreshing) return | |||||
| this.tracer.updateEnvironment() // todo do post frame? | |||||
| this.reset() | |||||
| }) | |||||
| viewer.renderManager.addEventListener('resize', () => { | |||||
| if (!this.enabled || !this.tracer || this._refreshing) return | |||||
| // this.ptRenderer.setSize(viewer.renderer.renderSize.width, viewer.renderer.renderSize.height) | |||||
| // if (this._refreshing) return | |||||
| // console.log('resize') | |||||
| this.tracer.updateCamera()// todo do post frame? | |||||
| this.reset() | |||||
| }) | |||||
| viewer.scene.mainCamera.addEventListener('update', () => { | |||||
| if (!this.enabled || !this.tracer || this._refreshing) return | |||||
| this.tracer.updateCamera()// todo do post frame? | |||||
| this.reset() | |||||
| }) | |||||
| viewer.addEventListener('preFrame', () => { | |||||
| if (!this.enabled || this._refreshing || !this._viewer || !this.tracer) return | |||||
| // if (viewer.getPlugin(ProgressivePlugin)?.isConverged(true)) { | |||||
| // todo on no rasterize don't render rasterize pipeline | |||||
| const rasterize = viewer.renderManager.frameCount < 16 - 1 // min 16 required for path tracing to show up | |||||
| viewer.renderManager.defaultRenderToScreen = rasterize | |||||
| if (viewer.renderManager.frameCount > 0 && viewer.renderManager.frameCount < this.maxFrameCount) { | |||||
| // const outline = viewer.getPluginByType<OutlinePlugin>('Outline') | |||||
| // if (outline) outline.mouseInOutAnimationEnabled = false | |||||
| if (this._sceneNeedsRefresh) { | |||||
| this._refreshScene() | |||||
| return | |||||
| } | |||||
| // | |||||
| // if (!this.ptMaterial.bvh.bvhBounds.source.data.data) { | |||||
| // return | |||||
| // } | |||||
| this._viewer.renderEnabled = false | |||||
| // this.ptRenderer.material.environmentMap = (viewer.renderManager.webglRenderer as any).cubeuvmaps | |||||
| // const env = viewer.scene.getEnvironment() | |||||
| // let bg = viewer.scene.getBackground() as any | |||||
| // if (!bg || !bg.isTexture || bg === env) bg = null | |||||
| // if (this._lastEnvMap !== env) { | |||||
| // this._lastEnvMap = env | |||||
| // this.ptRenderer.material.envMapInfo.updateFrom(env) | |||||
| // } | |||||
| // if (this._lastBgMap !== bg) { | |||||
| // this._lastBgMap = bg | |||||
| // this.ptRenderer.material.backgroundMap = bg | |||||
| // } | |||||
| // // viewer.getPlugin(DiamondPlugin)?.envMap || viewer.scene.getEnvironment() | |||||
| // // this.ptRenderer.material.environmentMap = viewer.scene.getEnvironment() | |||||
| // this.ptRenderer.material.environmentRotation.makeRotationY(viewer.scene.getEnvironment()?.rotation || 0) | |||||
| // // console.log(this.ptRenderer.material.environmentMap) | |||||
| // // console.log(viewer.getPlugin(DiamondPlugin)?.envMap) | |||||
| // | |||||
| // this.ptRenderer.material.filterGlossyFactor = this.filterGlossyFactor | |||||
| // this.ptRenderer.material.environmentIntensity = (viewer.scene.envMapIntensity || 1) * this.environmentMultiplier | |||||
| // this.ptRenderer.material.bounces = this.bounces | |||||
| // this.ptRenderer.camera.updateMatrixWorld() | |||||
| // | |||||
| // this.ptRenderer.camera.clearViewOffset() | |||||
| // | |||||
| // this._updateDiamondMaterials(this._modelRoot()) | |||||
| // | |||||
| const encoding = viewer.renderManager.webglRenderer.outputColorSpace | |||||
| viewer.renderManager.webglRenderer.outputColorSpace = NoColorSpace | |||||
| // const pcl = (viewer.renderManager.webglRenderer as any).useLegacyLights; | |||||
| // (viewer.renderManager.webglRenderer as any).useLegacyLights = true | |||||
| for (let i = 0, l = this.samplesPerFrame; i < l; i++) | |||||
| this.tracer.renderSample() | |||||
| // ;(viewer.renderManager.webglRenderer as any).useLegacyLights = pcl | |||||
| viewer.renderManager.webglRenderer.outputColorSpace = encoding | |||||
| // this.ptRenderer.camera.clearViewOffset() // todo? | |||||
| // viewer.renderManager.webglRenderer.autoClear = false | |||||
| // ;(fsQuad.material as any).map = this.ptRenderer.target.texture | |||||
| // fsQuad.render(viewer.renderManager.webglRenderer) | |||||
| // viewer.renderManager.webglRenderer.autoClear = true | |||||
| // todo | |||||
| // const combinedPost = this._viewer.getPlugin(CombinedPostPlugin)! | |||||
| if (!rasterize) { | |||||
| const combinedPass = viewer.renderManager.screenPass | |||||
| // if (this._iterationCount < 10 || this._iterationCount > 5 && this._iterationCount % 5 === 0) { | |||||
| combinedPass.render(viewer.renderManager.renderer, null, this.tracer.target, 0, false) | |||||
| viewer.renderManager.incRenderToScreen() | |||||
| } | |||||
| // } | |||||
| // viewer.renderer.blit(this.ptRenderer.target.texture, undefined) | |||||
| // console.log(this.ptRenderer.target.texture) | |||||
| this._viewer.renderEnabled = true | |||||
| } | |||||
| }) | |||||
| } | |||||
| private _refreshing = false | |||||
| private _sceneNeedsRefresh = false | |||||
| @uiButton() | |||||
| refreshScene() { | |||||
| this._sceneNeedsRefresh = true | |||||
| } | |||||
| // private readonly _diaPhysicalMaterial = new MeshPhysicalMaterial({ | |||||
| // color: 0xffffff, | |||||
| // metalness: 0, | |||||
| // roughness: 0, | |||||
| // fog: false, | |||||
| // transmission: 1.0, | |||||
| // opacity: 1, | |||||
| // transparent: true, | |||||
| // thickness: 0, | |||||
| // attenuationDistance: 100, | |||||
| // }) | |||||
| // private readonly _diaMatMap: Record<string, MeshPhysicalMaterial> = {} | |||||
| // private _modelRoot = ()=>this._viewer?.scene.modelRoot.modelObject | |||||
| private async _refreshScene() { | |||||
| if (!this._viewer) return | |||||
| if (!this.enabled || !this.tracer) return | |||||
| if (!this._sceneNeedsRefresh) return | |||||
| if (this._refreshing) { | |||||
| console.warn('ThreeGpuPathTracerPlugin: refreshing already') | |||||
| return | |||||
| } | |||||
| // const root = this._modelRoot() | |||||
| // | |||||
| // if (!root || !root.children.length) return | |||||
| this._sceneNeedsRefresh = false | |||||
| this._viewer.renderEnabled = false | |||||
| // // if (this.sceneInfo) { | |||||
| // // this.sceneInfo.bvh.geometry.dispose() | |||||
| // // this.sceneInfo.bvh.geometry.disposeBoundsTree?.() // todo: check what this is and if required | |||||
| // // this.sceneInfo = undefined | |||||
| // // } | |||||
| // this.reset() | |||||
| // | |||||
| // let geoms = 0 | |||||
| // root.traverse((node:any)=>{ | |||||
| // if (node.geometry) geoms++ | |||||
| // }) | |||||
| // if (geoms < 1) return | |||||
| // | |||||
| this._refreshing = true | |||||
| const viewer = this._viewer | |||||
| const async = false // todo needs bvh worker? | |||||
| if (async) { | |||||
| await this.tracer.setSceneAsync(viewer.scene, viewer.scene.mainCamera, { | |||||
| onProgress: (progress)=>{ | |||||
| console.log('Path Tracing Scene Generation Progress:', progress) | |||||
| }, | |||||
| }) | |||||
| } else { | |||||
| this.tracer.setScene(viewer.scene, viewer.scene.mainCamera, { | |||||
| onProgress: (progress) => { | |||||
| console.log('Path Tracing Scene Generation Progress:', progress) | |||||
| }, | |||||
| }) | |||||
| } | |||||
| // | |||||
| // const generator = new PathTracingSceneGenerator() | |||||
| // this._updateDiamondMaterials(root) | |||||
| // | |||||
| // const matMap: any = {} | |||||
| // | |||||
| // root.traverse((node: any) => { | |||||
| // if (node.material?.isDiamondMaterial) { | |||||
| // matMap[node.uuid] = node.material | |||||
| // node.material = this._diaMatMap[this._diamondMaterialKey(node.material)] | |||||
| // } | |||||
| // }) | |||||
| // | |||||
| // const result = generator.generate([root, ...this._viewer!.scene.children.filter(c=>(c as any)?.isLight)] as any) | |||||
| // // const result = generator.generate(root as any) | |||||
| // | |||||
| // root.traverse((node: any) => { | |||||
| // if (matMap[node.uuid]) { | |||||
| // node.material = matMap[node.uuid] | |||||
| // } | |||||
| // }) | |||||
| // | |||||
| // this.sceneInfo = result | |||||
| // const geometry = result.bvh.geometry | |||||
| // // update bvh and geometry attribute textures | |||||
| // this.ptMaterial.bvh.updateFrom(result.bvh) | |||||
| // this.ptMaterial.attributesArray.updateFrom( | |||||
| // geometry.attributes.normal, | |||||
| // geometry.attributes.tangent, | |||||
| // geometry.attributes.uv, | |||||
| // geometry.attributes.color, | |||||
| // ) | |||||
| // // this.ptMaterial.normalAttribute.updateFrom(geometry.attributes.normal) | |||||
| // // this.ptMaterial.tangentAttribute.updateFrom(geometry.attributes.tangent) | |||||
| // // this.ptMaterial.uvAttribute.updateFrom(geometry.attributes.uv) | |||||
| // // this.ptMaterial.colorAttribute.updateFrom(geometry.attributes.color) | |||||
| // | |||||
| // // console.log(this.ptMaterial, result.materials) | |||||
| // | |||||
| // // update lights | |||||
| // this.ptMaterial.lights.updateFrom(result.lights) | |||||
| // // this.ptMaterial.lightCount = result.lights.length | |||||
| // | |||||
| // // update materials and texture arrays | |||||
| // this.ptMaterial.materialIndexAttribute.updateFrom(geometry.attributes.materialIndex) | |||||
| // this.ptMaterial.textures.setTextures(this._viewer.renderManager.webglRenderer, 2048, 2048, result.textures) | |||||
| // | |||||
| // // this is done before render also | |||||
| // this.ptMaterial.materials.updateFrom(result.materials, result.textures) | |||||
| // | |||||
| // result.matera | |||||
| // | |||||
| // // generator.dispose() | |||||
| // | |||||
| this._viewer.renderEnabled = true | |||||
| this._refreshing = false | |||||
| } | |||||
| // private _updateDiamondMaterials(root?: Object3D) { | |||||
| // root?.traverse((node: any) => { | |||||
| // if (node.material?.isDiamondMaterial) { | |||||
| // const matKey = this._diamondMaterialKey(node.material) | |||||
| // if (!this._diaMatMap[matKey]) { | |||||
| // this._diaMatMap[matKey] = this._diaPhysicalMaterial.clone() | |||||
| // } | |||||
| // this._diaMatMap[matKey].color.set(node.material.color) | |||||
| // this._diaMatMap[matKey].ior = node.material.refractiveIndex | |||||
| // } | |||||
| // }) | |||||
| // } | |||||
| // | |||||
| // private _diamondMaterialKey = (mat: any)=>mat.color.getHexString() + mat.refractiveIndex | |||||
| reset() { | |||||
| if (!this.enabled || !this.tracer) return | |||||
| this.tracer.reset() | |||||
| this._viewer?.setDirty() | |||||
| } | |||||
| dispose() { | |||||
| super.dispose() | |||||
| if (this.tracer) { // todo do on plugin remove | |||||
| this.tracer.dispose() | |||||
| // this.tracer = undefined | |||||
| } | |||||
| } | |||||
| } | |||||
| import { | |||||
| serialize, | |||||
| uiButton, | |||||
| uiFolderContainer, | |||||
| uiNumber, | |||||
| uiSlider, | |||||
| uiToggle, | |||||
| uiVector, | |||||
| Vector2, | |||||
| WebGLRenderer, | |||||
| WebGLRenderTarget, | |||||
| } from 'threepipe' | |||||
| import {WebGLPathTracer} from 'three-gpu-pathtracer' | |||||
| @uiFolderContainer('Path Tracer') | |||||
| export class WebGLPathTracer2 extends WebGLPathTracer { | |||||
| constructor(renderer: WebGLRenderer) { | |||||
| super(renderer) | |||||
| this.rasterizeScene = false | |||||
| this.renderToCanvas = false | |||||
| // this.tiles.set(1, 1) | |||||
| } | |||||
| @uiNumber(undefined, {readOnly: true}) | |||||
| readonly declare samples: number | |||||
| // @uiImage(undefined, {readOnly: true}) | |||||
| readonly declare target: WebGLRenderTarget | |||||
| @uiVector(undefined) | |||||
| readonly declare tiles: Vector2 | |||||
| @uiToggle() | |||||
| @serialize() | |||||
| declare multipleImportanceSampling: boolean | |||||
| @uiSlider(undefined, [1, 100], 1) | |||||
| @serialize() | |||||
| declare bounces: number | |||||
| @uiSlider(undefined, [1, 100], 1) | |||||
| @serialize() | |||||
| declare transmissiveBounces: number | |||||
| @uiNumber() | |||||
| @serialize() | |||||
| declare filterGlossyFactor: number | |||||
| @uiNumber() | |||||
| declare renderDelay: number | |||||
| @uiNumber() | |||||
| declare minSamples: number | |||||
| @uiNumber() | |||||
| declare fadeDuration: number | |||||
| @uiToggle() | |||||
| declare enablePathTracing: boolean | |||||
| @uiToggle() | |||||
| declare pausePathTracing: boolean | |||||
| @uiToggle() | |||||
| declare dynamicLowRes: boolean | |||||
| @uiNumber() | |||||
| declare lowResScale: number | |||||
| @uiNumber() | |||||
| declare renderScale: number | |||||
| @uiToggle() | |||||
| declare synchronizeRenderSize: boolean | |||||
| // @uiToggle() | |||||
| // rasterizeScene: boolean | |||||
| // @uiToggle() | |||||
| // renderToCanvas: boolean | |||||
| @uiVector() | |||||
| @serialize() | |||||
| declare textureSize: Vector2 | |||||
| @uiSlider('Emissive Multiplier', [0, 1000], 0.1) | |||||
| @serialize() | |||||
| emissiveMultiplier = 1 | |||||
| @uiButton() | |||||
| updateCamera() { | |||||
| super.updateCamera() | |||||
| } | |||||
| protected _materials: any[] = [] | |||||
| @uiButton() | |||||
| updateMaterials() { | |||||
| this._materials.forEach((material: any) => { | |||||
| material.userData.__emissiveIntensity = material.emissiveIntensity | |||||
| material.emissiveIntensity *= this.emissiveMultiplier | |||||
| if (material.attenuationDistance < 0.00001) { | |||||
| material.attenuationDistance = 10000 | |||||
| material.userData.__attenuationDistance = material.attenuationDistance | |||||
| } | |||||
| }) | |||||
| super.updateMaterials() | |||||
| // this.ptMaterial.materials.updateFrom(this.sceneInfo.materials, this.sceneInfo.textures) | |||||
| // | |||||
| this._materials.forEach((material: any) => { | |||||
| if (material.userData.__emissiveIntensity !== undefined) { | |||||
| material.emissiveIntensity = material.userData.__emissiveIntensity | |||||
| delete material.userData.__emissiveIntensity | |||||
| } | |||||
| if (material.userData.__attenuationDistance !== undefined) { | |||||
| material.attenuationDistance = material.userData.__attenuationDistance | |||||
| delete material.userData.__attenuationDistance | |||||
| } | |||||
| }) | |||||
| } | |||||
| @uiButton() | |||||
| updateLights() { | |||||
| super.updateLights() | |||||
| } | |||||
| @uiButton() | |||||
| updateEnvironment() { | |||||
| super.updateEnvironment() | |||||
| } | |||||
| // @uiButton() | |||||
| // renderSample() {super.renderSample()} | |||||
| @uiButton() | |||||
| reset() { | |||||
| super.reset() | |||||
| } | |||||
| // @uiButton() | |||||
| // dispose() {super.dispose()} | |||||
| } |
| declare module '*.txt' { | |||||
| const content: string | |||||
| export default content | |||||
| } | |||||
| declare module '*.glsl' { | |||||
| const content: string | |||||
| export default content | |||||
| } | |||||
| declare module '*.vert' { | |||||
| const content: string | |||||
| export default content | |||||
| } | |||||
| declare module '*.frag' { | |||||
| const content: string | |||||
| export default content | |||||
| } | |||||
| declare module '*.module.scss' { | |||||
| const content: any | |||||
| export default content | |||||
| export const stylesheet: string | |||||
| } | |||||
| declare module '*.module.css' { | |||||
| const content: any | |||||
| export default content | |||||
| export const stylesheet: string | |||||
| } | |||||
| declare module '*.css' { | |||||
| const content: string | |||||
| export default content | |||||
| } | |||||
| declare module '*.css?inline' { // for vite | |||||
| const content: string | |||||
| export default content | |||||
| } | |||||
| // export {} | |||||
| // hack for typedoc | |||||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||||
| // declare type OffscreenCanvas = HTMLCanvasElement |
| export {ThreeGpuPathTracerPlugin} from './ThreeGpuPathTracerPlugin' | |||||
| export {WebGLPathTracer2} from './WebGLPathTracer2' |
| { | |||||
| "compilerOptions": { | |||||
| "baseUrl": "./src", | |||||
| "rootDir": "./src", | |||||
| "allowJs": true, | |||||
| "checkJs": false, | |||||
| "skipLibCheck": true, | |||||
| "allowSyntheticDefaultImports": true, | |||||
| "experimentalDecorators": true, | |||||
| "isolatedModules": true, | |||||
| "module": "es2020", | |||||
| "noImplicitAny": true, | |||||
| "declaration": true, | |||||
| "declarationMap": true, | |||||
| "declarationDir": "dist", | |||||
| "outDir": "dist", | |||||
| "noImplicitThis": true, | |||||
| "noUnusedLocals": true, | |||||
| "noUnusedParameters": true, | |||||
| "removeComments": false, | |||||
| "preserveConstEnums": true, | |||||
| "moduleResolution": "node", | |||||
| "emitDecoratorMetadata": false, | |||||
| "sourceMap": true, | |||||
| "target": "ES2021", | |||||
| "strictNullChecks": true, | |||||
| "lib": [ | |||||
| "es2020", | |||||
| "esnext", | |||||
| "dom" | |||||
| ] | |||||
| }, | |||||
| "include": [ | |||||
| "src/**/*" | |||||
| ], | |||||
| "exclude": [ | |||||
| "node_modules", | |||||
| "**/*.spec.ts", | |||||
| "dist" | |||||
| ] | |||||
| } |
| { | |||||
| "extends": [ | |||||
| "../../typedoc.json" | |||||
| ], | |||||
| "entryPoints": [ | |||||
| "src/index.ts" | |||||
| ], | |||||
| "name": "Threepipe Path Tracing Plugins", | |||||
| "readme": "none" | |||||
| } |
| import {defineConfig} from 'vite' | |||||
| import json from '@rollup/plugin-json'; | |||||
| import dts from 'vite-plugin-dts' | |||||
| import packageJson from './package.json'; | |||||
| import license from 'rollup-plugin-license'; | |||||
| import replace from '@rollup/plugin-replace'; | |||||
| import glsl from 'rollup-plugin-glsl'; | |||||
| import path from 'node:path'; | |||||
| const isProd = process.env.NODE_ENV === 'production' | |||||
| const { name, version, author } = packageJson | |||||
| const {main, module, browser} = packageJson | |||||
| const globals = { | |||||
| 'three': 'threepipe', // just incase someone uses three | |||||
| 'threepipe': 'threepipe', | |||||
| // '@threepipe/plugin-tweakpane': '@threepipe/plugin-tweakpane', | |||||
| } | |||||
| export default defineConfig({ | |||||
| optimizeDeps: { | |||||
| exclude: ['uiconfig.js', 'ts-browser-helpers'], | |||||
| }, | |||||
| base: '', | |||||
| // define: { | |||||
| // 'process.env': process.env | |||||
| // }, | |||||
| build: { | |||||
| sourcemap: true, | |||||
| minify: false, | |||||
| cssMinify: isProd, | |||||
| cssCodeSplit: false, | |||||
| watch: !isProd ? { | |||||
| buildDelay: 1000, | |||||
| } : null, | |||||
| lib: { | |||||
| entry: 'src/index.ts', | |||||
| formats: isProd ? ['es', 'umd'] : ['es'], | |||||
| name: name, | |||||
| fileName: (format) => (format === 'umd' ? main : module).replace('dist/', ''), | |||||
| }, | |||||
| outDir: 'dist', | |||||
| emptyOutDir: isProd, | |||||
| commonjsOptions: { | |||||
| exclude: [/uiconfig.js/, /ts-browser-helpers/], | |||||
| }, | |||||
| rollupOptions: { | |||||
| output: { | |||||
| // inlineDynamicImports: false, | |||||
| globals, | |||||
| }, | |||||
| external: Object.keys(globals), | |||||
| }, | |||||
| }, | |||||
| plugins: [ | |||||
| isProd ? dts({tsconfigPath: './tsconfig.json'}) : null, | |||||
| replace({ | |||||
| 'from \'three\'': 'from \'threepipe\'', | |||||
| // 'from \'three/examples/jsm/.*\'': 'from \'threepipe\'', // todo regex doesnt work... | |||||
| 'from \'three/examples/jsm/postprocessing/Pass.js\'': 'from \'threepipe\'', | |||||
| delimiters: ['', ''], | |||||
| }), | |||||
| replace({ | |||||
| 'process.env.NODE_ENV': JSON.stringify(isProd ? 'production' : 'development'), | |||||
| preventAssignment: true, | |||||
| }), | |||||
| glsl({ // todo: minify glsl. | |||||
| include: 'src/**/*.glsl', | |||||
| }), | |||||
| json(), | |||||
| // postcss({ | |||||
| // modules: false, | |||||
| // autoModules: true, // todo; issues with typescript import css, because inject is false | |||||
| // inject: false, | |||||
| // minimize: isProduction, | |||||
| // // Or with custom options for `postcss-modules` | |||||
| // }), | |||||
| license({ | |||||
| banner: ` | |||||
| @license | |||||
| ${name} v${version} | |||||
| Copyright 2022<%= moment().format('YYYY') > 2022 ? '-' + moment().format('YYYY') : null %> ${author} | |||||
| ${packageJson.license} License | |||||
| See ./dependencies.txt for any bundled third-party dependencies and licenses. | |||||
| `, | |||||
| thirdParty: { | |||||
| output: path.join(__dirname, 'dist', 'dependencies.txt'), | |||||
| includePrivate: true, // Default is false. | |||||
| }, | |||||
| }), | |||||
| ], | |||||
| }) |
| {text: 'svg-renderer Plugin', link: 'package/plugin-svg-renderer'}, | {text: 'svg-renderer Plugin', link: 'package/plugin-svg-renderer'}, | ||||
| {text: '3D Tiles (OGC) Renderer Plugin', link: 'package/plugin-3d-tiles-renderer'}, | {text: '3D Tiles (OGC) Renderer Plugin', link: 'package/plugin-3d-tiles-renderer'}, | ||||
| {text: 'Assimpjs Plugin', link: 'package/plugin-assimpjs'}, | {text: 'Assimpjs Plugin', link: 'package/plugin-assimpjs'}, | ||||
| {text: 'Path Tracing', link: 'package/plugin-path-tracing'}, | |||||
| ] | ] | ||||
| }, | }, | ||||
| ], | ], |
| - [@threepipe/plugin-gaussian-splatting](../package/plugin-gaussian-splatting) - [3D Gaussian Splatting](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/) plugin for loading and rendering splat files | - [@threepipe/plugin-gaussian-splatting](../package/plugin-gaussian-splatting) - [3D Gaussian Splatting](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/) plugin for loading and rendering splat files | ||||
| - [@threepipe/plugin-svg-renderer](../package/plugin-svg-renderer) - Add support for exporting 3d scene as SVG (WIP) using [three-svg-renderer](https://www.npmjs.com/package/three-svg-renderer). | - [@threepipe/plugin-svg-renderer](../package/plugin-svg-renderer) - Add support for exporting 3d scene as SVG (WIP) using [three-svg-renderer](https://www.npmjs.com/package/three-svg-renderer). | ||||
| - [@threepipe/plugin-3d-tiles-renderer](https://threepipe.org/package/plugin-3d-tiles-renderer.html) - Plugins for [3d-tiles-renderer](https://github.com/NASA-AMMOS/3DTilesRendererJS), b3dm, i3dm, cmpt, pnts importers. | - [@threepipe/plugin-3d-tiles-renderer](https://threepipe.org/package/plugin-3d-tiles-renderer.html) - Plugins for [3d-tiles-renderer](https://github.com/NASA-AMMOS/3DTilesRendererJS), b3dm, i3dm, cmpt, pnts importers. | ||||
| - [@threepipe/plugin-path-tracing](https://threepipe.org/package/plugin-path-tracing.html) - Plugins for [path-tracing](https://en.wikipedia.org/wiki/Path_tracing). Using [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) | |||||
| - [@threepipe/plugin-assimpjs](https://threepipe.org/package/plugin-assimpjs.html) - Plugin and helpers to load and use [assimpjs](https://github.com/kovacsv/assimpjs) (with fbx, other exporters) in the browser. | - [@threepipe/plugin-assimpjs](https://threepipe.org/package/plugin-assimpjs.html) - Plugin and helpers to load and use [assimpjs](https://github.com/kovacsv/assimpjs) (with fbx, other exporters) in the browser. |
| text: '@threepipe/plugin-3d-tiles-renderer' | text: '@threepipe/plugin-3d-tiles-renderer' | ||||
| link: './plugin-3d-tiles-renderer' | link: './plugin-3d-tiles-renderer' | ||||
| next: false | |||||
| next: | |||||
| text: '@threepipe/plugin-path-tracing' | |||||
| link: './plugin-path-tracing' | |||||
| --- | --- | ||||
| # @threepipe/plugin-assimpjs | # @threepipe/plugin-assimpjs |
| --- | |||||
| prev: | |||||
| text: '@threepipe/plugin-assimpjs' | |||||
| link: './plugin-assimpjs' | |||||
| next: false | |||||
| --- | |||||
| # @threepipe/plugin-path-tracing | |||||
| Provides plugin(s) for [path-tracing](https://en.wikipedia.org/wiki/Path_tracing). | |||||
| Exports | |||||
| - [ThreeGpuPathTracer](https://threepipe.org/plugins/path-tracing/docs/classes/ThreeGpuPathTracer.html) - adds support for full scene path tracing with GPU acceleration using [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer) | |||||
| It provides options to configure the path tracing parameters such as bounces, samples per frame, and more. | |||||
| It also integrates with the Three.js scene and camera, updating materials and lights as needed. | |||||
| Serialization and deserialization are supported for plugin state management. | |||||
| It listens to scene updates, camera changes, and material updates to refresh the path tracing setup. | |||||
| It can be enabled or disabled, and it automatically handles rendering to the screen or to a texture. | |||||
| It supports progressive rendering, allowing for a smooth transition of rendered frames. | |||||
| [Example](https://threepipe.org/examples/#three-gpu-pathtracer/) — | |||||
| [Source Code](https://github.com/repalash/threepipe/blob/master/plugins/path-tracing/src/index.ts) — | |||||
| [API Reference](https://threepipe.org/plugins/path-tracing/docs) | |||||
| [](https://www.npmjs.com/package/@threepipe/plugin-path-tracing) | |||||
| ```bash | |||||
| npm install @threepipe/plugin-path-tracing | |||||
| ``` | |||||
| ::: warning Note | |||||
| This is still a WIP. | |||||
| ::: | |||||
| :::tip Editor | |||||
| Path tracing rendering can be done directly in the tweakpane editor. Simply enable the plugin from the UI. | |||||
| ::: | |||||
| ## Sample Usage | |||||
| To use the plugin, simply add it to the viewer. | |||||
| The plugin automatically interfaces with the `ProgressivePlugin` to render upto `maxFrameCount`. | |||||
| The samples are rendered whenever the plugin is enabled and the camera is not moving. | |||||
| The `WebGLPathTracer` instance in the plugin can be accessed via `viewer.getPlugin(ThreeGpuPathTracer).tracer` property or edited in the UI. | |||||
| ```typescript | |||||
| import {ThreeViewer} from 'threepipe' | |||||
| import {ThreeGpuPathTracer} from '@threepipe/plugin-path-tracing' | |||||
| const viewer = new ThreeViewer({...}) | |||||
| const pathTracer = viewer.addPluginSync(new ThreeGpuPathTracer(false)) // add the plugin disabled | |||||
| console.log(pathTracer.tracer) // access the path tracer instance | |||||
| // load files and environment | |||||
| pathTracer.enabled = true // enable the plugin to start rendering | |||||
| ``` | |||||
| Check the [three-gpu-pathtracer](https://threepipe.org/examples/#three-gpu-pathtracer/) example for a live demo. |