diff --git a/example/web-client/public/style.css b/example/web-client/public/style.css index 60b80ea2..f9c31d5a 100644 --- a/example/web-client/public/style.css +++ b/example/web-client/public/style.css @@ -2,38 +2,14 @@ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; - color-scheme: light dark; color: black; background-color: #121212; - font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; - - --tp-base-background-color: hsla(0, 0%, 10%, 0.8); - --tp-base-shadow-color: hsla(0, 0%, 0%, 0.2); - --tp-button-background-color: hsla(0, 0%, 80%, 1); - --tp-button-background-color-active: hsla(0, 0%, 100%, 1); - --tp-button-background-color-focus: hsla(0, 0%, 95%, 1); - --tp-button-background-color-hover: hsla(0, 0%, 85%, 1); - --tp-button-foreground-color: hsla(0, 0%, 0%, 0.8); - --tp-container-background-color: hsla(0, 0%, 0%, 0.3); - --tp-container-background-color-active: hsla(0, 0%, 0%, 0.6); - --tp-container-background-color-focus: hsla(0, 0%, 0%, 0.5); - --tp-container-background-color-hover: hsla(0, 0%, 0%, 0.4); - --tp-container-foreground-color: hsla(0, 0%, 100%, 0.5); - --tp-groove-foreground-color: hsla(0, 0%, 0%, 0.2); - --tp-input-background-color: hsla(0, 0%, 0%, 0.3); - --tp-input-background-color-active: hsla(0, 0%, 0%, 0.6); - --tp-input-background-color-focus: hsla(0, 0%, 0%, 0.5); - --tp-input-background-color-hover: hsla(0, 0%, 0%, 0.4); - --tp-input-foreground-color: hsla(0, 0%, 100%, 0.5); - --tp-label-foreground-color: hsla(0, 0%, 100%, 0.5); - --tp-monitor-background-color: hsla(0, 0%, 0%, 0.3); - --tp-monitor-foreground-color: hsla(0, 0%, 100%, 0.3); } a { @@ -78,20 +54,3 @@ h1 { background-color: #f9f9f9; } } - -.tp-brkv { - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.tp-dfwv { - width: 333px !important; - display: none; -} - -.tp-lblv_l { - font-size: 10px; - padding-left: 0px !important; - padding-right: 0px !important; -} diff --git a/example/web-client/src/index.ts b/example/web-client/src/index.ts index 6bce0a15..bfbe836a 100644 --- a/example/web-client/src/index.ts +++ b/example/web-client/src/index.ts @@ -8,13 +8,14 @@ import { MMLCompositionScene, KeyInputManager, TimeManager, + Sun as ComposerSun, } from "@mml-io/3d-web-client-core"; import { UserNetworkingClient, UserNetworkingClientUpdate, WebsocketStatus, } from "@mml-io/3d-web-user-networking"; -import { AudioListener, Fog, Group, PerspectiveCamera, Scene } from "three"; +import { AudioListener, Group, PerspectiveCamera, Scene } from "three"; import { Room } from "./Room"; import { Sun } from "./Sun"; @@ -36,11 +37,12 @@ export class App { private readonly modelsPath: string = "/web-client/assets/models"; private readonly characterDescription: CharacterDescription | null = null; - private readonly sun: Sun; + private readonly useComposerSun: boolean = true; + + private readonly sun: Sun | ComposerSun | null; constructor() { this.scene = new Scene(); - this.scene.fog = new Fog(0xc7cad0, 30, 210); this.collisionsManager = new CollisionsManager(this.scene); this.audioListener = new AudioListener(); @@ -59,7 +61,7 @@ export class App { this.cameraManager = new CameraManager(this.collisionsManager); this.camera = this.cameraManager.camera; this.camera.add(this.audioListener); - this.composer = new Composer(this.scene, this.camera); + this.composer = new Composer(this.scene, this.camera, true); this.composer.useHDRI("/web-client/assets/hdr/industrial_sunset_2k.hdr"); this.characterDescription = { @@ -120,8 +122,13 @@ export class App { ); this.group.add(mmlComposition.group); - this.sun = new Sun(); - this.group.add(this.sun); + + if (this.useComposerSun === true) { + this.sun = this.composer.sun; + } else { + this.sun = new Sun(); + this.group.add(this.sun); + } const room = new Room(); this.collisionsManager.addMeshesGroup(room); @@ -132,7 +139,9 @@ export class App { this.timeManager.update(); this.characterManager.update(); this.cameraManager.update(); - this.sun.updateCharacterPosition(this.characterManager.character?.position); + if (this.sun) { + this.sun.updateCharacterPosition(this.characterManager.character?.position); + } this.composer.render(this.timeManager); requestAnimationFrame(() => { this.update(); diff --git a/packages/3d-web-client-core/src/camera/CameraManager.ts b/packages/3d-web-client-core/src/camera/CameraManager.ts index b4dde1a5..31bb0425 100644 --- a/packages/3d-web-client-core/src/camera/CameraManager.ts +++ b/packages/3d-web-client-core/src/camera/CameraManager.ts @@ -7,15 +7,15 @@ import { getTweakpaneActive } from "../tweakpane/tweakPaneActivity"; export class CameraManager { public readonly camera: PerspectiveCamera; - public initialDistance: number = 2.5; + public initialDistance: number = 3.3; private minDistance: number = 0.1; - private maxDistance: number = 6; + private maxDistance: number = 8; - private initialFOV: number = 80; + private initialFOV: number = 60; private fov: number = this.initialFOV; - private minFOV: number = 63; - private maxFOV: number = 85; + private minFOV: number = 50; + private maxFOV: number = 70; private targetFOV: number = this.initialFOV; private minPolarAngle: number = Math.PI * 0.25; diff --git a/packages/3d-web-client-core/src/character/CharacterTooltip.ts b/packages/3d-web-client-core/src/character/CharacterTooltip.ts index 40be403a..5ecf6b86 100644 --- a/packages/3d-web-client-core/src/character/CharacterTooltip.ts +++ b/packages/3d-web-client-core/src/character/CharacterTooltip.ts @@ -1,6 +1,5 @@ import { Camera, - CanvasTexture, Color, FrontSide, LinearFilter, @@ -29,8 +28,6 @@ const defaultLabelHeight = 0.125; const defaultLabelCastShadows = true; export class CharacterTooltip { - private texture: CanvasTexture; - private geometry: PlaneGeometry; private material: MeshBasicMaterial; private mesh: Mesh; @@ -55,7 +52,7 @@ export class CharacterTooltip { constructor(parentModel: Object3D) { this.setText = this.setText.bind(this); this.material = new MeshBasicMaterial({ - map: this.texture, + map: null, transparent: true, opacity: 0, }); diff --git a/packages/3d-web-client-core/src/index.ts b/packages/3d-web-client-core/src/index.ts index f4a86b80..d25961b4 100644 --- a/packages/3d-web-client-core/src/index.ts +++ b/packages/3d-web-client-core/src/index.ts @@ -7,3 +7,4 @@ export { MMLCompositionScene } from "./mml/MMLCompositionScene"; export { Composer } from "./rendering/composer"; export { TimeManager } from "./time/TimeManager"; export { CollisionsManager } from "./collisions/CollisionsManager"; +export { Sun } from "./sun/Sun"; diff --git a/packages/3d-web-client-core/src/rendering/composer.ts b/packages/3d-web-client-core/src/rendering/composer.ts index 5beb8f2b..c98943cd 100644 --- a/packages/3d-web-client-core/src/rendering/composer.ts +++ b/packages/3d-web-client-core/src/rendering/composer.ts @@ -16,7 +16,9 @@ import { NormalPass, } from "postprocessing"; import { + AmbientLight, Color, + Fog, HalfFloatType, LinearSRGBColorSpace, LoadingManager, @@ -30,8 +32,10 @@ import { } from "three"; import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; +import { Sun } from "../sun/Sun"; import { TimeManager } from "../time/TimeManager"; import { composerValues as vals } from "../tweakpane/composerSettings"; +import { envValues } from "../tweakpane/envSettings"; import { TweakPane } from "../tweakpane/TweakPane"; import { BrightnessContrastSaturation } from "./post-effects/bright-contrast-sat"; @@ -71,11 +75,17 @@ export class Composer { private readonly gaussGrainEffect = GaussGrainEffect; private readonly gaussGrainPass: ShaderPass; + private ambientLight: AmbientLight | null = null; + + public sun: Sun | null = null; + public spawnSun: boolean; + private tweakPane: TweakPane; - constructor(scene: Scene, camera: PerspectiveCamera) { + constructor(scene: Scene, camera: PerspectiveCamera, spawnSun: boolean = false) { this.scene = scene; this.camera = camera; + this.spawnSun = spawnSun; this.renderer = new WebGLRenderer({ powerPreference: "high-performance", antialias: false, @@ -89,6 +99,9 @@ export class Composer { this.renderer.toneMapping = vals.renderer.toneMapping as ToneMapping; this.renderer.toneMappingExposure = vals.renderer.exposure; + this.setAmbientLight(); + this.setFog(); + document.body.appendChild(this.renderer.domElement); this.composer = new EffectComposer(this.renderer, { @@ -167,6 +180,11 @@ export class Composer { this.composer.addPass(this.bcsPass); this.composer.addPass(this.gaussGrainPass); + if (this.spawnSun === true) { + this.sun = new Sun(); + this.scene.add(this.sun); + } + this.tweakPane.setupRenderPane( this.ssaoEffect, this.toneMappingEffect, @@ -174,6 +192,11 @@ export class Composer { this.bcs, this.bloomEffect, this.gaussGrainEffect, + this.spawnSun, + this.sun, + this.setHDRIFromFile.bind(this), + this.setAmbientLight.bind(this), + this.setFog.bind(this), ); window.addEventListener("resize", () => this.updateProjection()); this.updateProjection(); @@ -212,8 +235,8 @@ export class Composer { } } - public useHDRI(url: string): void { - if (this.isEnvHDRI || !this.renderer) return; + public useHDRI(url: string, fromFile: boolean = false): void { + if ((this.isEnvHDRI && fromFile === false) || !this.renderer) return; const pmremGenerator = new PMREMGenerator(this.renderer); new RGBELoader(new LoadingManager()).load( url, @@ -236,4 +259,52 @@ export class Composer { }, ); } + + public setHDRIFromFile(): void { + if (!this.renderer) return; + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = ".hdr"; + fileInput.addEventListener("change", () => { + const file = fileInput.files?.[0]; + if (!file) { + console.log("no file"); + return; + } + const fileURL = URL.createObjectURL(file); + if (fileURL) { + this.useHDRI(fileURL, true); + URL.revokeObjectURL(fileURL); + document.body.removeChild(fileInput); + } + }); + document.body.appendChild(fileInput); + fileInput.click(); + } + + public setFog(): void { + const fogColor = new Color().setRGB( + envValues.fog.fogColor.r, + envValues.fog.fogColor.g, + envValues.fog.fogColor.b, + ); + this.scene.fog = new Fog(fogColor, envValues.fog.fogNear, envValues.fog.fogFar); + } + + public setAmbientLight(): void { + if (this.ambientLight) { + this.scene.remove(this.ambientLight); + this.ambientLight.dispose(); + } + const ambientLightColor = new Color().setRGB( + envValues.ambientLight.ambientLightColor.r, + envValues.ambientLight.ambientLightColor.g, + envValues.ambientLight.ambientLightColor.b, + ); + this.ambientLight = new AmbientLight( + ambientLightColor, + envValues.ambientLight.ambientLightIntensity, + ); + this.scene.add(this.ambientLight); + } } diff --git a/packages/3d-web-client-core/src/sun/Sun.ts b/packages/3d-web-client-core/src/sun/Sun.ts new file mode 100644 index 00000000..bc57c565 --- /dev/null +++ b/packages/3d-web-client-core/src/sun/Sun.ts @@ -0,0 +1,90 @@ +import { CameraHelper, Color, DirectionalLight, Group, OrthographicCamera, Vector3 } from "three"; + +import { sunValues } from "../tweakpane/sunSettings"; + +export class Sun extends Group { + private readonly debug: boolean = false; + private readonly sunOffset: Vector3 = new Vector3( + 39 * (Math.PI / 180), + 50 * (Math.PI / 180), + 100, + ); + private readonly shadowResolution: number = 8192; + private readonly shadowCamFrustum: number = 50; + private readonly camHelper: CameraHelper | null = null; + + private readonly shadowCamera: OrthographicCamera; + private readonly directionalLight: DirectionalLight; + + public target: Vector3 | null = null; + + constructor() { + super(); + this.shadowCamera = new OrthographicCamera( + -this.shadowCamFrustum, + this.shadowCamFrustum, + this.shadowCamFrustum, + -this.shadowCamFrustum, + 0.1, + 200, + ); + if (this.debug === true) { + this.camHelper = new CameraHelper(this.shadowCamera); + } + this.directionalLight = new DirectionalLight(0xffffff, 0.5); + this.directionalLight.shadow.normalBias = 0.05; + this.directionalLight.shadow.radius = 1.5; + this.directionalLight.shadow.camera = this.shadowCamera; + this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution); + this.directionalLight.castShadow = true; + + this.updateCharacterPosition(new Vector3(0, 0, 0)); + + this.add(this.directionalLight); + if (this.debug === true && this.camHelper instanceof CameraHelper) { + this.add(this.camHelper); + } + } + + public updateCharacterPosition(position: Vector3 | undefined) { + if (!position) return; + this.target = position; + this.setSunPosition(this.sunOffset.x, this.sunOffset.y); + } + + public setAzimuthalAngle(angle: number) { + if (this.sunOffset) this.sunOffset.x = angle; + if (this.target) this.updateCharacterPosition(this.target); + } + + public setPolarAngle(angle: number) { + if (this.sunOffset) this.sunOffset.y = angle; + if (this.target) this.updateCharacterPosition(this.target); + } + + public setIntensity(intensity: number) { + this.directionalLight.intensity = intensity; + } + + public setColor() { + this.directionalLight.color = new Color().setRGB( + sunValues.sunColor.r, + sunValues.sunColor.g, + sunValues.sunColor.b, + ); + } + + private setSunPosition(azimuthalAngle: number, polarAngle: number) { + if (!this.target) return; + const distance = this.sunOffset.z; + const sphericalPosition = new Vector3( + distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle), + distance * Math.cos(polarAngle), + distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle), + ); + const newSunPosition = this.target.clone().add(sphericalPosition); + this.directionalLight.position.set(newSunPosition.x, newSunPosition.y, newSunPosition.z); + this.directionalLight.target.position.copy(this.target.clone()); + this.directionalLight.target.updateMatrixWorld(); + } +} diff --git a/packages/3d-web-client-core/src/tweakpane/TweakPane.ts b/packages/3d-web-client-core/src/tweakpane/TweakPane.ts index e56be382..647548a8 100644 --- a/packages/3d-web-client-core/src/tweakpane/TweakPane.ts +++ b/packages/3d-web-client-core/src/tweakpane/TweakPane.ts @@ -7,9 +7,10 @@ import { ToneMappingEffect, } from "postprocessing"; import { Color, Scene, WebGLRenderer } from "three"; -import { FolderApi, Pane, TpChangeEvent } from "tweakpane"; +import { ButtonApi, FolderApi, Pane, TpChangeEvent } from "tweakpane"; import { GaussGrainEffect } from "../rendering/post-effects/gauss-grain"; +import { Sun } from "../sun/Sun"; import { TimeManager } from "../time/TimeManager"; import { BrightnessContrastSaturation } from "./../rendering/post-effects/bright-contrast-sat"; @@ -25,6 +26,8 @@ import { customToneMappingBlade, setCustomToneMappingType, } from "./composerSettings"; +import { envOptions, envValues } from "./envSettings"; +import { sunOptions, sunValues } from "./sunSettings"; import { setTweakpaneActive } from "./tweakPaneActivity"; export class TweakPane { @@ -32,6 +35,49 @@ export class TweakPane { private scene: Scene; private composer: EffectComposer; + private guiStyle = ` +:root { + --tp-base-background-color: hsla(0, 0%, 10%, 0.8); + --tp-base-shadow-color: hsla(0, 0%, 0%, 0.2); + --tp-button-background-color: hsla(0, 0%, 80%, 1); + --tp-button-background-color-active: hsla(0, 0%, 100%, 1); + --tp-button-background-color-focus: hsla(0, 0%, 95%, 1); + --tp-button-background-color-hover: hsla(0, 0%, 85%, 1); + --tp-button-foreground-color: hsla(0, 0%, 0%, 0.8); + --tp-container-background-color: hsla(0, 0%, 0%, 0.3); + --tp-container-background-color-active: hsla(0, 0%, 0%, 0.6); + --tp-container-background-color-focus: hsla(0, 0%, 0%, 0.5); + --tp-container-background-color-hover: hsla(0, 0%, 0%, 0.4); + --tp-container-foreground-color: hsla(0, 0%, 100%, 0.5); + --tp-groove-foreground-color: hsla(0, 0%, 0%, 0.2); + --tp-input-background-color: hsla(0, 0%, 0%, 0.3); + --tp-input-background-color-active: hsla(0, 0%, 0%, 0.6); + --tp-input-background-color-focus: hsla(0, 0%, 0%, 0.5); + --tp-input-background-color-hover: hsla(0, 0%, 0%, 0.4); + --tp-input-foreground-color: hsla(0, 0%, 100%, 0.5); + --tp-label-foreground-color: hsla(0, 0%, 100%, 0.5); + --tp-monitor-background-color: hsla(0, 0%, 0%, 0.3); + --tp-monitor-foreground-color: hsla(0, 0%, 100%, 0.3); +} + +.tp-brkv { + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.tp-dfwv { + width: 333px !important; + display: none; +} + +.tp-lblv_l { + font-size: 10px; + padding-left: 0px !important; + padding-right: 0px !important; +} +`; + private gui: Pane = new Pane(); private render: FolderApi; @@ -40,10 +86,17 @@ export class TweakPane { private ssao: FolderApi; private toneMapping: FolderApi; private post: FolderApi; + private export: FolderApi; private characterMaterial: FolderApi; + private environment: FolderApi; + + private sun: FolderApi; + private sunButton: ButtonApi; + private ambient: FolderApi; + private saveVisibilityInLocalStorage: boolean = true; public guiVisible: boolean = false; @@ -63,6 +116,11 @@ export class TweakPane { this.scene = scene; this.composer = composer; + const styleElement = document.createElement("style"); + styleElement.type = "text/css"; + styleElement.appendChild(document.createTextNode(this.guiStyle)); + document.head.appendChild(styleElement); + this.render = this.gui.addFolder({ title: "rendering", expanded: true }); this.stats = this.render.addFolder({ title: "stats", expanded: true }); @@ -73,97 +131,108 @@ export class TweakPane { this.toneMapping.hidden = composerValues.renderer.toneMapping === 5 ? false : true; - this.characterMaterial = this.gui.addFolder({ title: "characterMaterial", expanded: false }); - this.characterMaterial.addInput( - characterValues.material, - "transmission", - characterOptions.material.transmission, - ); - this.characterMaterial.addInput( - characterValues.material, - "metalness", - characterOptions.material.metalness, - ); - this.characterMaterial.addInput( - characterValues.material, - "roughness", - characterOptions.material.roughness, - ); - this.characterMaterial.addInput(characterValues.material, "ior", characterOptions.material.ior); - this.characterMaterial.addInput( - characterValues.material, - "thickness", - characterOptions.material.thickness, - ); - this.characterMaterial.addInput(characterValues.material, "specularColor", { - color: { type: "float" }, - }); - this.characterMaterial.addInput( - characterValues.material, - "specularIntensity", - characterOptions.material.specularIntensity, - ); - this.characterMaterial.addInput(characterValues.material, "emissive", { - color: { type: "float" }, - }); - this.characterMaterial.addInput( - characterValues.material, - "emissiveIntensity", - characterOptions.material.emissiveIntensity, - ); - this.characterMaterial.addInput( - characterValues.material, - "envMapIntensity", - characterOptions.material.envMapIntensity, - ); - this.characterMaterial.addInput(characterValues.material, "sheenColor", { - color: { type: "float" }, - }); - this.characterMaterial.addInput( - characterValues.material, - "sheen", - characterOptions.material.sheen, - ); - this.characterMaterial.addInput( - characterValues.material, - "clearcoat", - characterOptions.material.clearcoat, - ); - this.characterMaterial.addInput( - characterValues.material, - "clearcoatRoughness", - characterOptions.material.clearcoatRoughness, - ); - - this.characterMaterial.on("change", (e: TpChangeEvent) => { - if (!e.presetKey) { - return; - } - if (e.presetKey === "specularColor") { - characterValues.material.specularColor = { - r: e.value.r, - g: e.value.g, - b: e.value.b, - }; - return; - } - if (e.presetKey === "emissive") { - characterValues.material.emissive = { - r: e.value.r, - g: e.value.g, - b: e.value.b, - }; - return; - } - if (e.presetKey === "sheenColor") { - characterValues.material.sheenColor = { - r: e.value.r, - g: e.value.g, - b: e.value.b, - }; - return; - } - }); + // Character + { + this.characterMaterial = this.gui.addFolder({ title: "characterMaterial", expanded: false }); + this.characterMaterial.addInput( + characterValues.material, + "transmission", + characterOptions.material.transmission, + ); + this.characterMaterial.addInput( + characterValues.material, + "metalness", + characterOptions.material.metalness, + ); + this.characterMaterial.addInput( + characterValues.material, + "roughness", + characterOptions.material.roughness, + ); + this.characterMaterial.addInput( + characterValues.material, + "ior", + characterOptions.material.ior, + ); + this.characterMaterial.addInput( + characterValues.material, + "thickness", + characterOptions.material.thickness, + ); + this.characterMaterial.addInput(characterValues.material, "specularColor", { + color: { type: "float" }, + }); + this.characterMaterial.addInput( + characterValues.material, + "specularIntensity", + characterOptions.material.specularIntensity, + ); + this.characterMaterial.addInput(characterValues.material, "emissive", { + color: { type: "float" }, + }); + this.characterMaterial.addInput( + characterValues.material, + "emissiveIntensity", + characterOptions.material.emissiveIntensity, + ); + this.characterMaterial.addInput( + characterValues.material, + "envMapIntensity", + characterOptions.material.envMapIntensity, + ); + this.characterMaterial.addInput(characterValues.material, "sheenColor", { + color: { type: "float" }, + }); + this.characterMaterial.addInput( + characterValues.material, + "sheen", + characterOptions.material.sheen, + ); + this.characterMaterial.addInput( + characterValues.material, + "clearcoat", + characterOptions.material.clearcoat, + ); + this.characterMaterial.addInput( + characterValues.material, + "clearcoatRoughness", + characterOptions.material.clearcoatRoughness, + ); + + this.characterMaterial.on("change", (e: TpChangeEvent) => { + if (!e.presetKey) { + return; + } + if (e.presetKey === "specularColor") { + characterValues.material.specularColor = { + r: e.value.r, + g: e.value.g, + b: e.value.b, + }; + return; + } + if (e.presetKey === "emissive") { + characterValues.material.emissive = { + r: e.value.r, + g: e.value.g, + b: e.value.b, + }; + return; + } + if (e.presetKey === "sheenColor") { + characterValues.material.sheenColor = { + r: e.value.r, + g: e.value.g, + b: e.value.b, + }; + return; + } + }); + } + + this.environment = this.gui.addFolder({ title: "environment", expanded: false }); + this.sun = this.environment.addFolder({ title: "sun", expanded: true }); + this.ambient = this.environment.addFolder({ title: "ambient", expanded: true }); this.export = this.gui.addFolder({ title: "import/export", expanded: false }); @@ -193,214 +262,336 @@ export class TweakPane { brightnessContrastSaturation: typeof BrightnessContrastSaturation, bloomEffect: BloomEffect, gaussGrainEffect: typeof GaussGrainEffect, + hasLighting: boolean, + sun: Sun | null, + setHDR: () => void, + setAmbientLight: () => void, + setFog: () => void, ): void { - this.stats.addMonitor(statsData, "triangles"); - this.stats.addMonitor(statsData, "geometries"); - this.stats.addMonitor(statsData, "textures"); - this.stats.addMonitor(statsData, "shaders"); - this.stats.addMonitor(statsData, "postPasses"); - this.stats.addMonitor(statsData, "drawCalls"); - this.stats.addMonitor(statsData, "rawDeltaTime"); - this.stats.addMonitor(statsData, "deltaTime"); - this.stats.addMonitor(statsData, "FPS"); - - this.renderOptions.addInput( - composerValues.renderer, - "shadowMap", - composerOptions.renderer.shadowMap, - ); - this.renderOptions.addMonitor(rendererBlades, "shadowMapType"); - - this.renderOptions.addInput( - composerValues.renderer, - "toneMapping", - composerOptions.renderer.toneMapping, - ); - - this.renderOptions.addMonitor(rendererBlades, "toneMappingType"); - - this.renderOptions.addInput( - composerValues.renderer, - "exposure", - composerOptions.renderer.exposure, - ); - - this.renderOptions.addInput( - composerValues.renderer, - "bgIntensity", - composerOptions.renderer.bgIntensity, - ); - - this.renderOptions.addInput( - composerValues.renderer, - "bgBlurriness", - composerOptions.renderer.bgBlurriness, - ); - - this.renderOptions.on("change", (e: TpChangeEvent) => { - const target = e.target as any; - switch (target.label) { - case "shadowMap": - this.renderer.shadowMap.type = e.value; - setShadowMapType(e.value); - break; - case "toneMapping": - this.renderer.toneMapping = e.value; - this.toneMapping.hidden = e.value !== 5; - toneMappingPass.enabled = e.value === 5 ? true : false; - setToneMappingType(e.value); - break; - case "exposure": - this.renderer.toneMappingExposure = e.value; - break; - case "bgIntensity": - this.scene.backgroundIntensity = e.value; - break; - case "bgBlurriness": - this.scene.backgroundBlurriness = e.value; - break; - default: - break; - } - }); + // Stats + { + this.stats.addMonitor(statsData, "triangles"); + this.stats.addMonitor(statsData, "geometries"); + this.stats.addMonitor(statsData, "textures"); + this.stats.addMonitor(statsData, "shaders"); + this.stats.addMonitor(statsData, "postPasses"); + this.stats.addMonitor(statsData, "drawCalls"); + this.stats.addMonitor(statsData, "rawDeltaTime"); + this.stats.addMonitor(statsData, "deltaTime"); + this.stats.addMonitor(statsData, "FPS"); + } - this.ssao.addInput({ showEffectOnly: false }, "showEffectOnly"); - this.ssao.addInput(composerValues.ssao, "samples", composerOptions.ssao.samples); - this.ssao.addInput(composerValues.ssao, "rings", composerOptions.ssao.rings); - this.ssao.addInput( - composerValues.ssao, - "luminanceInfluence", - composerOptions.ssao.luminanceInfluence, - ); - this.ssao.addInput(composerValues.ssao, "radius", composerOptions.ssao.radius); - this.ssao.addInput(composerValues.ssao, "intensity", composerOptions.ssao.intensity); - this.ssao.addInput(composerValues.ssao, "bias", composerOptions.ssao.bias); - this.ssao.addInput(composerValues.ssao, "fade", composerOptions.ssao.fade); - this.ssao.addInput( - composerValues.ssao, - "resolutionScale", - composerOptions.ssao.resolutionScale, - ); - this.ssao.addInput( - composerValues.ssao, - "worldDistanceThreshold", - composerOptions.ssao.worldDistanceThreshold, - ); - this.ssao.addInput( - composerValues.ssao, - "worldDistanceFalloff", - composerOptions.ssao.worldDistanceFalloff, - ); - this.ssao.addInput( - composerValues.ssao, - "worldProximityThreshold", - composerOptions.ssao.worldProximityThreshold, - ); - this.ssao.addInput( - composerValues.ssao, - "worldProximityFalloff", - composerOptions.ssao.worldProximityFalloff, - ); - this.ssao.addInput(composerValues.ssao, "color", { - color: { alpha: false, type: "float" }, - }); - this.ssao.on("change", (e: TpChangeEvent) => { - if (!e.presetKey) { - return; - } - const preset = e.presetKey; - if (preset === "showEffectOnly") { - ssaoEffect.blendMode.blendFunction = - e.value === true ? BlendFunction.NORMAL : BlendFunction.MULTIPLY; - return; - } - if (preset === "resolutionScale") { - ssaoEffect.resolution.scale = e.value; - return; - } - if (ssaoMaterialParams.includes(e.presetKey!)) { - (ssaoEffect.ssaoMaterial as any)[preset] = e.value; - return; - } - if (e.presetKey === "color") { - ssaoEffect.color = new Color().setRGB(e.value.r, e.value.g, e.value.b); - return; - } - (ssaoEffect as any)[preset] = e.value; - }); + // RenderOptions + { + this.renderOptions.addInput( + composerValues.renderer, + "shadowMap", + composerOptions.renderer.shadowMap, + ); + this.renderOptions.addMonitor(rendererBlades, "shadowMapType"); + + this.renderOptions.addInput( + composerValues.renderer, + "toneMapping", + composerOptions.renderer.toneMapping, + ); + + this.renderOptions.addMonitor(rendererBlades, "toneMappingType"); + + this.renderOptions.addInput( + composerValues.renderer, + "exposure", + composerOptions.renderer.exposure, + ); + + this.renderOptions.addInput( + composerValues.renderer, + "bgIntensity", + composerOptions.renderer.bgIntensity, + ); + + this.renderOptions.addInput( + composerValues.renderer, + "bgBlurriness", + composerOptions.renderer.bgBlurriness, + ); + + this.renderOptions.on("change", (e: TpChangeEvent) => { + const target = e.target as any; + switch (target.label) { + case "shadowMap": + this.renderer.shadowMap.type = e.value; + setShadowMapType(e.value); + break; + case "toneMapping": + this.renderer.toneMapping = e.value; + this.toneMapping.hidden = e.value !== 5; + toneMappingPass.enabled = e.value === 5 ? true : false; + setToneMappingType(e.value); + break; + case "exposure": + this.renderer.toneMappingExposure = e.value; + break; + case "bgIntensity": + this.scene.backgroundIntensity = e.value; + break; + case "bgBlurriness": + this.scene.backgroundBlurriness = e.value; + break; + default: + break; + } + }); + } + + // SSAO + { + this.ssao.addInput({ showEffectOnly: false }, "showEffectOnly"); + this.ssao.addInput(composerValues.ssao, "samples", composerOptions.ssao.samples); + this.ssao.addInput(composerValues.ssao, "rings", composerOptions.ssao.rings); + this.ssao.addInput( + composerValues.ssao, + "luminanceInfluence", + composerOptions.ssao.luminanceInfluence, + ); + this.ssao.addInput(composerValues.ssao, "radius", composerOptions.ssao.radius); + this.ssao.addInput(composerValues.ssao, "intensity", composerOptions.ssao.intensity); + this.ssao.addInput(composerValues.ssao, "bias", composerOptions.ssao.bias); + this.ssao.addInput(composerValues.ssao, "fade", composerOptions.ssao.fade); + this.ssao.addInput( + composerValues.ssao, + "resolutionScale", + composerOptions.ssao.resolutionScale, + ); + this.ssao.addInput( + composerValues.ssao, + "worldDistanceThreshold", + composerOptions.ssao.worldDistanceThreshold, + ); + this.ssao.addInput( + composerValues.ssao, + "worldDistanceFalloff", + composerOptions.ssao.worldDistanceFalloff, + ); + this.ssao.addInput( + composerValues.ssao, + "worldProximityThreshold", + composerOptions.ssao.worldProximityThreshold, + ); + this.ssao.addInput( + composerValues.ssao, + "worldProximityFalloff", + composerOptions.ssao.worldProximityFalloff, + ); + this.ssao.addInput(composerValues.ssao, "color", { + color: { alpha: false, type: "float" }, + }); + this.ssao.on("change", (e: TpChangeEvent) => { + if (!e.presetKey) { + return; + } + const preset = e.presetKey; + if (preset === "showEffectOnly") { + ssaoEffect.blendMode.blendFunction = + e.value === true ? BlendFunction.NORMAL : BlendFunction.MULTIPLY; + return; + } + if (preset === "resolutionScale") { + ssaoEffect.resolution.scale = e.value; + return; + } + if (ssaoMaterialParams.includes(e.presetKey!)) { + (ssaoEffect.ssaoMaterial as any)[preset] = e.value; + return; + } + if (e.presetKey === "color") { + ssaoEffect.color = new Color().setRGB(e.value.r, e.value.g, e.value.b); + return; + } + (ssaoEffect as any)[preset] = e.value; + }); + } - this.toneMapping.addInput(composerValues.toneMapping, "mode", composerOptions.toneMapping.mode); - this.toneMapping.addMonitor(customToneMappingBlade, "customToneMappingType"); - this.toneMapping.addInput( - composerValues.toneMapping, - "whitePoint", - composerOptions.toneMapping.whitePoint, - ); - this.toneMapping.addInput( - composerValues.toneMapping, - "middleGrey", - composerOptions.toneMapping.middleGrey, - ); - const minLuminance = this.toneMapping.addInput( - composerValues.toneMapping, - "minLuminance", - composerOptions.toneMapping.minLuminance, - ); - minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; - const averageLuminance = this.toneMapping.addInput( - composerValues.toneMapping, - "averageLuminance", - composerOptions.toneMapping.averageLuminance, - ); - averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; - this.toneMapping.addInput( - composerValues.toneMapping, - "adaptationRate", - composerOptions.toneMapping.adaptationRate, - ); - this.toneMapping.on("change", (e: TpChangeEvent) => { - if (!e.presetKey) { + // ToneMapping + { + this.toneMapping.addInput( + composerValues.toneMapping, + "mode", + composerOptions.toneMapping.mode, + ); + this.toneMapping.addMonitor(customToneMappingBlade, "customToneMappingType"); + this.toneMapping.addInput( + composerValues.toneMapping, + "whitePoint", + composerOptions.toneMapping.whitePoint, + ); + this.toneMapping.addInput( + composerValues.toneMapping, + "middleGrey", + composerOptions.toneMapping.middleGrey, + ); + const minLuminance = this.toneMapping.addInput( + composerValues.toneMapping, + "minLuminance", + composerOptions.toneMapping.minLuminance, + ); + minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; + const averageLuminance = this.toneMapping.addInput( + composerValues.toneMapping, + "averageLuminance", + composerOptions.toneMapping.averageLuminance, + ); + averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; + this.toneMapping.addInput( + composerValues.toneMapping, + "adaptationRate", + composerOptions.toneMapping.adaptationRate, + ); + this.toneMapping.on("change", (e: TpChangeEvent) => { + if (!e.presetKey) { + return; + } + const preset = e.presetKey; + if (preset === "mode") { + minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; + averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; + setCustomToneMappingType(e.value); + } + (toneMappingEffect as any)[preset] = e.value; return; - } - const preset = e.presetKey; - if (preset === "mode") { - minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; - averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false; - setCustomToneMappingType(e.value); - } - (toneMappingEffect as any)[preset] = e.value; - return; - }); + }); + } - this.post.addInput(composerValues, "brightness", composerOptions.brightness.amount); - this.post.addInput(composerValues, "contrast", composerOptions.contrast.amount); - this.post.addInput(composerValues, "saturation", composerOptions.saturation.amount); - - this.post.addInput(composerValues, "bloom", composerOptions.bloom.amount); - this.post.addInput(composerValues, "grain", composerOptions.grain.amount); - - this.post.on("change", (e: TpChangeEvent) => { - const target = e.presetKey; - switch (target) { - case "brightness": - brightnessContrastSaturation.uniforms.brightness.value = e.value; - break; - case "contrast": - brightnessContrastSaturation.uniforms.contrast.value = e.value; - break; - case "saturation": - brightnessContrastSaturation.uniforms.saturation.value = e.value; - break; - case "bloom": - bloomEffect.intensity = e.value; - break; - case "grain": - gaussGrainEffect.uniforms.amount.value = e.value; - break; - default: - break; - } - }); + // Post + { + this.post.addInput(composerValues, "brightness", composerOptions.brightness.amount); + this.post.addInput(composerValues, "contrast", composerOptions.contrast.amount); + this.post.addInput(composerValues, "saturation", composerOptions.saturation.amount); + + this.post.addInput(composerValues, "bloom", composerOptions.bloom.amount); + this.post.addInput(composerValues, "grain", composerOptions.grain.amount); + + this.post.on("change", (e: TpChangeEvent) => { + const target = e.presetKey; + switch (target) { + case "brightness": + brightnessContrastSaturation.uniforms.brightness.value = e.value; + break; + case "contrast": + brightnessContrastSaturation.uniforms.contrast.value = e.value; + break; + case "saturation": + brightnessContrastSaturation.uniforms.saturation.value = e.value; + break; + case "bloom": + bloomEffect.intensity = e.value; + break; + case "grain": + gaussGrainEffect.uniforms.amount.value = e.value; + break; + default: + break; + } + }); + } + + // Environment + { + this.environment.hidden = hasLighting === false || sun === null; + this.sun.addInput( + sunValues.sunPosition, + "sunAzimuthalAngle", + sunOptions.sunPosition.sunAzimuthalAngle, + ); + this.sun.addInput( + sunValues.sunPosition, + "sunPolarAngle", + sunOptions.sunPosition.sunPolarAngle, + ); + this.sun.addInput(sunValues, "sunIntensity", sunOptions.sunIntensity); + this.sun.addInput(sunValues, "sunColor", { + color: { type: "float" }, + }); + this.sunButton = this.sun.addButton({ title: "Set HDRI" }); + this.sunButton.on("click", () => { + setHDR(); + }); + + this.sun.on("change", (e: TpChangeEvent) => { + const target = e.presetKey; + switch (target) { + case "sunAzimuthalAngle": + sun?.setAzimuthalAngle(e.value * (Math.PI / 180)); + break; + case "sunPolarAngle": + sun?.setPolarAngle(e.value * (Math.PI / 180)); + break; + case "sunIntensity": + sun?.setIntensity(e.value); + break; + case "sunColor": + sunValues.sunColor = { + r: e.value.r, + g: e.value.g, + b: e.value.b, + }; + sun?.setColor(); + break; + default: + break; + } + }); + + this.ambient.addInput( + envValues.ambientLight, + "ambientLightIntensity", + envOptions.ambientLight.ambientLightIntensity, + ); + this.ambient.addInput(envValues.ambientLight, "ambientLightColor", { + color: { type: "float" }, + }); + this.ambient.addInput(envValues.fog, "fogNear", envOptions.fog.fogNear); + this.ambient.addInput(envValues.fog, "fogFar", envOptions.fog.fogFar); + this.ambient.addInput(envValues.fog, "fogColor", { + color: { type: "float" }, + }); + + this.ambient.on("change", (e: TpChangeEvent) => { + const target = e.presetKey; + switch (target) { + case "ambientLightIntensity": + envValues.ambientLight.ambientLightIntensity = e.value; + setAmbientLight(); + break; + case "ambientLightColor": + envValues.ambientLight.ambientLightColor = { + r: e.value.r, + g: e.value.g, + b: e.value.b, + }; + setAmbientLight(); + break; + case "fogNear": + envValues.fog.fogNear = e.value; + setFog(); + break; + case "fogFar": + envValues.fog.fogFar = e.value; + setFog(); + break; + case "fogColor": + envValues.fog.fogColor = { + r: e.value.r, + g: e.value.g, + b: e.value.b, + }; + setFog(); + break; + default: + break; + } + }); + } const exportButton = this.export.addButton({ title: "export" }); exportButton.on("click", () => { diff --git a/packages/3d-web-client-core/src/tweakpane/composerSettings.ts b/packages/3d-web-client-core/src/tweakpane/composerSettings.ts index a66deb4d..b8620944 100644 --- a/packages/3d-web-client-core/src/tweakpane/composerSettings.ts +++ b/packages/3d-web-client-core/src/tweakpane/composerSettings.ts @@ -36,10 +36,10 @@ export const composerValues = { adaptationRate: 2.0, }, brightness: -0.03, - contrast: 1.3, + contrast: 1.21, saturation: 0.91, grain: 0.061, - bloom: 0.45, + bloom: 0.25, }; export const composerOptions = { @@ -47,7 +47,7 @@ export const composerOptions = { shadowMap: { min: 0, max: 2, step: 1 }, toneMapping: { min: 0, max: 5, step: 1 }, exposure: { min: 0, max: 3, step: 0.01 }, - bgIntensity: { min: 0, max: 1, step: 0.01 }, + bgIntensity: { min: 0, max: 1.3, step: 0.01 }, bgBlurriness: { min: 0, max: 0.1, step: 0.001 }, }, ssao: { diff --git a/packages/3d-web-client-core/src/tweakpane/envSettings.ts b/packages/3d-web-client-core/src/tweakpane/envSettings.ts new file mode 100644 index 00000000..37315628 --- /dev/null +++ b/packages/3d-web-client-core/src/tweakpane/envSettings.ts @@ -0,0 +1,21 @@ +export const envValues = { + ambientLight: { + ambientLightIntensity: 0.0, + ambientLightColor: { r: 1, g: 1, b: 1 }, + }, + fog: { + fogNear: 30, + fogFar: 210, + fogColor: { r: 0.42, g: 0.48, b: 0.59 }, + }, +}; + +export const envOptions = { + ambientLight: { + ambientLightIntensity: { min: 0, max: 1, step: 0.01 }, + }, + fog: { + fogNear: { min: 0, max: 80, step: 1 }, + fogFar: { min: 81, max: 300, step: 1 }, + }, +}; diff --git a/packages/3d-web-client-core/src/tweakpane/sunSettings.ts b/packages/3d-web-client-core/src/tweakpane/sunSettings.ts new file mode 100644 index 00000000..72997773 --- /dev/null +++ b/packages/3d-web-client-core/src/tweakpane/sunSettings.ts @@ -0,0 +1,16 @@ +export const sunValues = { + sunPosition: { + sunAzimuthalAngle: 39, + sunPolarAngle: 50, + }, + sunIntensity: 0.5, + sunColor: { r: 1.0, g: 1.0, b: 1.0 }, +}; + +export const sunOptions = { + sunPosition: { + sunAzimuthalAngle: { min: 0, max: 360, step: 1 }, + sunPolarAngle: { min: -90, max: 90, step: 1 }, + }, + sunIntensity: { min: 0, max: 1, step: 0.05 }, +};