UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

356 lines (355 loc) 14.3 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { math } from "../../core/math/math.js"; import { Color } from "../../core/math/color.js"; import { Debug } from "../../core/debug.js"; import { RenderPassShaderQuad } from "../../scene/graphics/render-pass-shader-quad.js"; import { GAMMA_NONE, GAMMA_SRGB, gammaNames, TONEMAP_LINEAR, tonemapNames } from "../../scene/constants.js"; import { ShaderChunks } from "../../scene/shader-lib/shader-chunks.js"; import { hashCode } from "../../core/hash.js"; import { FILTER_LINEAR, SEMANTIC_POSITION, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from "../../platform/graphics/constants.js"; import { ShaderUtils } from "../../scene/shader-lib/shader-utils.js"; import { composeChunksGLSL } from "../../scene/shader-lib/glsl/collections/compose-chunks-glsl.js"; import { composeChunksWGSL } from "../../scene/shader-lib/wgsl/collections/compose-chunks-wgsl.js"; class RenderPassCompose extends RenderPassShaderQuad { constructor(graphicsDevice) { super(graphicsDevice); /** * @type {Texture|null} */ __publicField(this, "sceneTexture", null); __publicField(this, "bloomIntensity", 0.01); __publicField(this, "_bloomTexture", null); __publicField(this, "_cocTexture", null); __publicField(this, "blurTexture", null); __publicField(this, "blurTextureUpscale", false); __publicField(this, "_ssaoTexture", null); __publicField(this, "_toneMapping", TONEMAP_LINEAR); __publicField(this, "_gradingEnabled", false); __publicField(this, "gradingSaturation", 1); __publicField(this, "gradingContrast", 1); __publicField(this, "gradingBrightness", 1); __publicField(this, "gradingTint", new Color(1, 1, 1, 1)); __publicField(this, "_shaderDirty", true); __publicField(this, "_vignetteEnabled", false); __publicField(this, "vignetteInner", 0.5); __publicField(this, "vignetteOuter", 1); __publicField(this, "vignetteCurvature", 0.5); __publicField(this, "vignetteIntensity", 0.3); __publicField(this, "vignetteColor", new Color(0, 0, 0)); __publicField(this, "_fringingEnabled", false); __publicField(this, "fringingIntensity", 10); __publicField(this, "_colorEnhanceEnabled", false); __publicField(this, "colorEnhanceShadows", 0); __publicField(this, "colorEnhanceHighlights", 0); __publicField(this, "colorEnhanceVibrance", 0); __publicField(this, "colorEnhanceDehaze", 0); __publicField(this, "colorEnhanceMidtones", 0); __publicField(this, "_taaEnabled", false); __publicField(this, "_hdrScene", true); __publicField(this, "_sharpness", 0.5); __publicField(this, "_gammaCorrection", GAMMA_SRGB); /** * @type {Texture|null} */ __publicField(this, "_colorLUT", null); /** * @type {Texture|null} */ __publicField(this, "_colorLUT2", null); __publicField(this, "colorLUTIntensity", 1); __publicField(this, "colorLUT2Intensity", 1); __publicField(this, "colorLUTBlend", 0); __publicField(this, "_key", ""); __publicField(this, "_debug", null); // track user-provided custom compose chunks __publicField(this, "_customComposeChunks", /* @__PURE__ */ new Map([ ["composeDeclarationsPS", ""], ["composeMainStartPS", ""], ["composeMainEndPS", ""] ])); ShaderChunks.get(graphicsDevice, SHADERLANGUAGE_GLSL).add(composeChunksGLSL, false); ShaderChunks.get(graphicsDevice, SHADERLANGUAGE_WGSL).add(composeChunksWGSL, false); const { scope } = graphicsDevice; this.sceneTextureId = scope.resolve("sceneTexture"); this.bloomTextureId = scope.resolve("bloomTexture"); this.cocTextureId = scope.resolve("cocTexture"); this.ssaoTextureId = scope.resolve("ssaoTexture"); this.blurTextureId = scope.resolve("blurTexture"); this.bloomIntensityId = scope.resolve("bloomIntensity"); this.bcsId = scope.resolve("brightnessContrastSaturation"); this.tintId = scope.resolve("tint"); this.vignetterParamsId = scope.resolve("vignetterParams"); this.vignetteColorId = scope.resolve("vignetteColor"); this.fringingIntensityId = scope.resolve("fringingIntensity"); this.sceneTextureInvResId = scope.resolve("sceneTextureInvRes"); this.sceneTextureInvResValue = new Float32Array(2); this.sharpnessId = scope.resolve("sharpness"); this.colorLUTId = scope.resolve("colorLUT"); this.colorLUT2Id = scope.resolve("colorLUT2"); this.colorLUTParams = new Float32Array(3); this.colorLUTParamsId = scope.resolve("colorLUTParams"); this.colorEnhanceParamsId = scope.resolve("colorEnhanceParams"); this.colorEnhanceMidtonesId = scope.resolve("colorEnhanceMidtones"); } set debug(value) { if (this._debug !== value) { this._debug = value; this._shaderDirty = true; } } get debug() { return this._debug; } set colorLUT(value) { if (this._colorLUT !== value) { this._colorLUT = value; this._shaderDirty = true; this._validateColorLUT(value, "colorLUT"); } } get colorLUT() { return this._colorLUT; } set colorLUT2(value) { if (this._colorLUT2 !== value) { this._colorLUT2 = value; this._shaderDirty = true; this._validateColorLUT(value, "colorLUT2"); } } get colorLUT2() { return this._colorLUT2; } // Validate that a LUT texture is configured as a 256x16 sRGB strip with no mipmaps and // linear filtering. Stripped in release builds. _validateColorLUT(value, slotName) { Debug.call(() => { if (value) { const required = []; if (value.width !== 256 || value.height !== 16) required.push("size: 256x16"); if (!value.srgb) required.push("srgb: true"); if (value.mipmaps) required.push("mipmaps: false"); if (value.minFilter !== FILTER_LINEAR) required.push("minFilter: FILTER_LINEAR"); if (value.magFilter !== FILTER_LINEAR) required.push("magFilter: FILTER_LINEAR"); if (required.length) { Debug.warnOnce(`CameraFrame.${slotName}: texture '${value.name ?? ""}' should be configured with: ${required.join("; ")}.`, value); } } }); } set bloomTexture(value) { if (this._bloomTexture !== value) { this._bloomTexture = value; this._shaderDirty = true; } } get bloomTexture() { return this._bloomTexture; } set cocTexture(value) { if (this._cocTexture !== value) { this._cocTexture = value; this._shaderDirty = true; } } get cocTexture() { return this._cocTexture; } set ssaoTexture(value) { if (this._ssaoTexture !== value) { this._ssaoTexture = value; this._shaderDirty = true; } } get ssaoTexture() { return this._ssaoTexture; } set taaEnabled(value) { if (this._taaEnabled !== value) { this._taaEnabled = value; this._shaderDirty = true; } } get taaEnabled() { return this._taaEnabled; } set gradingEnabled(value) { if (this._gradingEnabled !== value) { this._gradingEnabled = value; this._shaderDirty = true; } } get gradingEnabled() { return this._gradingEnabled; } set vignetteEnabled(value) { if (this._vignetteEnabled !== value) { this._vignetteEnabled = value; this._shaderDirty = true; } } get vignetteEnabled() { return this._vignetteEnabled; } set fringingEnabled(value) { if (this._fringingEnabled !== value) { this._fringingEnabled = value; this._shaderDirty = true; } } get fringingEnabled() { return this._fringingEnabled; } set colorEnhanceEnabled(value) { if (this._colorEnhanceEnabled !== value) { this._colorEnhanceEnabled = value; this._shaderDirty = true; } } get colorEnhanceEnabled() { return this._colorEnhanceEnabled; } set toneMapping(value) { if (this._toneMapping !== value) { this._toneMapping = value; this._shaderDirty = true; } } get toneMapping() { return this._toneMapping; } set sharpness(value) { if (this._sharpness !== value) { this._sharpness = value; this._shaderDirty = true; } } get sharpness() { return this._sharpness; } get isSharpnessEnabled() { return this._sharpness > 0; } set hdrScene(value) { if (this._hdrScene !== value) { this._hdrScene = value; this._shaderDirty = true; } } get hdrScene() { return this._hdrScene; } postInit() { this.setClearColor(Color.BLACK); this.setClearDepth(1); this.setClearStencil(0); } frameUpdate() { const rt = this.renderTarget ?? this.device.backBuffer; const srgb = rt.isColorBufferSrgb(0); const neededGammaCorrection = srgb ? GAMMA_NONE : GAMMA_SRGB; if (this._gammaCorrection !== neededGammaCorrection) { this._gammaCorrection = neededGammaCorrection; this._shaderDirty = true; } const shaderChunks = ShaderChunks.get(this.device, this.device.isWebGPU ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL); for (const [name, prevValue] of this._customComposeChunks.entries()) { const currentValue = shaderChunks.get(name); if (currentValue !== prevValue) { this._customComposeChunks.set(name, currentValue); this._shaderDirty = true; } } if (this._shaderDirty) { this._shaderDirty = false; const gammaCorrectionName = gammaNames[this._gammaCorrection]; const customChunks = this._customComposeChunks; const declHash = hashCode(customChunks.get("composeDeclarationsPS") ?? ""); const startHash = hashCode(customChunks.get("composeMainStartPS") ?? ""); const endHash = hashCode(customChunks.get("composeMainEndPS") ?? ""); const key = `${this.toneMapping}-${gammaCorrectionName}-${this.bloomTexture ? "bloom" : "nobloom"}-${this.cocTexture ? "dof" : "nodof"}-${this.blurTextureUpscale ? "dofupscale" : ""}-${this.ssaoTexture ? "ssao" : "nossao"}-${this.gradingEnabled ? "grading" : "nograding"}-${this.colorEnhanceEnabled ? "colorenhance" : "nocolorenhance"}-${this.colorLUT ? "colorlut" : "nocolorlut"}-${this.colorLUT2 ? "colorlut2" : "nocolorlut2"}-${this.vignetteEnabled ? "vignette" : "novignette"}-${this.fringingEnabled ? "fringing" : "nofringing"}-${this.taaEnabled ? "taa" : "notaa"}-${this.isSharpnessEnabled ? this._hdrScene ? "cashdr" : "cas" : "nocas"}-${this._debug ?? ""}-decl${declHash}-start${startHash}-end${endHash}`; if (this._key !== key) { this._key = key; const defines = /* @__PURE__ */ new Map(); defines.set("TONEMAP", tonemapNames[this.toneMapping]); defines.set("GAMMA", gammaCorrectionName); if (this.bloomTexture) defines.set("BLOOM", true); if (this.cocTexture) defines.set("DOF", true); if (this.blurTextureUpscale) defines.set("DOF_UPSCALE", true); if (this.ssaoTexture) defines.set("SSAO", true); if (this.gradingEnabled) defines.set("GRADING", true); if (this.colorEnhanceEnabled) defines.set("COLOR_ENHANCE", true); if (this.colorLUT) defines.set("COLOR_LUT", true); if (this.colorLUT && this.colorLUT2) defines.set("COLOR_LUT2", true); if (this.vignetteEnabled) defines.set("VIGNETTE", true); if (this.fringingEnabled) defines.set("FRINGING", true); if (this.taaEnabled) defines.set("TAA", true); if (this.isSharpnessEnabled) { defines.set("CAS", true); if (this._hdrScene) defines.set("CAS_HDR", true); } if (this._debug) defines.set("DEBUG_COMPOSE", this._debug); this.shader = ShaderUtils.createShader(this.device, { uniqueName: `ComposeShader-${key}`, attributes: { aPosition: SEMANTIC_POSITION }, vertexChunk: "quadVS", fragmentChunk: "composePS", fragmentDefines: defines }); } } } execute() { const sceneTex = this.sceneTexture; this.sceneTextureId.setValue(sceneTex); this.sceneTextureInvResValue[0] = 1 / sceneTex.width; this.sceneTextureInvResValue[1] = 1 / sceneTex.height; this.sceneTextureInvResId.setValue(this.sceneTextureInvResValue); if (this._bloomTexture) { this.bloomTextureId.setValue(this._bloomTexture); this.bloomIntensityId.setValue(this.bloomIntensity); } if (this._cocTexture) { this.cocTextureId.setValue(this._cocTexture); this.blurTextureId.setValue(this.blurTexture); } if (this._ssaoTexture) { this.ssaoTextureId.setValue(this._ssaoTexture); } if (this._gradingEnabled) { this.bcsId.setValue([this.gradingBrightness, this.gradingContrast, this.gradingSaturation]); this.tintId.setValue([this.gradingTint.r, this.gradingTint.g, this.gradingTint.b]); } if (this._colorEnhanceEnabled) { this.colorEnhanceParamsId.setValue([this.colorEnhanceShadows, this.colorEnhanceHighlights, this.colorEnhanceVibrance, this.colorEnhanceDehaze]); this.colorEnhanceMidtonesId.setValue(this.colorEnhanceMidtones); } const lutTexture = this._colorLUT; if (lutTexture) { this.colorLUTParams[0] = this.colorLUTIntensity; this.colorLUTParams[1] = this.colorLUT2Intensity; this.colorLUTParams[2] = this.colorLUTBlend; this.colorLUTParamsId.setValue(this.colorLUTParams); this.colorLUTId.setValue(lutTexture); if (this._colorLUT2) { this.colorLUT2Id.setValue(this._colorLUT2); } } if (this._vignetteEnabled) { this.vignetterParamsId.setValue([this.vignetteInner, this.vignetteOuter, this.vignetteCurvature, this.vignetteIntensity]); this.vignetteColorId.setValue([this.vignetteColor.r, this.vignetteColor.g, this.vignetteColor.b]); } if (this._fringingEnabled) { this.fringingIntensityId.setValue(this.fringingIntensity / 1024); } if (this.isSharpnessEnabled) { this.sharpnessId.setValue(math.lerp(-0.125, -0.2, this.sharpness)); } super.execute(); } } export { RenderPassCompose };