playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
356 lines (355 loc) • 14.3 kB
JavaScript
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
};