UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

238 lines (237 loc) 8.02 kB
"use strict"; import { Quaternion, Vector3, WebGLRenderTarget, Scene, BackSide, FrontSide, DoubleSide } from "three"; import { RENDER_TARGET_DEFAULT_SIZE, renderTargetFormat, renderTargetType } from "./lightMap/Common"; import { setBlurMaterial } from "./lightMap/BlurMaterial"; import { createBlurPlane } from "./lightMap/BlurPlane"; import { setRenderTargetsCombineMaterial } from "./lightMap/RenderTargetsCombineMaterial"; import { createRenderTargetsCombinePlane } from "./lightMap/RenderTargetsCombinePlane"; import { createLightMapMaterial, setLightMapMaterial } from "./lightMap/LightMapMaterial"; import { invertNormals } from "./lightMap/LightMapUtils"; import { RenderTargetPair } from "./lightMap/RenderTargetPair"; export class LightMapController { constructor(renderer) { this.renderer = renderer; this.objectTargets = []; this.lights = []; this._scene = new Scene(); this.renderFlippedUvs = false; this.nonFlippedRenderTargetPair = new RenderTargetPair(); this.flippedRenderTargetPair = new RenderTargetPair(); this.finalRenderTarget = new WebGLRenderTarget(RENDER_TARGET_DEFAULT_SIZE, RENDER_TARGET_DEFAULT_SIZE, { type: renderTargetType, format: renderTargetFormat }); this._params = { resolution: 1, lightRadius: 1, totalIterationsCount: 1, blur: false, blurAmount: 0 }; this._objectStateByObject = /* @__PURE__ */ new WeakMap(); this._previousRenderTarget = null; this._lightHierarchyStateByLight = /* @__PURE__ */ new WeakMap(); this._lightMatrixStateByLight = /* @__PURE__ */ new WeakMap(); this._t = new Vector3(); this._q = new Quaternion(); this._s = new Vector3(); this.lightMapMaterial = createLightMapMaterial(); const blurPlaneData = createBlurPlane(); this.blurPlane = blurPlaneData.plane; this.blurMaterial = blurPlaneData.mat; const renderTargetsCombinePlaneData = createRenderTargetsCombinePlane(); this.renderTargetsCombinePlane = renderTargetsCombinePlaneData.plane; this.renderTargetsCombineMaterial = renderTargetsCombinePlaneData.mat; } setSize(w, h) { this.nonFlippedRenderTargetPair.setSize(w, h); this.flippedRenderTargetPair.setSize(w, h); this.finalRenderTarget.setSize(w, h); } renderTargetPair() { return this.renderFlippedUvs ? this.flippedRenderTargetPair : this.nonFlippedRenderTargetPair; } textureRenderTarget() { return this.finalRenderTarget; } setParams(params) { this._params.resolution = params.resolution; this._params.lightRadius = params.lightRadius; this._params.totalIterationsCount = params.totalIterationsCount; this._params.blur = params.blur; this._params.blurAmount = params.blurAmount; this.setSize(params.resolution, params.resolution); } setState(objects, lights) { this._clearScene(); this._scene.add(this.blurPlane); this._previousRenderTarget = this.renderer.getRenderTarget(); this._setObjects(objects); this._setLights(lights); } _clearScene() { let child; while (child = this._scene.children[0]) { this._scene.remove(child); } } _setObjects(objects) { this.objectTargets = [...objects]; this._saveObjectsState(); } _setLights(lights) { this.lights = lights; for (const light of lights) { this._saveLightHierarchyState(light); this._scene.attach(light); this._saveLightMatrixState(light); } } _saveLightHierarchyState(light) { this._lightHierarchyStateByLight.set(light, { parent: light.parent, matrixAutoUpdate: light.matrixAutoUpdate }); light.matrixAutoUpdate = true; } _saveLightMatrixState(light) { light.updateMatrix(); light.matrix.decompose(this._t, this._q, this._s); this._lightMatrixStateByLight.set(light, { matrix: light.matrix.clone(), position: this._t.clone() }); } _saveObjectsState() { let i = 0; for (const object of this.objectTargets) { this._objectStateByObject.set(object, { frustumCulled: object.frustumCulled, material: object.material, parent: object.parent, renderOrder: object.renderOrder }); object.material = this.lightMapMaterial; object.frustumCulled = false; object.renderOrder = 1e3 + i; this._scene.attach(object); i++; } } _moveLights() { const lightRadius = this._params.lightRadius; for (const light of this.lights) { const state = this._lightMatrixStateByLight.get(light); if (state) { const position = state.position; light.position.x = position.x + lightRadius * (Math.random() - 0.5); light.position.y = position.y + lightRadius * (Math.random() - 0.5); light.position.z = position.z + lightRadius * (Math.random() - 0.5); } } } restoreState() { this._restoreObjectsState(); this._restoreLightsState(); this.renderer.setRenderTarget(this._previousRenderTarget); } _invertObjects() { for (const object of this.objectTargets) { invertNormals(object); } } _restoreObjectsState() { for (const object of this.objectTargets) { const state = this._objectStateByObject.get(object); if (state) { object.frustumCulled = state.frustumCulled; object.renderOrder = state.renderOrder; object.material = state.material; const parent = state.parent; if (parent) { parent.add(object); } } } } _restoreLightsState() { var _a; for (const light of this.lights) { const stateH = this._lightHierarchyStateByLight.get(light); const stateM = this._lightMatrixStateByLight.get(light); if (stateH && stateM) { light.matrixAutoUpdate = stateH.matrixAutoUpdate; light.matrix.copy(stateM.matrix); light.matrix.decompose(light.position, light.quaternion, light.scale); light.updateMatrix(); (_a = stateH.parent) == null ? void 0 : _a.attach(light); } } } runUpdates(camera) { const totalIterationsCount = this._params.totalIterationsCount; this.blurMaterial.uniforms.pixelOffset.value = this._params.blurAmount / this._params.resolution; this.blurPlane.visible = this._params.blur; this.lightMapMaterial.uniforms.lightMapMult.value = 1 / totalIterationsCount; this.lightMapMaterial.side = FrontSide; this.lightMapMaterial.shadowSide = null; this.renderFlippedUvs = false; this._clear(camera); for (let i = 0; i < totalIterationsCount; i++) { this._moveLights(); this._update(camera); } this.renderFlippedUvs = true; this._clear(camera); this._invertObjects(); this.lightMapMaterial.side = [DoubleSide, BackSide][1]; this.lightMapMaterial.shadowSide = BackSide; for (let i = 0; i < totalIterationsCount; i++) { this._moveLights(); this._update(camera); } this._invertObjects(); this._clearScene(); this._scene.add(this.renderTargetsCombinePlane); setRenderTargetsCombineMaterial(this.renderTargetsCombineMaterial, { rt1: this.flippedRenderTargetPair.current(), rt2: this.nonFlippedRenderTargetPair.current() }); this.renderer.setRenderTarget(this.finalRenderTarget); this.renderer.render(this._scene, camera); } _clear(camera) { this._scene.visible = false; this._update(camera); this._update(camera); this._scene.visible = true; } _update(camera) { const rtPair = this.renderTargetPair(); const activeMap = rtPair.current(); const inactiveMap = rtPair.previous(); this.renderer.setRenderTarget(activeMap); setLightMapMaterial(this.lightMapMaterial, { lightMap: inactiveMap // lightMapMult:1 / totalIterationsCount, }); setBlurMaterial(this.blurMaterial, { res: this._params.resolution, lightMap: inactiveMap }); rtPair.toggle(); this.renderer.render(this._scene, camera); } }