playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
330 lines (329 loc) • 11.5 kB
JavaScript
import { math } from "../../core/math/math.js";
import { Color } from "../../core/math/color.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 {
sceneTexture = null;
bloomIntensity = 0.01;
_bloomTexture = null;
_cocTexture = null;
blurTexture = null;
blurTextureUpscale = false;
_ssaoTexture = null;
_toneMapping = TONEMAP_LINEAR;
_gradingEnabled = false;
gradingSaturation = 1;
gradingContrast = 1;
gradingBrightness = 1;
gradingTint = new Color(1, 1, 1, 1);
_shaderDirty = true;
_vignetteEnabled = false;
vignetteInner = 0.5;
vignetteOuter = 1;
vignetteCurvature = 0.5;
vignetteIntensity = 0.3;
vignetteColor = new Color(0, 0, 0);
_fringingEnabled = false;
fringingIntensity = 10;
_colorEnhanceEnabled = false;
colorEnhanceShadows = 0;
colorEnhanceHighlights = 0;
colorEnhanceVibrance = 0;
colorEnhanceDehaze = 0;
colorEnhanceMidtones = 0;
_taaEnabled = false;
_hdrScene = true;
_sharpness = 0.5;
_gammaCorrection = GAMMA_SRGB;
_colorLUT = null;
_colorLUT2 = null;
colorLUTIntensity = 1;
colorLUT2Intensity = 1;
colorLUTBlend = 0;
_key = "";
_debug = null;
// track user-provided custom compose chunks
_customComposeChunks = /* @__PURE__ */ new Map([
["composeDeclarationsPS", ""],
["composeMainStartPS", ""],
["composeMainEndPS", ""]
]);
constructor(graphicsDevice) {
super(graphicsDevice);
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) {
}
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
};