| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>Directional Light</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" | |||||
| } | |||||
| } | |||||
| </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> |
| import { | |||||
| _testFinish, | |||||
| Box3B, | |||||
| DirectionalLight, | |||||
| IObject3D, | |||||
| Mesh, | |||||
| PCFSoftShadowMap, | |||||
| PhysicalMaterial, | |||||
| PlaneGeometry, | |||||
| RenderTargetPreviewPlugin, | |||||
| ThreeViewer, | |||||
| Vector3, | |||||
| } from 'threepipe' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| dropzone: { | |||||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'obj', 'fbx', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr'], | |||||
| addOptions: { | |||||
| disposeSceneObjects: true, | |||||
| autoSetEnvironment: true, // when hdr/exr is dropped | |||||
| }, | |||||
| }, | |||||
| }) | |||||
| // viewer.scene.addObject(new HemisphereLight(0xffffff, 0x444444, 10)) | |||||
| const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/fbx/Samba Dancing.fbx', { | |||||
| autoCenter: true, | |||||
| autoScale: true, | |||||
| }) | |||||
| const ground = new Mesh( | |||||
| new PlaneGeometry(100, 100) | |||||
| .rotateX(-Math.PI / 2) | |||||
| .translate(0, new Box3B().expandByObject(result!).getSize(new Vector3()).y / -2, 0), | |||||
| new PhysicalMaterial({ | |||||
| color: '#ffffff', | |||||
| }) | |||||
| ) | |||||
| ground.castShadow = false | |||||
| ground.receiveShadow = true | |||||
| viewer.scene.addObject(ground) | |||||
| const directionalLight = viewer.scene.addObject(new DirectionalLight(0xffffff, 4)) | |||||
| directionalLight.position.set(2, 2, 2) | |||||
| directionalLight.lookAt(0, 0, 0) | |||||
| directionalLight.castShadow = true | |||||
| directionalLight.shadow.mapSize.setScalar(1024) | |||||
| directionalLight.shadow.camera.near = 0.1 | |||||
| directionalLight.shadow.camera.far = 10 | |||||
| directionalLight.shadow.camera.top = 2 | |||||
| directionalLight.shadow.camera.bottom = -2 | |||||
| directionalLight.shadow.camera.left = -2 | |||||
| directionalLight.shadow.camera.right = 2 | |||||
| viewer.renderManager.renderer.shadowMap.type = PCFSoftShadowMap | |||||
| const rt = viewer.addPluginSync(RenderTargetPreviewPlugin) | |||||
| rt.addTarget(()=>directionalLight.shadow.map || undefined, 'shadow', true, true, true) | |||||
| } | |||||
| init().then(_testFinish) |
| const viewer = new ThreeViewer({ | const viewer = new ThreeViewer({ | ||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | ||||
| dropzone: { // this can also be set to true and configured by getting a reference to the DropzonePlugin | 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. | |||||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr', 'fbx', 'obj'], // only allow these file types. If undefined, all files are allowed. | |||||
| addOptions: { | addOptions: { | ||||
| disposeSceneObjects: true, // auto dispose of old scene objects | disposeSceneObjects: true, // auto dispose of old scene objects | ||||
| autoSetEnvironment: true, // when hdr is dropped | autoSetEnvironment: true, // when hdr is dropped |
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | ||||
| msaa: true, | msaa: true, | ||||
| dropzone: { | dropzone: { | ||||
| allowedExtensions: ['gltf', 'glb', 'hdr'], | |||||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr'], | |||||
| addOptions: { | addOptions: { | ||||
| disposeSceneObjects: true, | disposeSceneObjects: true, | ||||
| autoSetEnvironment: true, // when hdr is dropped | autoSetEnvironment: true, // when hdr is dropped |
| const viewer = new ThreeViewer({ | const viewer = new ThreeViewer({ | ||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | ||||
| rgbm: false, | 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.scene.backgroundColor = new Color().set('black') | ||||
| viewer.getPlugin(TonemapPlugin)!.exposure = 0.04 | viewer.getPlugin(TonemapPlugin)!.exposure = 0.04 |
| const viewer = new ThreeViewer({ | const viewer = new ThreeViewer({ | ||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | ||||
| rgbm: true, | 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.scene.backgroundColor = new Color().set('black') | ||||
| viewer.getPlugin(TonemapPlugin)!.exposure = 0.04 | viewer.getPlugin(TonemapPlugin)!.exposure = 0.04 |
| import {IDisposable, PartialRecord} from 'ts-browser-helpers' | import {IDisposable, PartialRecord} from 'ts-browser-helpers' | ||||
| import {Clock, Event, ShaderMaterial, Texture, Vector2, Vector4, WebGLRenderer, WebGLRenderTarget} from 'three' | |||||
| import { | |||||
| Blending, | |||||
| Clock, | |||||
| Event, | |||||
| ShaderMaterial, | |||||
| Texture, | |||||
| Vector2, | |||||
| Vector4, | |||||
| WebGLRenderer, | |||||
| WebGLRenderTarget, | |||||
| } from 'three' | |||||
| import {CreateRenderTargetOptions, IRenderTarget, RenderTargetManager} from '../rendering' | import {CreateRenderTargetOptions, IRenderTarget, RenderTargetManager} from '../rendering' | ||||
| import {IShaderPropertiesUpdater} from '../materials' | import {IShaderPropertiesUpdater} from '../materials' | ||||
| import {EffectComposer2, IPassID, IPipelinePass} from '../postprocessing' | import {EffectComposer2, IPassID, IPipelinePass} from '../postprocessing' | ||||
| [key: string]: any | [key: string]: any | ||||
| } | } | ||||
| export type IRenderManagerEventTypes = 'animationLoop'|'update'|'resize'|'contextLost'|'contextRestored' | export type IRenderManagerEventTypes = 'animationLoop'|'update'|'resize'|'contextLost'|'contextRestored' | ||||
| export interface RendererBlitOptions {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean, respectColorSpace?: boolean, blending?: Blending, transparent?: boolean} | |||||
| export interface IRenderManager<E extends IRenderManagerEvent = IRenderManagerEvent, ET extends string = IRenderManagerEventTypes> extends RenderTargetManager<E, ET>, IDisposable, IShaderPropertiesUpdater{ | export interface IRenderManager<E extends IRenderManagerEvent = IRenderManagerEvent, ET extends string = IRenderManagerEventTypes> extends RenderTargetManager<E, ET>, IDisposable, IShaderPropertiesUpdater{ | ||||
| readonly renderer: IWebGLRenderer | readonly renderer: IWebGLRenderer | ||||
| readonly needsRender: boolean | readonly needsRender: boolean | ||||
| webglRenderer: WebGLRenderer | webglRenderer: WebGLRenderer | ||||
| clock: Clock | clock: Clock | ||||
| blit(destination: IRenderTarget|undefined|null, options?: {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean}): void | |||||
| blit(destination: IRenderTarget|undefined|null, options?: RendererBlitOptions): void | |||||
| clearColor({r, g, b, a, target, depth = true, stencil = true, viewport}: | clearColor({r, g, b, a, target, depth = true, stencil = true, viewport}: | ||||
| {r?: number, g?: number, b?: number, a?: number, target?: IRenderTarget, depth?: boolean, stencil?: boolean, viewport?: Vector4}): void | {r?: number, g?: number, b?: number, a?: number, target?: IRenderTarget, depth?: boolean, stencil?: boolean, viewport?: Vector4}): void | ||||
| protected _beforeRender(): boolean {return this._pass?.enabled && this.enabled || false} | protected _beforeRender(): boolean {return this._pass?.enabled && this.enabled || false} | ||||
| private _enabledTemp = true // to save enabled state when pass is not yet created | private _enabledTemp = true // to save enabled state when pass is not yet created | ||||
| constructor() { | constructor() { | ||||
| super() | super() | ||||
| this._beforeRender = this._beforeRender.bind(this) | this._beforeRender = this._beforeRender.bind(this) |
| DoubleSide, | DoubleSide, | ||||
| FrontSide, | FrontSide, | ||||
| MeshDepthMaterial, | MeshDepthMaterial, | ||||
| MeshDepthMaterialParameters, | |||||
| NoBlending, | NoBlending, | ||||
| Object3D, | Object3D, | ||||
| Scene, | Scene, | ||||
| @uiImage('Depth Buffer' /* {readOnly: true}*/) texture?: Texture | @uiImage('Depth Buffer' /* {readOnly: true}*/) texture?: Texture | ||||
| // @uiConfig() // not supported in this material yet | |||||
| readonly material: MeshDepthMaterial = new MeshDepthMaterialOverride({ | readonly material: MeshDepthMaterial = new MeshDepthMaterialOverride({ | ||||
| depthPacking: BasicDepthPacking, | depthPacking: BasicDepthPacking, | ||||
| blending: NoBlending, | blending: NoBlending, | ||||
| this.texture = this.target.texture | this.texture = this.target.texture | ||||
| this.texture.name = 'depthBuffer' | this.texture.name = 'depthBuffer' | ||||
| if (this._pass) this._pass.target = this.target | |||||
| if (this.isPrimaryGBuffer) { | if (this.isPrimaryGBuffer) { | ||||
| this._viewer.renderManager.gbufferTarget = this.target | this._viewer.renderManager.gbufferTarget = this.target | ||||
| this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension]) | this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension]) | ||||
| } | } | ||||
| class MeshDepthMaterialOverride extends MeshDepthMaterial { | class MeshDepthMaterialOverride extends MeshDepthMaterial { | ||||
| constructor(parameters: MeshDepthMaterialParameters) { | |||||
| super(parameters) | |||||
| this.reset() | |||||
| } | |||||
| onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | ||||
| super.onBeforeRender(renderer, scene, camera, geometry, object) | super.onBeforeRender(renderer, scene, camera, geometry, object) | ||||
| if (!material) return | if (!material) return | ||||
| if (material.map !== undefined) this.map = material.map // in case there is alpha in the map. | if (material.map !== undefined) this.map = material.map // in case there is alpha in the map. | ||||
| if (material.side !== undefined) this.side = DoubleSide | |||||
| if (material.side !== undefined) this.side = material.side ?? FrontSide | |||||
| if (material.alphaMap !== undefined) this.alphaMap = material.alphaMap | if (material.alphaMap !== undefined) this.alphaMap = material.alphaMap | ||||
| if (material.alphaTest !== undefined) this.alphaTest = material.alphaTest < 1e-4 ? 1e-4 : material.alphaTest | if (material.alphaTest !== undefined) this.alphaTest = material.alphaTest < 1e-4 ? 1e-4 : material.alphaTest | ||||
| onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | ||||
| super.onAfterRender(renderer, scene, camera, geometry, object) | super.onAfterRender(renderer, scene, camera, geometry, object) | ||||
| this.reset() | |||||
| } | |||||
| reset() { | |||||
| this.map = null | this.map = null | ||||
| this.side = FrontSide | |||||
| this.side = DoubleSide | |||||
| this.alphaMap = null | this.alphaMap = null | ||||
| this.alphaTest = 0.001 | this.alphaTest = 0.001 | ||||
| BufferGeometry, | BufferGeometry, | ||||
| Camera, | Camera, | ||||
| Color, | Color, | ||||
| FrontSide, | |||||
| DoubleSide, | |||||
| HalfFloatType, | HalfFloatType, | ||||
| LinearSRGBColorSpace, | LinearSRGBColorSpace, | ||||
| MeshNormalMaterial, | MeshNormalMaterial, | ||||
| MeshNormalMaterialParameters, | |||||
| NearestFilter, | NearestFilter, | ||||
| NoBlending, | NoBlending, | ||||
| Object3D, | Object3D, | ||||
| }) | }) | ||||
| this.texture = this.target.texture | this.texture = this.target.texture | ||||
| this.texture.name = 'normalBuffer' | this.texture.name = 'normalBuffer' | ||||
| if (this._pass) this._pass.target = this.target | |||||
| } | } | ||||
| protected _disposeTarget() { | protected _disposeTarget() { | ||||
| if (!this._viewer) return | if (!this._viewer) return | ||||
| } | } | ||||
| class MeshNormalMaterialOverride extends MeshNormalMaterial { | class MeshNormalMaterialOverride extends MeshNormalMaterial { | ||||
| constructor(parameters: MeshNormalMaterialParameters) { | |||||
| super(parameters) | |||||
| this.reset() | |||||
| } | |||||
| onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | ||||
| super.onBeforeRender(renderer, scene, camera, geometry, object) | super.onBeforeRender(renderer, scene, camera, geometry, object) | ||||
| onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | ||||
| super.onAfterRender(renderer, scene, camera, geometry, object) | super.onAfterRender(renderer, scene, camera, geometry, object) | ||||
| this.reset() | |||||
| } | |||||
| reset() { | |||||
| this.bumpMap = null | this.bumpMap = null | ||||
| this.bumpScale = 1 | this.bumpScale = 1 | ||||
| // this.alphaMap = null | // this.alphaMap = null | ||||
| this.flatShading = false | this.flatShading = false | ||||
| this.side = FrontSide | |||||
| this.side = DoubleSide | |||||
| this.wireframe = false | this.wireframe = false | ||||
| this.wireframeLinewidth = 1 | this.wireframeLinewidth = 1 |
| this.clear = false | this.clear = false | ||||
| this.needsSwap = true | this.needsSwap = true | ||||
| } | } | ||||
| set weights2(value: Vector4) { | |||||
| (this.uniforms.weight2.value as Vector4).copy(value) | |||||
| } | |||||
| get weights2(): Vector4 { | |||||
| return this.uniforms.weight2.value as Vector4 | |||||
| } | |||||
| set weights1(value: Vector4) { | |||||
| (this.uniforms.weight.value as Vector4).copy(value) | |||||
| } | |||||
| get weights1(): Vector4 { | |||||
| return this.uniforms.weight.value as Vector4 | |||||
| } | |||||
| set blendTexture(value: Texture) { | |||||
| this.uniforms.tDiffuse2.value = value | |||||
| } | |||||
| } | } |
| varying vec2 vUv; | varying vec2 vUv; | ||||
| ${extraFrag} | ${extraFrag} | ||||
| void main() { | void main() { | ||||
| vec4 a = tDiffuseTexelToLinear ( texture2D( tDiffuse, vUv ) ) | |||||
| vec4 b = tDiffuse2TexelToLinear ( texture2D( tDiffuse2, vUv ) ) | |||||
| vec4 a = tDiffuseTexelToLinear ( texture2D( tDiffuse, vUv ) ); | |||||
| vec4 b = tDiffuse2TexelToLinear ( texture2D( tDiffuse2, vUv ) ); | |||||
| vec4 c = vec4(0); | vec4 c = vec4(0); | ||||
| ${blendFunc} | ${blendFunc} | ||||
| c = clamp(c, vec4(0), vec4(8)); | c = clamp(c, vec4(0), vec4(8)); |
| import {IDisposable} from 'ts-browser-helpers' | |||||
| import {IDisposable, ValOrFunc} from 'ts-browser-helpers' | |||||
| import {IUniform} from 'three' | import {IUniform} from 'three' | ||||
| import {Pass} from 'three/examples/jsm/postprocessing/Pass.js' | import {Pass} from 'three/examples/jsm/postprocessing/Pass.js' | ||||
| import {IShaderPropertiesUpdater, MaterialExtension} from '../materials' | import {IShaderPropertiesUpdater, MaterialExtension} from '../materials' | ||||
| updateShaderProperties?: (updater?: (IShaderPropertiesUpdater|undefined) | (IShaderPropertiesUpdater|undefined)[])=>void | updateShaderProperties?: (updater?: (IShaderPropertiesUpdater|undefined) | (IShaderPropertiesUpdater|undefined)[])=>void | ||||
| materialExtension?: MaterialExtension | materialExtension?: MaterialExtension | ||||
| dirty?: boolean // isDirty (optional) | |||||
| dirty?: ValOrFunc<boolean> // isDirty (optional) | |||||
| setDirty?(): void | setDirty?(): void | ||||
| onDirty?: (()=>void)[]; | onDirty?: (()=>void)[]; | ||||
| import { | import { | ||||
| Blending, | |||||
| Color, | Color, | ||||
| FloatType, | FloatType, | ||||
| HalfFloatType, | HalfFloatType, | ||||
| NormalBlending, | NormalBlending, | ||||
| NoToneMapping, | NoToneMapping, | ||||
| PCFShadowMap, | PCFShadowMap, | ||||
| ShaderMaterial, | |||||
| Texture, | Texture, | ||||
| Vector2, | Vector2, | ||||
| Vector4, | Vector4, | ||||
| IWebGLRenderer, | IWebGLRenderer, | ||||
| upgradeWebGLRenderer, | upgradeWebGLRenderer, | ||||
| } from '../core' | } from '../core' | ||||
| import {base64ToArrayBuffer, canvasFlipY, Class, onChange2, serializable, serialize, ValOrArr} from 'ts-browser-helpers' | |||||
| import { | |||||
| base64ToArrayBuffer, | |||||
| canvasFlipY, | |||||
| Class, | |||||
| getOrCall, | |||||
| onChange2, | |||||
| serializable, | |||||
| serialize, | |||||
| ValOrArr, | |||||
| } from 'ts-browser-helpers' | |||||
| import {uiButton, uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | import {uiButton, uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | ||||
| import {generateUUID, textureDataToImageData} from '../three' | import {generateUUID, textureDataToImageData} from '../three' | ||||
| import {BlobExt, EXRExporter2} from '../assetmanager' | import {BlobExt, EXRExporter2} from '../assetmanager' | ||||
| import {RendererBlitOptions} from '../core/IRenderer' | |||||
| @serializable('RenderManager') | @serializable('RenderManager') | ||||
| @uiFolderContainer('Render Manager') | @uiFolderContainer('Render Manager') | ||||
| } | } | ||||
| get needsRender(): boolean { | get needsRender(): boolean { | ||||
| this._dirty = this._dirty || this._passes.findIndex(value => value.dirty) >= 0 // todo: check for enabled passes only. | |||||
| this._dirty = this._dirty || this._passes.findIndex(value => getOrCall(value.dirty)) >= 0 // todo: check for enabled passes only. | |||||
| return this._dirty | return this._dirty | ||||
| } | } | ||||
| * @param blending - Note - Set to NormalBlending if transparent is set to false | * @param blending - Note - Set to NormalBlending if transparent is set to false | ||||
| * @param transparent | * @param transparent | ||||
| */ | */ | ||||
| blit(destination: IRenderTarget|undefined|null, {source, viewport, material, clear = true, respectColorSpace = false, blending = NoBlending, transparent = true}: {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean, respectColorSpace?: boolean, blending?: Blending, transparent?: boolean} = {}): void { | |||||
| blit(destination: IRenderTarget|undefined|null, {source, viewport, material, clear = true, respectColorSpace = false, blending = NoBlending, transparent = true}: RendererBlitOptions = {}): void { | |||||
| const copyPass = !respectColorSpace ? this._composer.copyPass : this._composer.copyPass2 | const copyPass = !respectColorSpace ? this._composer.copyPass : this._composer.copyPass2 | ||||
| const {renderToScreen, material: oldMaterial, uniforms: oldUniforms, clear: oldClear} = copyPass | const {renderToScreen, material: oldMaterial, uniforms: oldUniforms, clear: oldClear} = copyPass | ||||
| if (material) { | if (material) { |