diff --git a/packages/dev/core/src/Engines/Processors/iShaderProcessor.ts b/packages/dev/core/src/Engines/Processors/iShaderProcessor.ts index 126fb87e617..a451766e1e1 100644 --- a/packages/dev/core/src/Engines/Processors/iShaderProcessor.ts +++ b/packages/dev/core/src/Engines/Processors/iShaderProcessor.ts @@ -25,7 +25,7 @@ export interface IShaderProcessor { textureProcessor?: (texture: string, isFragment: boolean, preProcessors: { [key: string]: string }, processingContext: Nullable) => string; endOfUniformBufferProcessor?: (closingBracketLine: string, isFragment: boolean, processingContext: Nullable) => string; lineProcessor?: (line: string, isFragment: boolean, processingContext: Nullable) => string; - preProcessor?: (code: string, defines: string[], isFragment: boolean, processingContext: Nullable) => string; + preProcessor?: (code: string, defines: string[], preProcessors: { [key: string]: string }, isFragment: boolean, processingContext: Nullable) => string; postProcessor?: ( code: string, defines: string[], diff --git a/packages/dev/core/src/Engines/Processors/shaderProcessor.ts b/packages/dev/core/src/Engines/Processors/shaderProcessor.ts index 1ce25bb84cf..c58987eacd1 100644 --- a/packages/dev/core/src/Engines/Processors/shaderProcessor.ts +++ b/packages/dev/core/src/Engines/Processors/shaderProcessor.ts @@ -330,7 +330,7 @@ function _ProcessShaderConversion(sourceCode: string, options: ProcessingOptions // General pre processing if (options.processor.preProcessor) { - preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, options.isFragment, options.processingContext); + preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, preprocessors, options.isFragment, options.processingContext); } preparedSourceCode = _EvaluatePreProcessors(preparedSourceCode, preprocessors, options); @@ -367,7 +367,7 @@ function _ApplyPreProcessing(sourceCode: string, options: ProcessingOptions, eng // General pre processing if (options.processor?.preProcessor) { - preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, options.isFragment, options.processingContext); + preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, preprocessors, options.isFragment, options.processingContext); } preparedSourceCode = _EvaluatePreProcessors(preparedSourceCode, preprocessors, options); diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuCacheRenderPipeline.ts b/packages/dev/core/src/Engines/WebGPU/webgpuCacheRenderPipeline.ts index b471ed628d4..199616abeb9 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuCacheRenderPipeline.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuCacheRenderPipeline.ts @@ -10,7 +10,7 @@ import type { DataBuffer } from "../../Buffers/dataBuffer"; import type { Nullable } from "../../types"; import type { WebGPUHardwareTexture } from "./webgpuHardwareTexture"; import type { WebGPUPipelineContext } from "./webgpuPipelineContext"; -import { WebGPUShaderProcessor } from "./webgpuShaderProcessor"; +import type { WebGPUShaderProcessor } from "./webgpuShaderProcessor"; import { WebGPUTextureHelper } from "./webgpuTextureHelper"; import { renderableTextureFormatToIndex } from "./webgpuTextureManager"; @@ -898,7 +898,7 @@ export abstract class WebGPUCacheRenderPipeline { if (entry.texture) { const name = shaderProcessingContext.bindGroupLayoutEntryInfo[i][entry.binding].name; const textureInfo = shaderProcessingContext.availableTextures[name]; - const samplerInfo = textureInfo.autoBindSampler ? shaderProcessingContext.availableSamplers[name + WebGPUShaderProcessor.AutoSamplerSuffix] : null; + const samplerInfo = textureInfo.autoBindSampler ? shaderProcessingContext.availableSamplers[name + Constants.AUTOSAMPLERSUFFIX] : null; let sampleType = textureInfo.sampleType; let samplerType = samplerInfo?.type ?? WebGPUConstants.SamplerBindingType.Filtering; diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessor.ts b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessor.ts index 9c6e4a13c74..06965e7a6d6 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessor.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessor.ts @@ -9,7 +9,6 @@ import type { WebGPUSamplerDescription, WebGPUShaderProcessingContext, WebGPUTex /** @internal */ export abstract class WebGPUShaderProcessor implements IShaderProcessor { - public static readonly AutoSamplerSuffix = "Sampler"; public static readonly LeftOvertUBOName = "LeftOver"; public static readonly InternalsUBOName = "Internals"; diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsGLSL.ts b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsGLSL.ts index f3063ee383f..cef04eda752 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsGLSL.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsGLSL.ts @@ -8,6 +8,7 @@ import * as WebGPUConstants from "./webgpuConstants"; import { Logger } from "../../Misc/logger"; import { WebGPUShaderProcessor } from "./webgpuShaderProcessor"; import { ShaderLanguage } from "../../Materials/shaderLanguage"; +import { Constants } from "../constants"; /** @internal */ export class WebGPUShaderProcessorGLSL extends WebGPUShaderProcessor { @@ -174,7 +175,7 @@ export class WebGPUShaderProcessorGLSL extends WebGPUShaderProcessor { const samplerType = WebGPUShaderProcessor._SamplerTypeByWebGLSamplerType[uniformType] ?? "sampler"; const isComparisonSampler = !!WebGPUShaderProcessor._IsComparisonSamplerByWebGPUSamplerType[samplerType]; const samplerBindingType = isComparisonSampler ? WebGPUConstants.SamplerBindingType.Comparison : WebGPUConstants.SamplerBindingType.Filtering; - const samplerName = name + WebGPUShaderProcessor.AutoSamplerSuffix; + const samplerName = name + Constants.AUTOSAMPLERSUFFIX; let samplerInfo = this._webgpuProcessingContext.availableSamplers[samplerName]; if (!samplerInfo) { diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts index 513efd30b83..a4fd63d08b3 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts @@ -28,7 +28,15 @@ import "../../ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration"; import "../../ShadersWGSL/ShadersInclude/morphTargetsVertexGlobal"; import "../../ShadersWGSL/ShadersInclude/morphTargetsVertexGlobalDeclaration"; import "../../ShadersWGSL/ShadersInclude/sceneUboDeclaration"; +import "../../ShadersWGSL/ShadersInclude/lightsFragmentFunctions"; +import "../../ShadersWGSL/ShadersInclude/lightFragment"; +import "../../ShadersWGSL/ShadersInclude/lightUboDeclaration"; +import "../../ShadersWGSL/ShadersInclude/lightVxUboDeclaration"; +import "../../ShadersWGSL/ShadersInclude/shadowsFragmentFunctions"; +import "../../ShadersWGSL/ShadersInclude/shadowsVertex"; +import "../../ShadersWGSL/ShadersInclude/fogFragmentDeclaration"; import { ShaderLanguage } from "../../Materials/shaderLanguage"; +import { Constants } from "../constants"; const builtInName_frag_depth = "fragmentOutputs.fragDepth"; @@ -70,6 +78,21 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { public textureRegexp = /var\s+(\w+)\s*:\s*((array<\s*)?(texture_\w+)\s*(<\s*(.+)\s*>)?\s*(,\s*\w+\s*>\s*)?);/; public noPrecision = true; + public preProcessor(code: string, defines: string[], preProcessors: { [key: string]: string }, isFragment: boolean, processingContext: Nullable) { + // Convert defines into const + for (const key in preProcessors) { + if (key === "__VERSION__") { + continue; + } + const value = preProcessors[key]; + if (!isNaN(parseInt(value)) || !isNaN(parseFloat(value))) { + code = `const ${key} = ${value};\n` + code; + } + } + + return code; + } + protected _getArraySize(name: string, uniformType: string, preProcessors: { [key: string]: string }): [string, string, number] { let length = 0; @@ -251,16 +274,17 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { return texture; } - public postProcessor(code: string, defines: string[]) { - const defineToValue: { [key: string]: string } = {}; - for (const define of defines) { - const parts = define.split(/ +/); - defineToValue[parts[1]] = parts.length > 2 ? parts[2] : ""; - } - return code.replace(/\$(\w+)\$/g, (_, p1) => { - return defineToValue[p1] ?? p1; - }); - } + // Ignore for now as we inject const for numeric defines + // public postProcessor(code: string, defines: string[]) { + // const defineToValue: { [key: string]: string } = {}; + // for (const define of defines) { + // const parts = define.split(/ +/); + // defineToValue[parts[1]] = parts.length > 2 ? parts[2] : ""; + // } + // return code.replace(/\$(\w+)\$/g, (_, p1) => { + // return defineToValue[p1] ?? p1; + // }); + // } public finalizeShaders(vertexCode: string, fragmentCode: string): { vertexCode: string; fragmentCode: string } { const fragCoordCode = @@ -287,6 +311,7 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { fragmentCode = leftOverUBO + fragmentCode; // Vertex code + vertexCode = vertexCode.replace(/#define (\w+)\s+(\d+\.?\d*)/g, "const $1 = $2;"); vertexCode = vertexCode.replace(/#define /g, "//#define "); vertexCode = this._processStridedUniformArrays(vertexCode); @@ -316,10 +341,14 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { vertexMainStartingCode += "\n"; } const vertexMainEndingCode = ` vertexOutputs.position.y = vertexOutputs.position.y * internals.yFactor_;\n return vertexOutputs;`; + let needDiagnosticOff = vertexCode.indexOf(Constants.DISABLEUA) !== -1; - vertexCode = this._injectStartingAndEndingCode(vertexCode, "fn main", vertexMainStartingCode, vertexMainEndingCode); + vertexCode = + (needDiagnosticOff ? "diagnostic(off, derivative_uniformity);\n" : "") + + this._injectStartingAndEndingCode(vertexCode, "fn main", vertexMainStartingCode, vertexMainEndingCode); // fragment code + fragmentCode = fragmentCode.replace(/#define (\w+)\s+(\d+\.?\d*)/g, "const $1 = $2;"); fragmentCode = fragmentCode.replace(/#define /g, "//#define "); fragmentCode = this._processStridedUniformArrays(fragmentCode); fragmentCode = fragmentCode.replace(/dpdy/g, "(-internals.yFactor_)*dpdy"); // will also handle dpdyCoarse and dpdyFine @@ -361,8 +390,11 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { const fragmentStartingCode = " fragmentInputs = input;\n " + fragCoordCode; const fragmentEndingCode = " return fragmentOutputs;"; + needDiagnosticOff = fragmentCode.indexOf(Constants.DISABLEUA) !== -1; - fragmentCode = this._injectStartingAndEndingCode(fragmentCode, "fn main", fragmentStartingCode, fragmentEndingCode); + fragmentCode = + (needDiagnosticOff ? "diagnostic(off, derivative_uniformity);\n" : "") + + this._injectStartingAndEndingCode(fragmentCode, "fn main", fragmentStartingCode, fragmentEndingCode); this._collectBindingNames(); this._preCreateBindGroupEntries(); @@ -416,8 +448,8 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { const name = match[1]; // name of the variable const samplerType = match[2]; // sampler or sampler_comparison const textureName = - name.indexOf(WebGPUShaderProcessor.AutoSamplerSuffix) === name.length - WebGPUShaderProcessor.AutoSamplerSuffix.length - ? name.substring(0, name.indexOf(WebGPUShaderProcessor.AutoSamplerSuffix)) + name.indexOf(Constants.AUTOSAMPLERSUFFIX) === name.length - Constants.AUTOSAMPLERSUFFIX.length + ? name.substring(0, name.indexOf(Constants.AUTOSAMPLERSUFFIX)) : null; const samplerBindingType = samplerType === "sampler_comparison" ? WebGPUConstants.SamplerBindingType.Comparison : WebGPUConstants.SamplerBindingType.Filtering; @@ -516,7 +548,7 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { private _processStridedUniformArrays(code: string): string { for (const uniformArrayName of this._stridedUniformArrays) { - code = code.replace(new RegExp(`${uniformArrayName}\\s*\\[(.*)\\]`, "g"), `${uniformArrayName}[$1].el`); + code = code.replace(new RegExp(`${uniformArrayName}\\s*\\[(.*?)\\]`, "g"), `${uniformArrayName}[$1].el`); } return code; } diff --git a/packages/dev/core/src/Engines/constants.ts b/packages/dev/core/src/Engines/constants.ts index 45bc6713f43..28efa3492ea 100644 --- a/packages/dev/core/src/Engines/constants.ts +++ b/packages/dev/core/src/Engines/constants.ts @@ -1,6 +1,10 @@ /* eslint-disable @typescript-eslint/naming-convention */ /** Defines the cross module used constants to avoid circular dependencies */ export class Constants { + /** Sampler suffix when associated with a texture name */ + public static readonly AUTOSAMPLERSUFFIX = "Sampler"; + /** Flag used to disable diagnostics for WebGPU */ + public static readonly DISABLEUA = "#define DIAGNOSTIC_OFF"; /** Defines that alpha blending is disabled */ public static readonly ALPHA_DISABLE = 0; /** Defines that alpha blending is SRC ALPHA * SRC + DEST */ diff --git a/packages/dev/core/src/Engines/webgpuEngine.ts b/packages/dev/core/src/Engines/webgpuEngine.ts index 62d57f04928..26206f1f360 100644 --- a/packages/dev/core/src/Engines/webgpuEngine.ts +++ b/packages/dev/core/src/Engines/webgpuEngine.ts @@ -109,9 +109,6 @@ const viewDescriptorSwapChain: GPUTextureViewDescriptor = { mipLevelCount: 1, arrayLayerCount: 1, }; - -const disableUniformityAnalysisMarker = "/* disable_uniformity_analysis */"; - const tempColor4 = new Color4(); /** @internal */ @@ -1216,10 +1213,6 @@ export class WebGPUEngine extends AbstractEngine { * @param name The texture name */ public setDepthStencilTexture(channel: number, uniform: Nullable, texture: Nullable, name?: string): void { - if (channel === undefined) { - return; - } - if (!texture || !texture.depthStencilTexture) { this._setTexture(channel, null, undefined, undefined, name); } else { @@ -2058,8 +2051,8 @@ export class WebGPUEngine extends AbstractEngine { } private _compileRawPipelineStageDescriptor(vertexCode: string, fragmentCode: string, shaderLanguage: ShaderLanguage): IWebGPURenderPipelineStageDescriptor { - const disableUniformityAnalysisInVertex = vertexCode.indexOf(disableUniformityAnalysisMarker) >= 0; - const disableUniformityAnalysisInFragment = fragmentCode.indexOf(disableUniformityAnalysisMarker) >= 0; + const disableUniformityAnalysisInVertex = vertexCode.indexOf(Constants.DISABLEUA) >= 0; + const disableUniformityAnalysisInFragment = fragmentCode.indexOf(Constants.DISABLEUA) >= 0; const vertexShader = shaderLanguage === ShaderLanguage.GLSL ? this._compileRawShaderToSpirV(vertexCode, "vertex") : vertexCode; const fragmentShader = shaderLanguage === ShaderLanguage.GLSL ? this._compileRawShaderToSpirV(fragmentCode, "fragment") : fragmentCode; @@ -2075,8 +2068,8 @@ export class WebGPUEngine extends AbstractEngine { ): IWebGPURenderPipelineStageDescriptor { this.onBeforeShaderCompilationObservable.notifyObservers(this); - const disableUniformityAnalysisInVertex = vertexCode.indexOf(disableUniformityAnalysisMarker) >= 0; - const disableUniformityAnalysisInFragment = fragmentCode.indexOf(disableUniformityAnalysisMarker) >= 0; + const disableUniformityAnalysisInVertex = vertexCode.indexOf(Constants.DISABLEUA) >= 0; + const disableUniformityAnalysisInFragment = fragmentCode.indexOf(Constants.DISABLEUA) >= 0; const shaderVersion = "#version 450\n"; const vertexShader = @@ -2653,7 +2646,7 @@ export class WebGPUEngine extends AbstractEngine { this._currentMaterialContext.setTexture(name, texture); if (availableTexture && availableTexture.autoBindSampler) { - const samplerName = baseName + WebGPUShaderProcessor.AutoSamplerSuffix; + const samplerName = baseName + Constants.AUTOSAMPLERSUFFIX; this._currentMaterialContext.setSampler(samplerName, texture as InternalTexture); // we can safely cast to InternalTexture because ExternalTexture always has autoBindSampler = false } } diff --git a/packages/dev/core/src/Lights/Shadows/cascadedShadowGenerator.ts b/packages/dev/core/src/Lights/Shadows/cascadedShadowGenerator.ts index e1f5f2e290f..28b4fbe41ad 100644 --- a/packages/dev/core/src/Lights/Shadows/cascadedShadowGenerator.ts +++ b/packages/dev/core/src/Lights/Shadows/cascadedShadowGenerator.ts @@ -972,7 +972,7 @@ export class CascadedShadowGenerator extends ShadowGenerator { // Only PCF uses depth stencil texture. if (this._filter === ShadowGenerator.FILTER_PCF) { - effect.setDepthStencilTexture("shadowSampler" + lightIndex, shadowMap); + effect.setDepthStencilTexture("shadowTexture" + lightIndex, shadowMap); light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), width, 1 / width, this.frustumEdgeFalloff, lightIndex); } else if (this._filter === ShadowGenerator.FILTER_PCSS) { for (let cascadeIndex = 0; cascadeIndex < this._numCascades; ++cascadeIndex) { @@ -989,14 +989,15 @@ export class CascadedShadowGenerator extends ShadowGenerator { ? 1 : (this._cascadeMaxExtents[cascadeIndex].z - this._cascadeMinExtents[cascadeIndex].z) / (this._cascadeMaxExtents[0].z - this._cascadeMinExtents[0].z); } - effect.setDepthStencilTexture("shadowSampler" + lightIndex, shadowMap); - effect.setTexture("depthSampler" + lightIndex, shadowMap); + effect.setDepthStencilTexture("shadowTexture" + lightIndex, shadowMap); + effect.setTexture("depthTexture" + lightIndex, shadowMap); + effect.setArray2("lightSizeUVCorrection" + lightIndex, this._lightSizeUVCorrection); effect.setArray("depthCorrection" + lightIndex, this._depthCorrection); effect.setFloat("penumbraDarkness" + lightIndex, this.penumbraDarkness); light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), 1 / width, this._contactHardeningLightSizeUVRatio * width, this.frustumEdgeFalloff, lightIndex); } else { - effect.setTexture("shadowSampler" + lightIndex, shadowMap); + effect.setTexture("shadowTexture" + lightIndex, shadowMap); light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), width, 1 / width, this.frustumEdgeFalloff, lightIndex); } diff --git a/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts b/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts index 30665af2ccb..ed76599fb9f 100644 --- a/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts +++ b/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts @@ -1783,12 +1783,13 @@ export class ShadowGenerator implements IShadowGenerator { } // Only PCF uses depth stencil texture. + const shadowMapForRendering = this.getShadowMapForRendering(); if (this._filter === ShadowGenerator.FILTER_PCF) { - effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering()); + effect.setDepthStencilTexture("shadowTexture" + lightIndex, shadowMapForRendering); light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), shadowMap.getSize().width, 1 / shadowMap.getSize().width, this.frustumEdgeFalloff, lightIndex); } else if (this._filter === ShadowGenerator.FILTER_PCSS) { - effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering()); - effect.setTexture("depthSampler" + lightIndex, this.getShadowMapForRendering()); + effect.setDepthStencilTexture("shadowTexture" + lightIndex, shadowMapForRendering); + effect.setTexture("depthTexture" + lightIndex, shadowMapForRendering); light._uniformBuffer.updateFloat4( "shadowsInfo", this.getDarkness(), @@ -1798,7 +1799,7 @@ export class ShadowGenerator implements IShadowGenerator { lightIndex ); } else { - effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering()); + effect.setTexture("shadowTexture" + lightIndex, shadowMapForRendering); light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), this.blurScale / shadowMap.getSize().width, this.depthScale, this.frustumEdgeFalloff, lightIndex); } diff --git a/packages/dev/core/src/Lights/spotLight.ts b/packages/dev/core/src/Lights/spotLight.ts index 26fc76fd5a2..30b3fed8d9c 100644 --- a/packages/dev/core/src/Lights/spotLight.ts +++ b/packages/dev/core/src/Lights/spotLight.ts @@ -383,7 +383,7 @@ export class SpotLight extends ShadowLight { this._computeProjectionTextureMatrix(); } effect.setMatrix("textureProjectionMatrix" + lightIndex, this._projectionTextureMatrix); - effect.setTexture("projectionLightSampler" + lightIndex, this.projectionTexture); + effect.setTexture("projectionLightTexture" + lightIndex, this.projectionTexture); } return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/Dual/fogBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Dual/fogBlock.ts index 11ec78524db..3caedef8dea 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Dual/fogBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Dual/fogBlock.ts @@ -13,6 +13,7 @@ import { RegisterClass } from "../../../../Misc/typeStore"; import "../../../../Shaders/ShadersInclude/fogFragmentDeclaration"; import { GetFogState } from "core/Materials/materialHelper.functions"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to add support for scene fog @@ -131,11 +132,28 @@ export class FogBlock extends NodeMaterialBlock { state.sharedData.blocksWithDefines.push(this); state.sharedData.bindableBlocks.push(this); + let replaceStrings = []; + let prefix1 = ""; + let prefix2 = ""; + + if (state.shaderLanguage === ShaderLanguage.WGSL) { + replaceStrings = [ + { search: /fn CalcFogFactor\(\)/, replace: "fn CalcFogFactor(vFogDistance: vec3f, vFogInfos: vec4f)" }, + { search: /uniforms.vFogInfos/g, replace: "vFogInfos" }, + { search: /fragmentInputs.vFogDistance/g, replace: "vFogDistance" }, + ]; + + prefix1 = "fragmentInputs."; + prefix2 = "uniforms."; + } else { + replaceStrings = [{ search: /float CalcFogFactor\(\)/, replace: "float CalcFogFactor(vec3 vFogDistance, vec4 vFogInfos)" }]; + } + state._emitFunctionFromInclude("fogFragmentDeclaration", `//${this.name}`, { removeUniforms: true, removeVaryings: true, removeIfDef: false, - replaceStrings: [{ search: /float CalcFogFactor\(\)/, replace: "float CalcFogFactor(vec3 vFogDistance, vec4 vFogInfos)" }], + replaceStrings: replaceStrings, }); const tempFogVariablename = state._getFreeVariableName("fog"); @@ -147,7 +165,7 @@ export class FogBlock extends NodeMaterialBlock { state._emitUniformFromString(this._fogParameters, NodeMaterialBlockConnectionPointTypes.Vector4); state.compilationString += `#ifdef FOG\n`; - state.compilationString += `float ${tempFogVariablename} = CalcFogFactor(${this._fogDistanceName}, ${this._fogParameters});\n`; + state.compilationString += `${state._declareLocalVar(tempFogVariablename, NodeMaterialBlockConnectionPointTypes.Float)} = CalcFogFactor(${prefix1}${this._fogDistanceName}, ${prefix2}${this._fogParameters});\n`; state.compilationString += state._declareOutput(output) + ` = ${tempFogVariablename} * ${color.associatedVariableName}.rgb + (1.0 - ${tempFogVariablename}) * ${fogColor.associatedVariableName}.rgb;\n`; @@ -158,7 +176,8 @@ export class FogBlock extends NodeMaterialBlock { const view = this.view; this._fogDistanceName = state._getFreeVariableName("vFogDistance"); state._emitVaryingFromString(this._fogDistanceName, NodeMaterialBlockConnectionPointTypes.Vector3); - state.compilationString += `${this._fogDistanceName} = (${view.associatedVariableName} * ${worldPos.associatedVariableName}).xyz;\n`; + const prefix = state.shaderLanguage === ShaderLanguage.WGSL ? "vertexOutputs." : ""; + state.compilationString += `${prefix}${this._fogDistanceName} = (${view.associatedVariableName} * ${worldPos.associatedVariableName}).xyz;\n`; } return this; diff --git a/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts index 6733ad03601..034670ad7db 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts @@ -105,7 +105,7 @@ export class ImageSourceBlock extends NodeMaterialBlock { super._buildBlock(state); if (state.target === NodeMaterialBlockTargets.Vertex) { - this._samplerName = state._getFreeVariableName(this.name + "Sampler"); + this._samplerName = state._getFreeVariableName(this.name + "Texture"); // Declarations state.sharedData.blockingBlocks.push(this); diff --git a/packages/dev/core/src/Materials/Node/Blocks/Dual/lightBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Dual/lightBlock.ts index ec6f048f12c..27b32bf4da6 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Dual/lightBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Dual/lightBlock.ts @@ -24,8 +24,9 @@ import "../../../../Shaders/ShadersInclude/helperFunctions"; import "../../../../Shaders/ShadersInclude/lightsFragmentFunctions"; import "../../../../Shaders/ShadersInclude/shadowsFragmentFunctions"; import "../../../../Shaders/ShadersInclude/shadowsVertex"; -import { Logger } from "core/Misc/logger"; +import { Logger } from "../../../../Misc/logger"; import { BindLight, BindLights, PrepareDefinesForLight, PrepareDefinesForLights, PrepareUniformsAndSamplersForLight } from "../../../materialHelper.functions"; +import { ShaderLanguage } from "../../../../Materials/shaderLanguage"; /** * Block used to add light in the fragment shader @@ -229,7 +230,7 @@ export class LightBlock extends NodeMaterialBlock { if (!this.light) { BindLights(scene, mesh, effect, true, nodeMaterial.maxSimultaneousLights); } else { - BindLight(this.light, this._lightId, scene, effect, true); + BindLight(this.light, this._lightId, scene, effect, true, undefined); } } @@ -262,8 +263,9 @@ export class LightBlock extends NodeMaterialBlock { // Inject code in vertex const worldPosVaryingName = "v_" + worldPos.associatedVariableName; + if (state._emitVaryingFromString(worldPosVaryingName, NodeMaterialBlockConnectionPointTypes.Vector4)) { - state.compilationString += `${worldPosVaryingName} = ${worldPos.associatedVariableName};\n`; + state.compilationString += (state.shaderLanguage === ShaderLanguage.WGSL ? "vertexOutputs." : "") + `${worldPosVaryingName} = ${worldPos.associatedVariableName};\n`; } if (this.light) { @@ -274,9 +276,9 @@ export class LightBlock extends NodeMaterialBlock { ], }); } else { - state.compilationString += `vec4 worldPos = ${worldPos.associatedVariableName};\n`; + state.compilationString += `${state._declareLocalVar("worldPos", NodeMaterialBlockConnectionPointTypes.Vector4)} = ${worldPos.associatedVariableName};\n`; if (this.view.isConnected) { - state.compilationString += `mat4 view = ${this.view.associatedVariableName};\n`; + state.compilationString += `${state._declareLocalVar("view", NodeMaterialBlockConnectionPointTypes.Matrix)} = ${this.view.associatedVariableName};\n`; } state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, { repeatKey: "maxSimultaneousLights", @@ -284,31 +286,54 @@ export class LightBlock extends NodeMaterialBlock { } } + private _injectUBODeclaration(state: NodeMaterialBuildState) { + const comments = `//${this.name}`; + + if (!this.light) { + // Emit for all lights + state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, { + repeatKey: "maxSimultaneousLights", + substitutionVars: this.generateOnlyFragmentCode ? "varying," : undefined, + }); + } else { + state._emitFunctionFromInclude( + state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", + comments, + { + replaceStrings: [{ search: /{X}/g, replace: this._lightId.toString() }], + }, + this._lightId.toString() + ); + } + } + protected override _buildBlock(state: NodeMaterialBuildState) { super._buildBlock(state); + const isWGSL = state.shaderLanguage === ShaderLanguage.WGSL; + const addF = isWGSL ? "f" : ""; + + const comments = `//${this.name}`; + if (state.target !== NodeMaterialBlockTargets.Fragment) { // Vertex this._injectVertexCode(state); - return; } if (this.generateOnlyFragmentCode) { state.sharedData.dynamicUniformBlocks.push(this); } - // Fragment + const accessor = state.shaderLanguage === ShaderLanguage.WGSL ? "fragmentInputs." : ""; state.sharedData.forcedBindableBlocks.push(this); state.sharedData.blocksWithDefines.push(this); - - const comments = `//${this.name}`; const worldPos = this.worldPosition; let worldPosVariableName = worldPos.associatedVariableName; if (this.generateOnlyFragmentCode) { worldPosVariableName = state._getFreeVariableName("globalWorldPos"); - state._emitFunction("light_globalworldpos", `vec3 ${worldPosVariableName};\n`, comments); + state._emitFunction("light_globalworldpos", `${state._declareLocalVar(worldPosVariableName, NodeMaterialBlockConnectionPointTypes.Vector3)};\n`, comments); state.compilationString += `${worldPosVariableName} = ${worldPos.associatedVariableName}.xyz;\n`; state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, { @@ -316,7 +341,7 @@ export class LightBlock extends NodeMaterialBlock { substitutionVars: this.generateOnlyFragmentCode ? `worldPos,${worldPos.associatedVariableName}` : undefined, }); } else { - worldPosVariableName = "v_" + worldPosVariableName + ".xyz"; + worldPosVariableName = accessor + "v_" + worldPosVariableName + ".xyz"; } state._emitFunctionFromInclude("helperFunctions", comments); @@ -329,38 +354,23 @@ export class LightBlock extends NodeMaterialBlock { replaceStrings: [{ search: /vPositionW/g, replace: worldPosVariableName }], }); - if (!this.light) { - // Emit for all lights - state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, { - repeatKey: "maxSimultaneousLights", - substitutionVars: this.generateOnlyFragmentCode ? "varying," : undefined, - }); - } else { - state._emitFunctionFromInclude( - state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", - comments, - { - replaceStrings: [{ search: /{X}/g, replace: this._lightId.toString() }], - }, - this._lightId.toString() - ); - } + this._injectUBODeclaration(state); // Code if (this._lightId === 0) { if (state._registerTempVariable("viewDirectionW")) { - state.compilationString += `vec3 viewDirectionW = normalize(${this.cameraPosition.associatedVariableName} - ${worldPosVariableName});\n`; + state.compilationString += `${state._declareLocalVar("viewDirectionW", NodeMaterialBlockConnectionPointTypes.Vector3)} = normalize(${this.cameraPosition.associatedVariableName} - ${worldPosVariableName});\n`; } - state.compilationString += `lightingInfo info;\n`; - state.compilationString += `float shadow = 1.;\n`; - state.compilationString += `float aggShadow = 0.;\n`; - state.compilationString += `float numLights = 0.;\n`; - state.compilationString += `float glossiness = ${this.glossiness.isConnected ? this.glossiness.associatedVariableName : "1.0"} * ${ + state.compilationString += isWGSL ? `var info: lightingInfo;\n` : `lightingInfo info;\n`; + state.compilationString += `${state._declareLocalVar("shadow", NodeMaterialBlockConnectionPointTypes.Float)} = 1.;\n`; + state.compilationString += `${state._declareLocalVar("aggShadow", NodeMaterialBlockConnectionPointTypes.Float)} = 0.;\n`; + state.compilationString += `${state._declareLocalVar("numLights", NodeMaterialBlockConnectionPointTypes.Float)} = 0.;\n`; + state.compilationString += `${state._declareLocalVar("glossiness", NodeMaterialBlockConnectionPointTypes.Float)} = ${this.glossiness.isConnected ? this.glossiness.associatedVariableName : "1.0"} * ${ this.glossPower.isConnected ? this.glossPower.associatedVariableName : "1024.0" };\n`; - state.compilationString += `vec3 diffuseBase = vec3(0., 0., 0.);\n`; - state.compilationString += `vec3 specularBase = vec3(0., 0., 0.);\n`; - state.compilationString += `vec3 normalW = ${this.worldNormal.associatedVariableName}.xyz;\n`; + state.compilationString += `${state._declareLocalVar("diffuseBase", NodeMaterialBlockConnectionPointTypes.Vector3)} = vec3${addF}(0., 0., 0.);\n`; + state.compilationString += `${state._declareLocalVar("specularBase", NodeMaterialBlockConnectionPointTypes.Vector3)} = vec3${addF}(0., 0., 0.);\n`; + state.compilationString += `${state._declareLocalVar("normalW", NodeMaterialBlockConnectionPointTypes.Vector3)} = ${this.worldNormal.associatedVariableName}.xyz;\n`; } if (this.light) { diff --git a/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts index e096490ce4c..245575edc84 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts @@ -21,7 +21,6 @@ import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConne import { EngineStore } from "../../../../Engines/engineStore"; import type { PrePassTextureBlock } from "../Input/prePassTextureBlock"; import { ShaderLanguage } from "core/Materials/shaderLanguage"; -import type { WebGPUEngine } from "core/Engines"; /** * Block used to read a texture from a sampler @@ -32,7 +31,6 @@ export class TextureBlock extends NodeMaterialBlock { private _gammaDefineName: string; private _tempTextureRead: string; private _samplerName: string; - private _textureName: string; private _transformedUVName: string; private _textureTransformName: string; private _textureInfoName: string; @@ -413,17 +411,7 @@ export class TextureBlock extends NodeMaterialBlock { } if (!this._imageSource) { - if (this._textureName) { - const engineWebGPU = effect.getEngine() as WebGPUEngine; - - effect.setTexture(this._textureName, this.texture); - const setTextureSampler = engineWebGPU.setTextureSampler; - if (setTextureSampler) { - setTextureSampler.call(engineWebGPU, this._samplerName, this.texture._texture); - } - } else { - effect.setTexture(this._samplerName, this.texture); - } + effect.setTexture(this._samplerName, this.texture); } } @@ -503,7 +491,7 @@ export class TextureBlock extends NodeMaterialBlock { private _generateTextureSample(uv: string, state: NodeMaterialBuildState) { if (state.shaderLanguage === ShaderLanguage.WGSL) { const isVertex = state.target === NodeMaterialBlockTargets.Vertex; - return `${this._samplerFunc(state)}(${this._textureName},${this.samplerName}, ${this._getUVW(uv)}${this._samplerLodSuffix}${isVertex ? ", 0" : ""})`; + return `${this._samplerFunc(state)}(${this.samplerName},${this.samplerName + Constants.AUTOSAMPLERSUFFIX}, ${this._getUVW(uv)}${this._samplerLodSuffix}${isVertex ? ", 0" : ""})`; } return `${this._samplerFunc(state)}(${this.samplerName}, ${this._getUVW(uv)}${this._samplerLodSuffix})`; } @@ -597,15 +585,12 @@ export class TextureBlock extends NodeMaterialBlock { if ((!this._isMixed && state.target === NodeMaterialBlockTargets.Fragment) || (this._isMixed && state.target === NodeMaterialBlockTargets.Vertex)) { if (!this._imageSource) { const varName = state._getFreeVariableName(this.name); - this._samplerName = varName + "Sampler"; - if (state.shaderLanguage === ShaderLanguage.WGSL) { - this._textureName = varName + "Texture"; - } + this._samplerName = varName + "Texture"; if (this._texture?._texture?.is2DArray) { state._emit2DArraySampler(this._samplerName); } else { - state._emit2DSampler(this._samplerName, this._textureName); + state._emit2DSampler(this._samplerName); } } @@ -632,7 +617,7 @@ export class TextureBlock extends NodeMaterialBlock { if (this._texture?._texture?.is2DArray) { state._emit2DArraySampler(this._samplerName); } else { - state._emit2DSampler(this._samplerName, this._textureName); + state._emit2DSampler(this._samplerName); } } diff --git a/packages/dev/core/src/Materials/Node/Blocks/Fragment/discardBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Fragment/discardBlock.ts index 4524820d0db..40914b4d0a4 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Fragment/discardBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Fragment/discardBlock.ts @@ -50,7 +50,7 @@ export class DiscardBlock extends NodeMaterialBlock { return; } - state.compilationString += `if (${this.value.associatedVariableName} < ${this.cutoff.associatedVariableName}) discard;\n`; + state.compilationString += `if (${this.value.associatedVariableName} < ${this.cutoff.associatedVariableName}) { discard; }\n`; return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragCoordBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragCoordBlock.ts index 04a277b9496..a6f1c1fd337 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragCoordBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragCoordBlock.ts @@ -4,6 +4,7 @@ import type { NodeMaterialBuildState } from "../../nodeMaterialBuildState"; import type { NodeMaterialConnectionPoint } from "../../nodeMaterialBlockConnectionPoint"; import { NodeMaterialBlockTargets } from "../../Enums/nodeMaterialBlockTargets"; import { RegisterClass } from "../../../../Misc/typeStore"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to make gl_FragCoord available @@ -86,9 +87,11 @@ export class FragCoordBlock extends NodeMaterialBlock { protected writeOutputs(state: NodeMaterialBuildState): string { let code = ""; + const coord = state.shaderLanguage === ShaderLanguage.WGSL ? "fragmentInputs.position" : "gl_FragCoord"; + for (const output of this._outputs) { if (output.hasEndpoints) { - code += `${state._declareOutput(output)} = gl_FragCoord.${output.name};\n`; + code += `${state._declareOutput(output)} = ${coord}.${output.name};\n`; } } diff --git a/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragDepthBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragDepthBlock.ts index 4255738139d..b885a1477cf 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragDepthBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Fragment/fragDepthBlock.ts @@ -5,6 +5,7 @@ import { NodeMaterialBlockTargets } from "../../Enums/nodeMaterialBlockTargets"; import type { NodeMaterialConnectionPoint } from "../../nodeMaterialBlockConnectionPoint"; import { RegisterClass } from "../../../../Misc/typeStore"; import { Logger } from "core/Misc/logger"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to write the fragment depth */ @@ -53,16 +54,18 @@ export class FragDepthBlock extends NodeMaterialBlock { protected override _buildBlock(state: NodeMaterialBuildState) { super._buildBlock(state); + const fragDepth = state.shaderLanguage === ShaderLanguage.GLSL ? "gl_FragDepth" : "fragmentOutputs.fragDepth"; + if (this.depth.isConnected) { - state.compilationString += `gl_FragDepth = ${this.depth.associatedVariableName};\n`; + state.compilationString += `${fragDepth} = ${this.depth.associatedVariableName};\n`; } else if (this.worldPos.isConnected && this.viewProjection.isConnected) { state.compilationString += ` - vec4 p = ${this.viewProjection.associatedVariableName} * ${this.worldPos.associatedVariableName}; - float v = p.z / p.w; + ${state._declareLocalVar("p", NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this.viewProjection.associatedVariableName} * ${this.worldPos.associatedVariableName}; + ${state._declareLocalVar("v", NodeMaterialBlockConnectionPointTypes.Vector4)} = p.z / p.w; #ifndef IS_NDC_HALF_ZRANGE v = v * 0.5 + 0.5; #endif - gl_FragDepth = v; + ${fragDepth} = v; `; } else { diff --git a/packages/dev/core/src/Materials/Node/Blocks/Fragment/frontFacingBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Fragment/frontFacingBlock.ts index a8c488e24ec..7806207cb70 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Fragment/frontFacingBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Fragment/frontFacingBlock.ts @@ -4,6 +4,7 @@ import type { NodeMaterialBuildState } from "../../nodeMaterialBuildState"; import type { NodeMaterialConnectionPoint } from "../../nodeMaterialBlockConnectionPoint"; import { NodeMaterialBlockTargets } from "../../Enums/nodeMaterialBlockTargets"; import { RegisterClass } from "../../../../Misc/typeStore"; +import { ShaderLanguage } from "../../../../Materials/shaderLanguage"; /** * Block used to test if the fragment shader is front facing */ @@ -43,7 +44,9 @@ export class FrontFacingBlock extends NodeMaterialBlock { const output = this._outputs[0]; - state.compilationString += state._declareOutput(output) + ` = gl_FrontFacing ? 1.0 : 0.0;\n`; + state.compilationString += + state._declareOutput(output) + + ` = ${state._generateTertiary("1.0", "0.0", state.shaderLanguage === ShaderLanguage.GLSL ? "gl_FrontFacing" : "fragmentInputs.frontFacing")};\n`; return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/Input/inputBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Input/inputBlock.ts index 929f112e8de..98e5afebae5 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Input/inputBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Input/inputBlock.ts @@ -504,7 +504,7 @@ export class InputBlock extends NodeMaterialBlock { return; } state.constants.push(this.associatedVariableName); - state._constantDeclaration += state._declareOutput(this.output) + ` = ${this._emitConstant(state)};\n`; + state._constantDeclaration += state._declareOutput(this.output, true) + ` = ${this._emitConstant(state)};\n`; return; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/Vertex/instancesBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Vertex/instancesBlock.ts index 58c60aceb14..68a3aacc218 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Vertex/instancesBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Vertex/instancesBlock.ts @@ -9,6 +9,7 @@ import { NodeMaterialSystemValues } from "../../Enums/nodeMaterialSystemValues"; import { InputBlock } from "../Input/inputBlock"; import { RegisterClass } from "../../../../Misc/typeStore"; import type { SubMesh } from "../../../../Meshes/subMesh"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to add support for instances @@ -172,15 +173,24 @@ export class InstancesBlock extends NodeMaterialBlock { const world2 = this.world2; const world3 = this.world3; + let mat4 = "mat4"; + let instance = "gl_InstanceID"; + let floatCast = "float"; + if (state.shaderLanguage === ShaderLanguage.WGSL) { + mat4 = "mat4x4f"; + instance = "vertexInputs.instanceIndex"; + floatCast = "f32"; + } + state.compilationString += `#ifdef INSTANCES\n`; state.compilationString += state._declareOutput(output) + - ` = mat4(${world0.associatedVariableName}, ${world1.associatedVariableName}, ${world2.associatedVariableName}, ${world3.associatedVariableName});\n`; + ` = ${mat4}(${world0.associatedVariableName}, ${world1.associatedVariableName}, ${world2.associatedVariableName}, ${world3.associatedVariableName});\n`; state.compilationString += `#ifdef THIN_INSTANCES\n`; state.compilationString += `${output.associatedVariableName} = ${this.world.associatedVariableName} * ${output.associatedVariableName};\n`; state.compilationString += `#endif\n`; if (engine._caps.canUseGLInstanceID) { - state.compilationString += state._declareOutput(instanceID) + ` = float(gl_InstanceID);\n`; + state.compilationString += state._declareOutput(instanceID) + ` = ${floatCast}(${instance});\n`; } else { state.compilationString += state._declareOutput(instanceID) + ` = 0.0;\n`; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/biPlanarBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/biPlanarBlock.ts index c34c8a87abc..81eb9f262c0 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/biPlanarBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/biPlanarBlock.ts @@ -1,6 +1,9 @@ import type { NodeMaterialBuildState } from "../nodeMaterialBuildState"; import { RegisterClass } from "../../../Misc/typeStore"; import { TriPlanarBlock } from "./triPlanarBlock"; +import { NodeMaterialBlockConnectionPointTypes } from "../Enums/nodeMaterialBlockConnectionPointTypes"; +import { ShaderLanguage } from "../../../Materials/shaderLanguage"; +import { Constants } from "../../../Engines/constants"; /** * Block used to read a texture with triplanar mapping (see https://iquilezles.org/articles/biplanar/) @@ -22,55 +25,89 @@ export class BiPlanarBlock extends TriPlanarBlock { return "BiPlanarBlock"; } + private _declareLocalVarAsVec3I(name: string, state: NodeMaterialBuildState): string { + if (state.shaderLanguage === ShaderLanguage.WGSL) { + return `var ${name}: vec3`; + } else { + return `ivec3 ${name}`; + } + } + + private _getTextureGrad(state: NodeMaterialBuildState, samplerName: string) { + if (state.shaderLanguage === ShaderLanguage.WGSL) { + return `textureSampleGrad(${samplerName},${samplerName + Constants.AUTOSAMPLERSUFFIX}`; + } + + return `textureGrad(${samplerName}`; + } + protected override _generateTextureLookup(state: NodeMaterialBuildState): void { const samplerName = this.samplerName; const samplerYName = this.samplerYName ?? this.samplerName; const sharpness = this.sharpness.isConnected ? this.sharpness.associatedVariableName : "1.0"; - const dpdx = state._getFreeVariableName("dpdx"); - const dpdy = state._getFreeVariableName("dpdy"); + const dpdx = state._getFreeVariableName("dxValue"); + const dpdy = state._getFreeVariableName("dyValue"); const n = state._getFreeVariableName("n"); const ma = state._getFreeVariableName("ma"); const mi = state._getFreeVariableName("mi"); const me = state._getFreeVariableName("me"); const x = state._getFreeVariableName("x"); const y = state._getFreeVariableName("y"); - const w = state._getFreeVariableName("y"); + const w = state._getFreeVariableName("w"); + + let ivec3 = "ivec3"; + let dpdxFunc = "dFdx"; + let dpdyFunc = "dFdy"; + let suffix = ""; + + if (state.shaderLanguage === ShaderLanguage.WGSL) { + ivec3 = "vec3"; + dpdxFunc = "dpdx"; + dpdyFunc = "dpdy"; + suffix = "f"; + } state.compilationString += ` // grab coord derivatives for texturing - vec3 ${dpdx} = dFdx(${this.position.associatedVariableName}.xyz); - vec3 ${dpdy} = dFdy(${this.position.associatedVariableName}.xyz); - vec3 ${n} = abs(${this.normal.associatedVariableName}.xyz); + ${state._declareLocalVar(dpdx, NodeMaterialBlockConnectionPointTypes.Vector3)} = ${dpdxFunc}(${this.position.associatedVariableName}.xyz); + ${state._declareLocalVar(dpdy, NodeMaterialBlockConnectionPointTypes.Vector3)} = ${dpdyFunc}(${this.position.associatedVariableName}.xyz); + ${state._declareLocalVar(n, NodeMaterialBlockConnectionPointTypes.Vector3)} = abs(${this.normal.associatedVariableName}.xyz); // determine major axis (in x; yz are following axis) - ivec3 ${ma} = (${n}.x>${n}.y && ${n}.x>${n}.z) ? ivec3(0,1,2) : - (${n}.y>${n}.z) ? ivec3(1,2,0) : - ivec3(2,0,1) ; + ${this._declareLocalVarAsVec3I(ma, state)} = ${state._generateTertiary( + `${ivec3}(0,1,2)`, + `${state._generateTertiary(`${ivec3}(1,2,0)`, `${ivec3}(2,0,1)`, `(${n}.y>${n}.z)`)}`, + `(${n}.x>${n}.y && ${n}.x>${n}.z)` + )}; + // determine minor axis (in x; yz are following axis) - ivec3 ${mi} = (${n}.x<${n}.y && ${n}.x<${n}.z) ? ivec3(0,1,2) : - (${n}.y<${n}.z) ? ivec3(1,2,0) : - ivec3(2,0,1) ; + ${this._declareLocalVarAsVec3I(mi, state)} = ${state._generateTertiary( + `${ivec3}(0,1,2)`, + `${state._generateTertiary(`${ivec3}(1,2,0)`, `${ivec3}(2,0,1)`, `(${n}.y<${n}.z)`)}`, + `(${n}.x<${n}.y && ${n}.x<${n}.z)` + )}; + // determine median axis (in x; yz are following axis) - ivec3 ${me} = ivec3(3) - ${mi} - ${ma}; + ${this._declareLocalVarAsVec3I(me, state)} = ${ivec3}(3) - ${mi} - ${ma}; // project+fetch - vec4 ${x} = textureGrad( ${samplerName}, vec2( ${this.position.associatedVariableName}[${ma}.y], ${this.position.associatedVariableName}[${ma}.z]), - vec2(${dpdx}[${ma}.y],${dpdx}[${ma}.z]), - vec2(${dpdy}[${ma}.y],${dpdy}[${ma}.z]) ); - vec4 ${y} = textureGrad( ${samplerYName}, vec2( ${this.position.associatedVariableName}[${me}.y], ${this.position.associatedVariableName}[${me}.z]), - vec2(${dpdx}[${me}.y],${dpdx}[${me}.z]), - vec2(${dpdy}[${me}.y],${dpdy}[${me}.z]) ); + ${state._declareLocalVar(x, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._getTextureGrad(state, samplerName)}, vec2${suffix}(${this.position.associatedVariableName}[${ma}.y], ${this.position.associatedVariableName}[${ma}.z]), + vec2${suffix}(${dpdx}[${ma}.y],${dpdx}[${ma}.z]), + vec2${suffix}(${dpdy}[${ma}.y],${dpdy}[${ma}.z])); + ${state._declareLocalVar(y, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._getTextureGrad(state, samplerYName)}, vec2${suffix}(${this.position.associatedVariableName}[${me}.y], ${this.position.associatedVariableName}[${me}.z]), + vec2${suffix}(${dpdx}[${me}.y],${dpdx}[${me}.z]), + vec2${suffix}(${dpdy}[${me}.y],${dpdy}[${me}.z])); // blend factors - vec2 ${w} = vec2(${n}[${ma}.x],${n}[${me}.x]); + ${state._declareLocalVar(w, NodeMaterialBlockConnectionPointTypes.Vector2)} = vec2${suffix}(${n}[${ma}.x],${n}[${me}.x]); // make local support - ${w} = clamp( (${w}-0.5773)/(1.0-0.5773), 0.0, 1.0 ); + ${w} = clamp( (${w}-0.5773)/(1.0-0.5773), vec2${suffix}(0.0), vec2${suffix}(1.0) ); // shape transition - ${w} = pow( ${w}, vec2(${sharpness}/8.0) ); + ${w} = pow( ${w}, vec2${suffix}(${sharpness}/8.0) ); // blend and return - vec4 ${this._tempTextureRead} = (${x}*${w}.x + ${y}*${w}.y) / (${w}.x + ${w}.y); + ${state._declareLocalVar(this._tempTextureRead, NodeMaterialBlockConnectionPointTypes.Vector4)} = (${x}*${w}.x + ${y}*${w}.y) / (${w}.x + ${w}.y); `; } } diff --git a/packages/dev/core/src/Materials/Node/Blocks/clampBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/clampBlock.ts index d67a46147ea..aa31f010ce9 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/clampBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/clampBlock.ts @@ -6,6 +6,7 @@ import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; import { RegisterClass } from "../../../Misc/typeStore"; import type { Scene } from "../../../scene"; import { editableInPropertyPage, PropertyTypeForEdition } from "../../../Decorators/nodeDecorator"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to clamp a float @@ -58,8 +59,11 @@ export class ClampBlock extends NodeMaterialBlock { const output = this._outputs[0]; + const cast = state.shaderLanguage === ShaderLanguage.WGSL ? state._getShaderType(this.value.type) : ""; + state.compilationString += - state._declareOutput(output) + ` = clamp(${this.value.associatedVariableName}, ${this._writeFloat(this.minimum)}, ${this._writeFloat(this.maximum)});\n`; + state._declareOutput(output) + + ` = clamp(${this.value.associatedVariableName}, ${cast}(${this._writeFloat(this.minimum)}), ${cast}(${this._writeFloat(this.maximum)}));\n`; return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/cloudBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/cloudBlock.ts index 9d6f7e5b9f0..49a8687e8f0 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/cloudBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/cloudBlock.ts @@ -6,6 +6,7 @@ import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; import { RegisterClass } from "../../../Misc/typeStore"; import { editableInPropertyPage, PropertyTypeForEdition } from "../../../Decorators/nodeDecorator"; import type { Scene } from "../../../scene"; +import { ShaderLanguage } from "../../../Materials/shaderLanguage"; /** * block used to Generate Fractal Brownian Motion Clouds */ @@ -93,13 +94,18 @@ export class CloudBlock extends NodeMaterialBlock { return; } - const functionString = ` + let functionString = ` - float cloudRandom(in float p) { p = fract(p * 0.011); p *= p + 7.5; p *= p + p; return fract(p); } + float cloudRandom(float p) { + float temp = fract(p * 0.011); + temp *= temp + 7.5; + temp *= temp + temp; + return fract(temp); + } // Based on Morgan McGuire @morgan3d // https://www.shadertoy.com/view/4dS3Wd - float cloudNoise(in vec2 x, in vec2 chaos) { + float cloudNoise2(vec2 x, vec2 chaos) { vec2 step = chaos * vec2(75., 120.) + vec2(75., 120.); vec2 i = floor(x); @@ -115,7 +121,7 @@ export class CloudBlock extends NodeMaterialBlock { ); } - float cloudNoise(in vec3 x, in vec3 chaos) { + float cloudNoise3(vec3 x, vec3 chaos) { vec3 step = chaos * vec3(60., 120., 75.) + vec3(60., 120., 75.); vec3 i = floor(x); @@ -130,34 +136,41 @@ export class CloudBlock extends NodeMaterialBlock { mix( cloudRandom(n + dot(step, vec3(0, 1, 1))), cloudRandom(n + dot(step, vec3(1, 1, 1))), u.x), u.y), u.z); }`; - const fractalBrownianString = ` - float fbm(in vec2 st, in vec2 chaos) { + let fractalBrownianString = ` + float fbm2(vec2 st, vec2 chaos) { // Initial values float value = 0.0; float amplitude = .5; float frequency = 0.; // Loop of octaves + vec2 tempST = st; for (int i = 0; i < OCTAVES; i++) { - value += amplitude * cloudNoise(st, chaos); - st *= 2.0; + value += amplitude * cloudNoise2(tempST, chaos); + tempST *= 2.0; amplitude *= 0.5; } return value; } - float fbm(in vec3 x, in vec3 chaos) { + float fbm3(vec3 x, vec3 chaos) { // Initial values float value = 0.0; float amplitude = 0.5; - for (int i = 0; i < OCTAVES; ++i) { - value += amplitude * cloudNoise(x, chaos); - x = x * 2.0; + vec3 tempX = x; + for (int i = 0; i < OCTAVES; i++) { + value += amplitude * cloudNoise3(tempX, chaos); + tempX = tempX * 2.0; amplitude *= 0.5; } return value; }`; + if (state.shaderLanguage === ShaderLanguage.WGSL) { + functionString = state._babylonSLtoWGSL(functionString); + fractalBrownianString = state._babylonSLtoWGSL(fractalBrownianString); + } + const fbmNewName = `fbm${this.octaves}`; state._emitFunction("CloudBlockCode", functionString, "// CloudBlockCode"); state._emitFunction( @@ -167,16 +180,16 @@ export class CloudBlock extends NodeMaterialBlock { ); const localVariable = state._getFreeVariableName("st"); - const seedType = this.seed.connectedPoint?.type === NodeMaterialBlockConnectionPointTypes.Vector2 ? "vec2" : "vec3"; + const seedType = this.seed.connectedPoint?.type || NodeMaterialBlockConnectionPointTypes.Vector3; - state.compilationString += `${seedType} ${localVariable} = ${this.seed.associatedVariableName};\n`; + state.compilationString += `${state._declareLocalVar(localVariable, seedType)} = ${this.seed.associatedVariableName};\n`; if (this.offsetX.isConnected) { state.compilationString += `${localVariable}.x += 0.1 * ${this.offsetX.associatedVariableName};\n`; } if (this.offsetY.isConnected) { state.compilationString += `${localVariable}.y += 0.1 * ${this.offsetY.associatedVariableName};\n`; } - if (this.offsetZ.isConnected && seedType === "vec3") { + if (this.offsetZ.isConnected && seedType === NodeMaterialBlockConnectionPointTypes.Vector3) { state.compilationString += `${localVariable}.z += 0.1 * ${this.offsetZ.associatedVariableName};\n`; } @@ -184,10 +197,13 @@ export class CloudBlock extends NodeMaterialBlock { if (this.chaos.isConnected) { chaosValue = this.chaos.associatedVariableName; } else { - chaosValue = this.seed.connectedPoint?.type === NodeMaterialBlockConnectionPointTypes.Vector2 ? "vec2(0., 0.)" : "vec3(0., 0., 0.)"; + const addF = state.shaderLanguage === ShaderLanguage.WGSL ? "f" : ""; + chaosValue = this.seed.connectedPoint?.type === NodeMaterialBlockConnectionPointTypes.Vector2 ? `vec2${addF}(0., 0.)` : `vec3${addF}(0., 0., 0.)`; } - state.compilationString += state._declareOutput(this._outputs[0]) + ` = ${fbmNewName}(${localVariable}, ${chaosValue});\n`; + state.compilationString += + state._declareOutput(this._outputs[0]) + + ` = ${fbmNewName}${this.seed.connectedPoint?.type === NodeMaterialBlockConnectionPointTypes.Vector2 ? "2" : "3"}(${localVariable}, ${chaosValue});\n`; return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/matrixBuilderBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/matrixBuilderBlock.ts index 7f3722b9c97..234a5b5b884 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/matrixBuilderBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/matrixBuilderBlock.ts @@ -6,6 +6,7 @@ import type { NodeMaterialConnectionPoint } from "../nodeMaterialBlockConnection import { RegisterClass } from "../../../Misc/typeStore"; import { InputBlock } from "./Input/inputBlock"; import { Vector4 } from "../../../Maths/math.vector"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to build a matrix from 4 Vector4 @@ -103,9 +104,11 @@ export class MatrixBuilderBlock extends NodeMaterialBlock { const row2 = this.row2; const row3 = this.row3; + const mat4 = state.shaderLanguage === ShaderLanguage.WGSL ? "mat4x4f" : "mat4"; + state.compilationString += state._declareOutput(output) + - ` = mat4(${row0.associatedVariableName}, ${row1.associatedVariableName}, ${row2.associatedVariableName}, ${row3.associatedVariableName});\n`; + ` = ${mat4}(${row0.associatedVariableName}, ${row1.associatedVariableName}, ${row2.associatedVariableName}, ${row3.associatedVariableName});\n`; return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/matrixTransposeBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/matrixTransposeBlock.ts index 281a4da9477..44cd346dda5 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/matrixTransposeBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/matrixTransposeBlock.ts @@ -48,7 +48,7 @@ export class MatrixTransposeBlock extends NodeMaterialBlock { const output = this.output; const input = this.input; - state.compilationString += state._declareOutput(output) + `${output.associatedVariableName} = transpose(${input.associatedVariableName});\n`; + state.compilationString += state._declareOutput(output) + ` = transpose(${input.associatedVariableName});\n`; return this; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/normalBlendBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/normalBlendBlock.ts index 500c37fb3f7..ee6032345ec 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/normalBlendBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/normalBlendBlock.ts @@ -72,8 +72,8 @@ export class NormalBlendBlock extends NodeMaterialBlock { const stepR = state._getFreeVariableName("stepR"); const stepG = state._getFreeVariableName("stepG"); - state.compilationString += `float ${stepR} = step(0.5, ${input0.associatedVariableName}.r);\n`; - state.compilationString += `float ${stepG} = step(0.5, ${input0.associatedVariableName}.g);\n`; + state.compilationString += `${state._declareLocalVar(stepR, NodeMaterialBlockConnectionPointTypes.Float)} = step(0.5, ${input0.associatedVariableName}.r);\n`; + state.compilationString += `${state._declareLocalVar(stepG, NodeMaterialBlockConnectionPointTypes.Float)} = step(0.5, ${input0.associatedVariableName}.g);\n`; state.compilationString += state._declareOutput(output) + `;\n`; state.compilationString += `${output.associatedVariableName}.r = (1.0 - ${stepR}) * ${input0.associatedVariableName}.r * ${input1.associatedVariableName}.r * 2.0 + ${stepR} * (1.0 - (1.0 - ${input0.associatedVariableName}.r) * (1.0 - ${input1.associatedVariableName}.r) * 2.0);\n`; state.compilationString += `${output.associatedVariableName}.g = (1.0 - ${stepG}) * ${input0.associatedVariableName}.g * ${input1.associatedVariableName}.g * 2.0 + ${stepG} * (1.0 - (1.0 - ${input0.associatedVariableName}.g) * (1.0 - ${input1.associatedVariableName}.g) * 2.0);\n`; diff --git a/packages/dev/core/src/Materials/Node/Blocks/simplexPerlin3DBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/simplexPerlin3DBlock.ts index 71e2e4446d6..b1dd1ddcdd4 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/simplexPerlin3DBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/simplexPerlin3DBlock.ts @@ -4,6 +4,7 @@ import type { NodeMaterialBuildState } from "../nodeMaterialBuildState"; import type { NodeMaterialConnectionPoint } from "../nodeMaterialBlockConnectionPoint"; import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; import { RegisterClass } from "../../../Misc/typeStore"; +import { ShaderLanguage } from "../../../Materials/shaderLanguage"; /** * block used to Generate a Simplex Perlin 3d Noise Pattern */ @@ -84,8 +85,9 @@ export class SimplexPerlin3DBlock extends NodeMaterialBlock { functionString += `const float UNSKEWFACTOR = 1.0/6.0;\n`; functionString += `const float SIMPLEX_CORNER_POS = 0.5;\n`; functionString += `const float SIMPLEX_TETRAHADRON_HEIGHT = 0.70710678118654752440084436210485;\n`; - functionString += `float SimplexPerlin3D( vec3 P ){\n`; - functionString += ` P.x = P == vec3(0., 0., 0.) ? 0.00001 : P.x;\n`; + functionString += `float SimplexPerlin3D( vec3 source ){\n`; + functionString += ` vec3 P = source;\n`; + functionString += ` P.x = [P.x == 0. && P.y == 0. && P.z == 0. ? 0.00001 : P.x];\n`; functionString += ` P *= SIMPLEX_TETRAHADRON_HEIGHT;\n`; functionString += ` vec3 Pi = floor( P + dot( P, vec3( SKEWFACTOR) ) );`; functionString += ` vec3 x0 = P - Pi + dot(Pi, vec3( UNSKEWFACTOR ) );\n`; @@ -99,7 +101,7 @@ export class SimplexPerlin3DBlock extends NodeMaterialBlock { functionString += ` vec4 v1234_x = vec4( x0.x, x1.x, x2.x, x3.x );\n`; functionString += ` vec4 v1234_y = vec4( x0.y, x1.y, x2.y, x3.y );\n`; functionString += ` vec4 v1234_z = vec4( x0.z, x1.z, x2.z, x3.z );\n`; - functionString += ` Pi.xyz = Pi.xyz - floor(Pi.xyz * ( 1.0 / 69.0 )) * 69.0;\n`; + functionString += ` Pi = Pi.xyz - floor(Pi.xyz * ( 1.0 / 69.0 )) * 69.0;\n`; functionString += ` vec3 Pi_inc1 = step( Pi, vec3( 69.0 - 1.5 ) ) * ( Pi + 1.0 );\n`; functionString += ` vec4 Pt = vec4( Pi.xy, Pi_inc1.xy ) + vec2( 50.0, 161.0 ).xyxy;\n`; functionString += ` Pt *= Pt;\n`; @@ -109,19 +111,25 @@ export class SimplexPerlin3DBlock extends NodeMaterialBlock { functionString += ` const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n`; functionString += ` vec3 lowz_mods = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + Pi.zzz * ZINC.xyz ) );\n`; functionString += ` vec3 highz_mods = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + Pi_inc1.zzz * ZINC.xyz ) );\n`; - functionString += ` Pi_1 = ( Pi_1.z < 0.5 ) ? lowz_mods : highz_mods;\n`; - functionString += ` Pi_2 = ( Pi_2.z < 0.5 ) ? lowz_mods : highz_mods;\n`; + functionString += ` Pi_1 = [( Pi_1.z < 0.5 ) ? lowz_mods : highz_mods];\n`; + functionString += ` Pi_2 = [( Pi_2.z < 0.5 ) ? lowz_mods : highz_mods];\n`; functionString += ` vec4 hash_0 = fract( Pt * vec4( lowz_mods.x, Pi_1.x, Pi_2.x, highz_mods.x ) ) - 0.49999;\n`; functionString += ` vec4 hash_1 = fract( Pt * vec4( lowz_mods.y, Pi_1.y, Pi_2.y, highz_mods.y ) ) - 0.49999;\n`; functionString += ` vec4 hash_2 = fract( Pt * vec4( lowz_mods.z, Pi_1.z, Pi_2.z, highz_mods.z ) ) - 0.49999;\n`; functionString += ` vec4 grad_results = inversesqrt( hash_0 * hash_0 + hash_1 * hash_1 + hash_2 * hash_2 ) * ( hash_0 * v1234_x + hash_1 * v1234_y + hash_2 * v1234_z );\n`; functionString += ` const float FINAL_NORMALIZATION = 37.837227241611314102871574478976;\n`; functionString += ` vec4 kernel_weights = v1234_x * v1234_x + v1234_y * v1234_y + v1234_z * v1234_z;\n`; - functionString += ` kernel_weights = max(0.5 - kernel_weights, 0.0);\n`; + functionString += ` kernel_weights = max(0.5 - kernel_weights, vec4(0.));\n`; functionString += ` kernel_weights = kernel_weights*kernel_weights*kernel_weights;\n`; functionString += ` return dot( kernel_weights, grad_results ) * FINAL_NORMALIZATION;\n`; functionString += `}\n`; + if (state.shaderLanguage === ShaderLanguage.WGSL) { + functionString = state._babylonSLtoWGSL(functionString); + } else { + functionString = state._babylonSLtoGLSL(functionString); + } + state._emitFunction("SimplexPerlin3D", functionString, "// SimplexPerlin3D"); state.compilationString += state._declareOutput(this._outputs[0]) + ` = SimplexPerlin3D(${this.seed.associatedVariableName});\n`; diff --git a/packages/dev/core/src/Materials/Node/Blocks/transformBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/transformBlock.ts index 229574ce064..f98a3388a9c 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/transformBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/transformBlock.ts @@ -103,7 +103,7 @@ export class TransformBlock extends NodeMaterialBlock { const transformName = state._getFreeVariableName(`${transform.associatedVariableName}_NUS`); if (state.shaderLanguage === ShaderLanguage.WGSL) { - state.compilationString += `let ${transformName}: mat3x3f = mat3x3f(${transform.associatedVariableName}[0].xyz, ${transform.associatedVariableName}[1].xyz, ${transform.associatedVariableName}[2].xyz);\n`; + state.compilationString += `var ${transformName}: mat3x3f = mat3x3f(${transform.associatedVariableName}[0].xyz, ${transform.associatedVariableName}[1].xyz, ${transform.associatedVariableName}[2].xyz);\n`; } else { state.compilationString += `mat3 ${transformName} = mat3(${transform.associatedVariableName});\n`; } diff --git a/packages/dev/core/src/Materials/Node/Blocks/triPlanarBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/triPlanarBlock.ts index 3963cd961cf..894736a3ea3 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/triPlanarBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/triPlanarBlock.ts @@ -18,6 +18,7 @@ import { ImageSourceBlock } from "./Dual/imageSourceBlock"; import { NodeMaterialConnectionPointCustomObject } from "../nodeMaterialConnectionPointCustomObject"; import { EngineStore } from "../../../Engines/engineStore"; import { editableInPropertyPage, PropertyTypeForEdition } from "../../../Decorators/nodeDecorator"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to read a texture with triplanar mapping (see "boxmap" in https://iquilezles.org/articles/biplanar/) @@ -356,6 +357,20 @@ export class TriPlanarBlock extends NodeMaterialBlock { } } + private _samplerFunc(state: NodeMaterialBuildState) { + if (state.shaderLanguage === ShaderLanguage.WGSL) { + return "textureSample"; + } + return "texture2D"; + } + + private _generateTextureSample(textureName: string, uv: string, state: NodeMaterialBuildState) { + if (state.shaderLanguage === ShaderLanguage.WGSL) { + return `${this._samplerFunc(state)}(${textureName},${textureName + Constants.AUTOSAMPLERSUFFIX}, ${uv})`; + } + return `${this._samplerFunc(state)}(${textureName}, ${uv})`; + } + protected _generateTextureLookup(state: NodeMaterialBuildState): void { const samplerName = this.samplerName; const samplerYName = this.samplerYName ?? samplerName; @@ -373,11 +388,11 @@ export class TriPlanarBlock extends NodeMaterialBlock { const uvz = state._getFreeVariableName("uvz"); state.compilationString += ` - vec3 ${n} = ${this.normal.associatedVariableName}.xyz; + ${state._declareLocalVar(n, NodeMaterialBlockConnectionPointTypes.Vector3)} = ${this.normal.associatedVariableName}.xyz; - vec2 ${uvx} = ${this.position.associatedVariableName}.yz; - vec2 ${uvy} = ${this.position.associatedVariableName}.zx; - vec2 ${uvz} = ${this.position.associatedVariableName}.xy; + ${state._declareLocalVar(uvx, NodeMaterialBlockConnectionPointTypes.Vector2)} = ${this.position.associatedVariableName}.yz; + ${state._declareLocalVar(uvy, NodeMaterialBlockConnectionPointTypes.Vector2)} = ${this.position.associatedVariableName}.zx; + ${state._declareLocalVar(uvz, NodeMaterialBlockConnectionPointTypes.Vector2)} = ${this.position.associatedVariableName}.xy; `; if (this.projectAsCube) { @@ -396,31 +411,42 @@ export class TriPlanarBlock extends NodeMaterialBlock { `; } + const suffix = state.shaderLanguage === ShaderLanguage.WGSL ? "f" : ""; + state.compilationString += ` - vec4 ${x} = texture2D(${samplerName}, ${uvx}); - vec4 ${y} = texture2D(${samplerYName}, ${uvy}); - vec4 ${z} = texture2D(${samplerZName}, ${uvz}); + ${state._declareLocalVar(x, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._generateTextureSample(samplerName, uvx, state)}; + ${state._declareLocalVar(y, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._generateTextureSample(samplerYName, uvy, state)}; + ${state._declareLocalVar(z, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._generateTextureSample(samplerZName, uvz, state)}; // blend weights - vec3 ${w} = pow(abs(${n}), vec3(${sharpness})); + ${state._declareLocalVar(w, NodeMaterialBlockConnectionPointTypes.Vector3)} = pow(abs(${n}), vec3${suffix}(${sharpness})); // blend and return - vec4 ${this._tempTextureRead} = (${x}*${w}.x + ${y}*${w}.y + ${z}*${w}.z) / (${w}.x + ${w}.y + ${w}.z); + ${state._declareLocalVar(this._tempTextureRead, NodeMaterialBlockConnectionPointTypes.Vector4)} = (${x}*${w}.x + ${y}*${w}.y + ${z}*${w}.z) / (${w}.x + ${w}.y + ${w}.z); `; } private _generateConversionCode(state: NodeMaterialBuildState, output: NodeMaterialConnectionPoint, swizzle: string): void { + let vecSpecifier = ""; + + if ( + state.shaderLanguage === ShaderLanguage.WGSL && + (output.type === NodeMaterialBlockConnectionPointTypes.Vector3 || output.type === NodeMaterialBlockConnectionPointTypes.Color3) + ) { + vecSpecifier = "Vec3"; + } + if (swizzle !== "a") { // no conversion if the output is "a" (alpha) if (!this.texture || !this.texture.gammaSpace) { state.compilationString += `#ifdef ${this._linearDefineName} - ${output.associatedVariableName} = toGammaSpace(${output.associatedVariableName}); + ${output.associatedVariableName} = toGammaSpace${vecSpecifier}(${output.associatedVariableName}); #endif `; } state.compilationString += `#ifdef ${this._gammaDefineName} - ${output.associatedVariableName} = toLinearSpace(${output.associatedVariableName}); + ${output.associatedVariableName} = toLinearSpace${vecSpecifier}(${output.associatedVariableName}); #endif `; } @@ -430,7 +456,7 @@ export class TriPlanarBlock extends NodeMaterialBlock { let complement = ""; if (!this.disableLevelMultiplication) { - complement = ` * ${this._textureInfoName}`; + complement = ` * ${state.shaderLanguage === ShaderLanguage.WGSL ? "uniforms." : ""}${this._textureInfoName}`; } state.compilationString += `${state._declareOutput(output)} = ${this._tempTextureRead}.${swizzle}${complement};\n`; @@ -448,14 +474,14 @@ export class TriPlanarBlock extends NodeMaterialBlock { this._textureInfoName = state._getFreeVariableName("textureInfoName"); - this.level.associatedVariableName = this._textureInfoName; + this.level.associatedVariableName = (state.shaderLanguage === ShaderLanguage.WGSL ? "uniforms." : "") + this._textureInfoName; this._tempTextureRead = state._getFreeVariableName("tempTextureRead"); this._linearDefineName = state._getFreeDefineName("ISLINEAR"); this._gammaDefineName = state._getFreeDefineName("ISGAMMA"); if (!this._imageSource) { - this._samplerName = state._getFreeVariableName(this.name + "Sampler"); + this._samplerName = state._getFreeVariableName(this.name + "Texture"); state._emit2DSampler(this._samplerName); } diff --git a/packages/dev/core/src/Materials/Node/Blocks/worleyNoise3DBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/worleyNoise3DBlock.ts index 85fa8eb8954..85a42f0b0a1 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/worleyNoise3DBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/worleyNoise3DBlock.ts @@ -6,6 +6,7 @@ import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; import { RegisterClass } from "../../../Misc/typeStore"; import type { Scene } from "../../../scene"; import { editableInPropertyPage, PropertyTypeForEdition } from "../../../Decorators/nodeDecorator"; +import { ShaderLanguage } from "../../../Materials/shaderLanguage"; /** * block used to Generate a Worley Noise 3D Noise Pattern @@ -95,7 +96,7 @@ export class WorleyNoise3DBlock extends NodeMaterialBlock { functionString += `}\n\n`; functionString += `vec3 dist(vec3 x, vec3 y, vec3 z, bool manhattanDistance){\n`; - functionString += ` return manhattanDistance ? abs(x) + abs(y) + abs(z) : (x * x + y * y + z * z);\n`; + functionString += ` return [manhattanDistance ? abs(x) + abs(y) + abs(z) : (x * x + y * y + z * z)];\n`; functionString += `}\n\n`; functionString += `vec2 worley(vec3 P, float jitter, bool manhattanDistance){\n`; @@ -202,7 +203,7 @@ export class WorleyNoise3DBlock extends NodeMaterialBlock { functionString += ` vec3 dz33 = Pfz.z + jitter*oz33;\n`; functionString += `\n`; functionString += ` vec3 d11 = dist(dx11, dy11, dz11, manhattanDistance);\n`; - functionString += ` vec3 d12 =dist(dx12, dy12, dz12, manhattanDistance);\n`; + functionString += ` vec3 d12 = dist(dx12, dy12, dz12, manhattanDistance);\n`; functionString += ` vec3 d13 = dist(dx13, dy13, dz13, manhattanDistance);\n`; functionString += ` vec3 d21 = dist(dx21, dy21, dz21, manhattanDistance);\n`; functionString += ` vec3 d22 = dist(dx22, dy22, dz22, manhattanDistance);\n`; @@ -230,23 +231,31 @@ export class WorleyNoise3DBlock extends NodeMaterialBlock { functionString += ` d21 = max(d11, d21);\n`; functionString += ` d11 = min(da, d31); // Smallest now in d11\n`; functionString += ` d31 = max(da, d31); // 2nd smallest now not in d31\n`; - functionString += ` d11.xy = (d11.x < d11.y) ? d11.xy : d11.yx;\n`; - functionString += ` d11.xz = (d11.x < d11.z) ? d11.xz : d11.zx; // d11.x now smallest\n`; + functionString += ` if (d11.x >= d11.y) { vec2 temp = d11.yx; d11.x = temp.x; d11.y = temp.y; }\n`; + functionString += ` if (d11.x >= d11.z) { vec2 temp = d11.zx; d11.x = temp.x; d11.z = temp.y; }\n`; functionString += ` d12 = min(d12, d21); // 2nd smallest now not in d21\n`; functionString += ` d12 = min(d12, d22); // nor in d22\n`; functionString += ` d12 = min(d12, d31); // nor in d31\n`; functionString += ` d12 = min(d12, d32); // nor in d32\n`; - functionString += ` d11.yz = min(d11.yz,d12.xy); // nor in d12.yz\n`; - functionString += ` d11.y = min(d11.y,d12.z); // Only two more to go\n`; - functionString += ` d11.y = min(d11.y,d11.z); // Done! (Phew!)\n`; + functionString += ` vec2 temp2 = min(d11.yz, d12.xy); // nor in d12.yz\n`; + functionString += ` d11.y = temp2.x;\n`; + functionString += ` d11.z = temp2.y;\n`; + functionString += ` d11.y = min(d11.y, d12.z); // Only two more to go\n`; + functionString += ` d11.y = min(d11.y, d11.z); // Done! (Phew!)\n`; functionString += ` return sqrt(d11.xy); // F1, F2\n`; functionString += `}\n\n`; + if (state.shaderLanguage === ShaderLanguage.WGSL) { + functionString = state._babylonSLtoWGSL(functionString); + } else { + functionString = state._babylonSLtoGLSL(functionString); + } + state._emitFunction("worley3D", functionString, "// Worley3D"); const tempVariable = state._getFreeVariableName("worleyTemp"); - state.compilationString += `vec2 ${tempVariable} = worley(${this.seed.associatedVariableName}, ${this.jitter.associatedVariableName}, ${this.manhattanDistance});\n`; + state.compilationString += `${state._declareLocalVar(tempVariable, NodeMaterialBlockConnectionPointTypes.Vector2)} = worley(${this.seed.associatedVariableName}, ${this.jitter.associatedVariableName}, ${this.manhattanDistance});\n`; if (this.output.hasEndpoints) { state.compilationString += state._declareOutput(this.output) + ` = ${tempVariable};\n`; diff --git a/packages/dev/core/src/Materials/Node/nodeMaterial.ts b/packages/dev/core/src/Materials/Node/nodeMaterial.ts index 8ee712a3ca0..a8e74e6e473 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterial.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterial.ts @@ -262,6 +262,9 @@ export class NodeMaterial extends PushMaterial { /** Gets or sets a boolean indicating that node materials should not deserialize textures from json / snippet content */ public static IgnoreTexturesAtLoadTime = false; + /** Defines default shader language when no option is defined */ + public static DefaultShaderLanguage = ShaderLanguage.GLSL; + /** * Checks if a block is a texture block * @param block The block to check @@ -425,9 +428,13 @@ export class NodeMaterial extends PushMaterial { constructor(name: string, scene?: Scene, options: Partial = {}) { super(name, scene || EngineStore.LastCreatedScene!); + if (options && options.shaderLanguage === ShaderLanguage.WGSL && !this.getScene().getEngine().isWebGPU) { + throw new Error("WebGPU shader language is only supported with WebGPU engine"); + } + this._options = { emitComments: false, - shaderLanguage: ShaderLanguage.GLSL, + shaderLanguage: NodeMaterial.DefaultShaderLanguage, ...options, }; @@ -1553,27 +1560,6 @@ export class NodeMaterial extends PushMaterial { const result = this._processDefines(mesh, defines, useInstances, subMesh); - // //*********************** */ - // const tempA = ` - // uniform u_World : mat4x4; - // uniform u_ViewProjection : mat4x4; - // attribute position : vec3; - - // @vertex - // fn main(input : VertexInputs) -> FragmentInputs { - // vertexOutputs.position = uniforms.u_ViewProjection * uniforms.u_World * vec4(vertexInputs.position, 1.0); - // } - // `; - - // const tempB = ` - // uniform u_color : vec4; - - // @fragment - // fn main(input : FragmentInputs) -> FragmentOutputs { - // fragmentOutputs.color = uniforms.u_color; - // }`; - // /*********************** */ - if (result) { const previousEffect = subMesh.effect; // Compilation diff --git a/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts b/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts index e41d86fd657..fb9c02db8fa 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts @@ -1,10 +1,10 @@ import { NodeMaterialBlockConnectionPointTypes } from "./Enums/nodeMaterialBlockConnectionPointTypes"; import { NodeMaterialBlockTargets } from "./Enums/nodeMaterialBlockTargets"; import type { NodeMaterialBuildStateSharedData } from "./nodeMaterialBuildStateSharedData"; -import { Effect } from "../effect"; import { ShaderLanguage } from "../shaderLanguage"; import type { NodeMaterialConnectionPoint } from "./nodeMaterialBlockConnectionPoint"; import { ShaderStore as EngineShaderStore } from "../../Engines/shaderStore"; +import { Constants } from "../../Engines/constants"; /** * Class used to store node based material build state @@ -210,11 +210,11 @@ export class NodeMaterialBuildState { /** * @internal */ - public _emit2DSampler(name: string, textureName = "") { + public _emit2DSampler(name: string) { if (this.samplers.indexOf(name) < 0) { if (this.shaderLanguage === ShaderLanguage.WGSL) { - this._samplerDeclaration += `var ${name}: sampler;\n`; - this._samplerDeclaration += `var ${textureName}: texture_2d;\n`; + this._samplerDeclaration += `var ${name + Constants.AUTOSAMPLERSUFFIX}: sampler;\n`; + this._samplerDeclaration += `var ${name}: texture_2d;\n`; } else { this._samplerDeclaration += `uniform sampler2D ${name};\n`; } @@ -268,15 +268,15 @@ export class NodeMaterialBuildState { case NodeMaterialBlockConnectionPointTypes.Int: return isWGSL ? "i32" : "int"; case NodeMaterialBlockConnectionPointTypes.Vector2: - return isWGSL ? "vec2" : "vec2"; + return isWGSL ? "vec2f" : "vec2"; case NodeMaterialBlockConnectionPointTypes.Color3: case NodeMaterialBlockConnectionPointTypes.Vector3: - return isWGSL ? "vec3" : "vec3"; + return isWGSL ? "vec3f" : "vec3"; case NodeMaterialBlockConnectionPointTypes.Color4: case NodeMaterialBlockConnectionPointTypes.Vector4: - return isWGSL ? "vec4" : "vec4"; + return isWGSL ? "vec4f" : "vec4"; case NodeMaterialBlockConnectionPointTypes.Matrix: - return isWGSL ? "mat4x4" : "mat4"; + return isWGSL ? "mat4x4f" : "mat4"; } return ""; @@ -327,7 +327,8 @@ export class NodeMaterialBuildState { return `#include<${includeName}>${options.substitutionVars ? "(" + options.substitutionVars + ")" : ""}[0..${options.repeatKey}]\n`; } - let code = Effect.IncludesShadersStore[includeName] + "\n"; + const store = EngineShaderStore.GetIncludesShadersStore(this.shaderLanguage); + let code = store[includeName] + "\n"; if (this.sharedData.emitComments) { code = comments + `\n` + code; @@ -524,16 +525,16 @@ export class NodeMaterialBuildState { /** * @internal */ - public _declareOutput(output: NodeMaterialConnectionPoint): string { - return this._declareLocalVar(output.associatedVariableName, output.type); + public _declareOutput(output: NodeMaterialConnectionPoint, isConst?: boolean): string { + return this._declareLocalVar(output.associatedVariableName, output.type, isConst); } /** * @internal */ - public _declareLocalVar(name: string, type: NodeMaterialBlockConnectionPointTypes): string { + public _declareLocalVar(name: string, type: NodeMaterialBlockConnectionPointTypes, isConst?: boolean): string { if (this.shaderLanguage === ShaderLanguage.WGSL) { - return `var ${name}: ${this._getShaderType(type)}`; + return `${isConst ? "const" : "var"} ${name}: ${this._getShaderType(type)}`; } else { return `${this._getShaderType(type)} ${name}`; } @@ -551,11 +552,27 @@ export class NodeMaterialBuildState { return source.replace(new RegExp(`out\\s+var\\s+(\\w+)\\s*:\\s*(\\w+)`, "g"), `$1: ptr`); } + private _convertTernaryOperandsToWGSL(source: string): string { + return source.replace(new RegExp(`\\[(.*?)\\?(.*?):(.*)\\]`, "g"), (match, condition, trueCase, falseCase) => `select(${falseCase}, ${trueCase}, ${condition})`); + } + + private _convertModOperatorsToWGSL(source: string): string { + return source.replace(new RegExp(`mod\\((.+?),\\s*(.+?)\\)`, "g"), (match, left, right) => `((${left})%(${right}))`); + } + + private _convertConstToWGSL(source: string): string { + return source.replace(new RegExp(`const var`, "g"), `const`); + } + + private _convertInnerFunctionsToWGSL(source: string): string { + return source.replace(new RegExp(`inversesqrt`, "g"), `inverseSqrt`); + } + private _convertFunctionsToWGSL(source: string): string { - const regex = /var\s+(\w+)\s*:\s*(\w+)\((.*)\)/; - const match = source.match(regex); + const regex = /var\s+(\w+)\s*:\s*(\w+)\((.*)\)/g; - if (match) { + let match: RegExpMatchArray | null; + while ((match = regex.exec(source)) !== null) { const funcName = match[1]; const funcType = match[2]; const params = match[3]; // All parameters as a single string @@ -564,7 +581,7 @@ export class NodeMaterialBuildState { const formattedParams = params.replace(/var\s/g, ""); // Constructing the final output string - return source.replace(match[0], `fn ${funcName}(${formattedParams}) -> ${funcType}`); + source = source.replace(match[0], `fn ${funcName}(${formattedParams}) -> ${funcType}`); } return source; } @@ -572,6 +589,7 @@ export class NodeMaterialBuildState { public _babylonSLtoWGSL(code: string) { // variable declarations code = this._convertVariableDeclarationToWGSL("void", "voidnull", code); + code = this._convertVariableDeclarationToWGSL("bool", "bool", code); code = this._convertVariableDeclarationToWGSL("int", "i32", code); code = this._convertVariableDeclarationToWGSL("uint", "u32", code); code = this._convertVariableDeclarationToWGSL("float", "f32", code); @@ -591,6 +609,18 @@ export class NodeMaterialBuildState { code = this._convertVariableConstructorsToWGSL("mat3", "mat3x3f", code); code = this._convertVariableConstructorsToWGSL("mat4", "mat4x4f", code); + // Ternary operands + code = this._convertTernaryOperandsToWGSL(code); + + // Mod operators + code = this._convertModOperatorsToWGSL(code); + + // Const + code = this._convertConstToWGSL(code); + + // Inner functions + code = this._convertInnerFunctionsToWGSL(code); + // Out paramters code = this._convertOutParametersToWGSL(code); code = code.replace(/\[\*\]/g, "*"); @@ -604,8 +634,14 @@ export class NodeMaterialBuildState { return code; } + private _convertTernaryOperandsToGLSL(source: string): string { + return source.replace(new RegExp(`\\[(.+?)\\?(.+?):(.+)\\]`, "g"), (match, condition, trueCase, falseCase) => `${condition} ? ${trueCase} : ${falseCase}`); + } + public _babylonSLtoGLSL(code: string) { + /** Remove BSL specifics */ code = code.replace(/\[\*\]/g, ""); + code = this._convertTernaryOperandsToGLSL(code); return code; } diff --git a/packages/dev/core/src/Materials/materialHelper.functions.ts b/packages/dev/core/src/Materials/materialHelper.functions.ts index 806e9630cba..74ceebb6e45 100644 --- a/packages/dev/core/src/Materials/materialHelper.functions.ts +++ b/packages/dev/core/src/Materials/materialHelper.functions.ts @@ -939,8 +939,8 @@ export function PrepareUniformsAndSamplersForLight( "depthValues" + lightIndex ); - samplersList.push("shadowSampler" + lightIndex); - samplersList.push("depthSampler" + lightIndex); + samplersList.push("shadowTexture" + lightIndex); + samplersList.push("depthTexture" + lightIndex); uniformsList.push( "viewFrustumZ" + lightIndex, @@ -952,7 +952,7 @@ export function PrepareUniformsAndSamplersForLight( ); if (projectedLightTexture) { - samplersList.push("projectionLightSampler" + lightIndex); + samplersList.push("projectionLightTexture" + lightIndex); uniformsList.push("textureProjectionMatrix" + lightIndex); } } diff --git a/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx b/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx index 3d7c52be0e2..96f69fc04f1 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx @@ -130,7 +130,7 @@ #endif #ifdef PROJECTEDLIGHTTEXTURE{X} - info.diffuse *= computeProjectionTextureDiffuseLighting(projectionLightSampler{X}, textureProjectionMatrix{X}); + info.diffuse *= computeProjectionTextureDiffuseLighting(projectionLightTexture{X}, textureProjectionMatrix{X}); #endif #endif @@ -155,22 +155,22 @@ { #if defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) - shadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) - shadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #else - shadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) - shadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + shadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); #elif defined(SHADOWMEDIUMQUALITY{X}) - shadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + shadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); #else - shadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + shadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); #endif #else - shadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #ifdef SHADOWCSMDEBUG{X} @@ -186,22 +186,22 @@ float nextShadow = 0.; #if defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) - nextShadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + nextShadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) - nextShadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + nextShadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #else - nextShadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + nextShadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) - nextShadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + nextShadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); #elif defined(SHADOWMEDIUMQUALITY{X}) - nextShadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + nextShadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); #else - nextShadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + nextShadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); #endif #else - nextShadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + nextShadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif shadow = mix(nextShadow, shadow, diffRatio); @@ -213,43 +213,43 @@ } #elif defined(SHADOWCLOSEESM{X}) #if defined(SHADOWCUBE{X}) - shadow = computeShadowWithCloseESMCube(vPositionW, light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); + shadow = computeShadowWithCloseESMCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); #else - shadow = computeShadowWithCloseESM(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); + shadow = computeShadowWithCloseESM(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); #endif #elif defined(SHADOWESM{X}) #if defined(SHADOWCUBE{X}) - shadow = computeShadowWithESMCube(vPositionW, light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); + shadow = computeShadowWithESMCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); #else - shadow = computeShadowWithESM(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); + shadow = computeShadowWithESM(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPOISSON{X}) #if defined(SHADOWCUBE{X}) - shadow = computeShadowWithPoissonSamplingCube(vPositionW, light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.depthValues); + shadow = computeShadowWithPoissonSamplingCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.depthValues); #else - shadow = computeShadowWithPoissonSampling(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPoissonSampling(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCF{X}) #if defined(SHADOWLOWQUALITY{X}) - shadow = computeShadowWithPCF1(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPCF1(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) - shadow = computeShadowWithPCF3(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPCF3(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #else - shadow = computeShadowWithPCF5(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPCF5(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #elif defined(SHADOWPCSS{X}) #if defined(SHADOWLOWQUALITY{X}) - shadow = computeShadowWithPCSS16(vPositionFromLight{X}, vDepthMetric{X}, depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPCSS16(vPositionFromLight{X}, vDepthMetric{X}, depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #elif defined(SHADOWMEDIUMQUALITY{X}) - shadow = computeShadowWithPCSS32(vPositionFromLight{X}, vDepthMetric{X}, depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPCSS32(vPositionFromLight{X}, vDepthMetric{X}, depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #else - shadow = computeShadowWithPCSS64(vPositionFromLight{X}, vDepthMetric{X}, depthSampler{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadowWithPCSS64(vPositionFromLight{X}, vDepthMetric{X}, depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #else #if defined(SHADOWCUBE{X}) - shadow = computeShadowCube(vPositionW, light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.depthValues); + shadow = computeShadowCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.depthValues); #else - shadow = computeShadow(vPositionFromLight{X}, vDepthMetric{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + shadow = computeShadow(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); #endif #endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/lightFragmentDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/lightFragmentDeclaration.fx index d48fb040571..638dd701d9c 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/lightFragmentDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/lightFragmentDeclaration.fx @@ -19,15 +19,15 @@ varying vec4 vPositionFromCamera{X}; #if defined(SHADOWPCSS{X}) - uniform highp sampler2DArrayShadow shadowSampler{X}; - uniform highp sampler2DArray depthSampler{X}; + uniform highp sampler2DArrayShadow shadowTexture{X}; + uniform highp sampler2DArray depthTexture{X}; uniform vec2 lightSizeUVCorrection{X}[SHADOWCSMNUM_CASCADES{X}]; uniform float depthCorrection{X}[SHADOWCSMNUM_CASCADES{X}]; uniform float penumbraDarkness{X}; #elif defined(SHADOWPCF{X}) - uniform highp sampler2DArrayShadow shadowSampler{X}; + uniform highp sampler2DArrayShadow shadowTexture{X}; #else - uniform highp sampler2DArray shadowSampler{X}; + uniform highp sampler2DArray shadowTexture{X}; #endif #ifdef SHADOWCSMDEBUG{X} @@ -53,18 +53,18 @@ float diff{X} = 0.; #elif defined(SHADOWCUBE{X}) - uniform samplerCube shadowSampler{X}; + uniform samplerCube shadowTexture{X}; #else varying vec4 vPositionFromLight{X}; varying float vDepthMetric{X}; #if defined(SHADOWPCSS{X}) - uniform highp sampler2DShadow shadowSampler{X}; - uniform highp sampler2D depthSampler{X}; + uniform highp sampler2DShadow shadowTexture{X}; + uniform highp sampler2D depthTexture{X}; #elif defined(SHADOWPCF{X}) - uniform highp sampler2DShadow shadowSampler{X}; + uniform highp sampler2DShadow shadowTexture{X}; #else - uniform sampler2D shadowSampler{X}; + uniform sampler2D shadowTexture{X}; #endif uniform mat4 lightMatrix{X}; #endif @@ -81,6 +81,6 @@ #endif #ifdef PROJECTEDLIGHTTEXTURE{X} uniform mat4 textureProjectionMatrix{X}; - uniform sampler2D projectionLightSampler{X}; + uniform sampler2D projectionLightTexture{X}; #endif #endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/lightUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/lightUboDeclaration.fx index e7ea3df7355..55763d95b6e 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/lightUboDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/lightUboDeclaration.fx @@ -18,7 +18,7 @@ } light{X}; #ifdef PROJECTEDLIGHTTEXTURE{X} uniform mat4 textureProjectionMatrix{X}; - uniform sampler2D projectionLightSampler{X}; + uniform sampler2D projectionLightTexture{X}; #endif #ifdef SHADOW{X} #ifdef SHADOWCSM{X} @@ -32,15 +32,15 @@ varying vec4 vPositionFromCamera{X}; #if defined(SHADOWPCSS{X}) - uniform highp sampler2DArrayShadow shadowSampler{X}; - uniform highp sampler2DArray depthSampler{X}; + uniform highp sampler2DArrayShadow shadowTexture{X}; + uniform highp sampler2DArray depthTexture{X}; uniform vec2 lightSizeUVCorrection{X}[SHADOWCSMNUM_CASCADES{X}]; uniform float depthCorrection{X}[SHADOWCSMNUM_CASCADES{X}]; uniform float penumbraDarkness{X}; #elif defined(SHADOWPCF{X}) - uniform highp sampler2DArrayShadow shadowSampler{X}; + uniform highp sampler2DArrayShadow shadowTexture{X}; #else - uniform highp sampler2DArray shadowSampler{X}; + uniform highp sampler2DArray shadowTexture{X}; #endif #ifdef SHADOWCSMDEBUG{X} @@ -66,18 +66,18 @@ float diff{X} = 0.; #elif defined(SHADOWCUBE{X}) - uniform samplerCube shadowSampler{X}; + uniform samplerCube shadowTexture{X}; #else varying vec4 vPositionFromLight{X}; varying float vDepthMetric{X}; #if defined(SHADOWPCSS{X}) - uniform highp sampler2DShadow shadowSampler{X}; - uniform highp sampler2D depthSampler{X}; + uniform highp sampler2DShadow shadowTexture{X}; + uniform highp sampler2D depthTexture{X}; #elif defined(SHADOWPCF{X}) - uniform highp sampler2DShadow shadowSampler{X}; + uniform highp sampler2DShadow shadowTexture{X}; #else - uniform sampler2D shadowSampler{X}; + uniform sampler2D shadowTexture{X}; #endif uniform mat4 lightMatrix{X}; #endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/fogFragmentDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/fogFragmentDeclaration.fx new file mode 100644 index 00000000000..a7581befc93 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/fogFragmentDeclaration.fx @@ -0,0 +1,36 @@ +#ifdef FOG + +#define FOGMODE_NONE 0. +#define FOGMODE_EXP 1. +#define FOGMODE_EXP2 2. +#define FOGMODE_LINEAR 3. +#define E 2.71828 + +uniform vFogInfos: vec4f; +uniform vFogColor: vec3f; +varying vFogDistance: vec3f; + +fn CalcFogFactor() -> f32 +{ + var fogCoeff: f32 = 1.0; + var fogStart: f32 = uniforms.vFogInfos.y; + var fogEnd: f32 = uniforms.vFogInfos.z; + var fogDensity: f32 = uniforms.vFogInfos.w; + var fogDistance: f32 = length(fragmentInputs.vFogDistance); + + if (FOGMODE_LINEAR == uniforms.vFogInfos.x) + { + fogCoeff = (fogEnd - fogDistance) / (fogEnd - fogStart); + } + else if (FOGMODE_EXP == uniforms.vFogInfos.x) + { + fogCoeff = 1.0 / pow(E, fogDistance * fogDensity); + } + else if (FOGMODE_EXP2 == uniforms.vFogInfos.x) + { + fogCoeff = 1.0 / pow(E, fogDistance * fogDistance * fogDensity * fogDensity); + } + + return clamp(fogCoeff, 0.0, 1.0); +} +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx new file mode 100644 index 00000000000..398c9b34a46 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx @@ -0,0 +1,330 @@ +#ifdef LIGHT{X} + #if defined(SHADOWONLY) || defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X}) + //No light calculation + #else + #ifdef PBR + // Compute Pre Lighting infos + #ifdef SPOTLIGHT{X} + preInfo = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + #elif defined(POINTLIGHT{X}) + preInfo = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + #elif defined(HEMILIGHT{X}) + preInfo = computeHemisphericPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + #elif defined(DIRLIGHT{X}) + preInfo = computeDirectionalPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + #endif + + preInfo.NdotV = NdotV; + + // Compute Attenuation infos + #ifdef SPOTLIGHT{X} + #ifdef LIGHT_FALLOFF_GLTF{X} + preInfo.attenuation = computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared, light{X}.vLightFalloff.y); + preInfo.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); + #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) + preInfo.attenuation = computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared); + preInfo.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w); + #elif defined(LIGHT_FALLOFF_STANDARD{X}) + preInfo.attenuation = computeDistanceLightFalloff_Standard(preInfo.lightOffset, light{X}.vLightFalloff.x); + preInfo.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w); + #else + preInfo.attenuation = computeDistanceLightFalloff(preInfo.lightOffset, preInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y); + preInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); + #endif + #elif defined(POINTLIGHT{X}) + #ifdef LIGHT_FALLOFF_GLTF{X} + preInfo.attenuation = computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared, light{X}.vLightFalloff.y); + #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) + preInfo.attenuation = computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared); + #elif defined(LIGHT_FALLOFF_STANDARD{X}) + preInfo.attenuation = computeDistanceLightFalloff_Standard(preInfo.lightOffset, light{X}.vLightFalloff.x); + #else + preInfo.attenuation = computeDistanceLightFalloff(preInfo.lightOffset, preInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y); + #endif + #else + preInfo.attenuation = 1.0; + #endif + + // Simulates Light radius for diffuse and spec term + // clear coat is using a dedicated roughness + #ifdef HEMILIGHT{X} + preInfo.roughness = roughness; + #else + preInfo.roughness = adjustRoughnessFromLightProperties(roughness, light{X}.vLightSpecular.a, preInfo.lightDistance); + #endif + + #ifdef IRIDESCENCE + preInfo.iridescenceIntensity = iridescenceIntensity; + #endif + + // Diffuse contribution + #ifdef HEMILIGHT{X} + info.diffuse = computeHemisphericDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb, light{X}.vLightGround); + #elif defined(SS_TRANSLUCENCY) + info.diffuse = computeDiffuseAndTransmittedLighting(preInfo, light{X}.vLightDiffuse.rgb, subSurfaceOut.transmittance); + #else + info.diffuse = computeDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb); + #endif + + // Specular contribution + #ifdef SPECULARTERM + #ifdef ANISOTROPIC + info.specular = computeAnisotropicSpecularLighting(preInfo, viewDirectionW, normalW, anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb); + #else + info.specular = computeSpecularLighting(preInfo, normalW, clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb); + #endif + #endif + + // Sheen contribution + #ifdef SHEEN + #ifdef SHEEN_LINKWITHALBEDO + // BE Carefull: Sheen intensity is replacing the roughness value. + preInfo.roughness = sheenOut.sheenIntensity; + #else + #ifdef HEMILIGHT{X} + preInfo.roughness = sheenOut.sheenRoughness; + #else + preInfo.roughness = adjustRoughnessFromLightProperties(sheenOut.sheenRoughness, light{X}.vLightSpecular.a, preInfo.lightDistance); + #endif + #endif + info.sheen = computeSheenLighting(preInfo, normalW, sheenOut.sheenColor, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb); + #endif + + // Clear Coat contribution + #ifdef CLEARCOAT + // Simulates Light radius + #ifdef HEMILIGHT{X} + preInfo.roughness = clearcoatOut.clearCoatRoughness; + #else + preInfo.roughness = adjustRoughnessFromLightProperties(clearcoatOut.clearCoatRoughness, light{X}.vLightSpecular.a, preInfo.lightDistance); + #endif + + info.clearCoat = computeClearCoatLighting(preInfo, clearcoatOut.clearCoatNormalW, clearcoatOut.clearCoatAARoughnessFactors.x, clearcoatOut.clearCoatIntensity, light{X}.vLightDiffuse.rgb); + + #ifdef CLEARCOAT_TINT + // Absorption + absorption = computeClearCoatLightingAbsorption(clearcoatOut.clearCoatNdotVRefract, preInfo.L, clearcoatOut.clearCoatNormalW, clearcoatOut.clearCoatColor, clearcoatOut.clearCoatThickness, clearcoatOut.clearCoatIntensity); + info.diffuse *= absorption; + #ifdef SPECULARTERM + info.specular *= absorption; + #endif + #endif + + // Apply energy conservation on diffuse and specular term. + info.diffuse *= info.clearCoat.w; + #ifdef SPECULARTERM + info.specular *= info.clearCoat.w; + #endif + #ifdef SHEEN + info.sheen *= info.clearCoat.w; + #endif + #endif + #else + #ifdef SPOTLIGHT{X} + info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular.rgb, light{X}.vLightDiffuse.a, glossiness); + #elif defined(HEMILIGHT{X}) + info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular.rgb, light{X}.vLightGround, glossiness); + #elif defined(POINTLIGHT{X}) || defined(DIRLIGHT{X}) + info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular.rgb, light{X}.vLightDiffuse.a, glossiness); + #endif + #endif + + #ifdef PROJECTEDLIGHTTEXTURE{X} + info.diffuse *= computeProjectionTextureDiffuseLighting(projectionLightTexture{X}, projectionLightTexture{X}Sampler, textureProjectionMatrix{X}); + #endif + #endif + + #ifdef SHADOW{X} + #ifdef SHADOWCSMDEBUG{X} + var shadowDebug{X}: vec3f; + #endif + #ifdef SHADOWCSM{X} + #ifdef SHADOWCSMUSESHADOWMAXZ{X} + var index{X}: i32 = -1; + #else + var index{X}: i32 = SHADOWCSMNUM_CASCADES{X} - 1; + #endif + + var diff{X}: f32 = 0.; + + vPositionFromLight{X}[0] = fragmentInputs.vPositionFromLight{X}_0; + vPositionFromLight{X}[1] = fragmentInputs.vPositionFromLight{X}_1; + vPositionFromLight{X}[2] = fragmentInputs.vPositionFromLight{X}_2; + vPositionFromLight{X}[3] = fragmentInputs.vPositionFromLight{X}_3; + + vDepthMetric{X}[0] = fragmentInputs.vDepthMetric{X}_0; + vDepthMetric{X}[1] = fragmentInputs.vDepthMetric{X}_1; + vDepthMetric{X}[2] = fragmentInputs.vDepthMetric{X}_2; + vDepthMetric{X}[3] = fragmentInputs.vDepthMetric{X}_3; + + for (var i:i32 = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) + { + #ifdef SHADOWCSM_RIGHTHANDED{X} + diff{X} = uniforms.viewFrustumZ{X}[i] + fragmentInputs.vPositionFromCamera{X}.z; + #else + diff{X} = uniforms.viewFrustumZ{X}[i] - fragmentInputs.vPositionFromCamera{X}.z; + #endif + if (diff{X} >= 0.) { + index{X} = i; + break; + } + } + #ifdef SHADOWCSMUSESHADOWMAXZ{X} + if (index{X} >= 0) + #endif + { + #if defined(SHADOWPCF{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow = computeShadowWithCSMPCF1(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow = computeShadowWithCSMPCF3(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + shadow = computeShadowWithCSMPCF5(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCSS{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow = computeShadowWithCSMPCSS16(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, uniforms.lightSizeUVCorrection{X}[index{X}], uniforms.depthCorrection{X}[index{X}], uniforms.penumbraDarkness{X}); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow = computeShadowWithCSMPCSS32(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, uniforms.lightSizeUVCorrection{X}[index{X}], uniforms.depthCorrection{X}[index{X}], uniforms.penumbraDarkness{X}); + #else + shadow = computeShadowWithCSMPCSS64(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, uniforms.lightSizeUVCorrection{X}[index{X}], uniforms.depthCorrection{X}[index{X}], uniforms.penumbraDarkness{X}); + #endif + #else + shadow = computeShadowCSM(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + + #ifdef SHADOWCSMDEBUG{X} + shadowDebug{X} = vec3f(shadow) * vCascadeColorsMultiplier{X}[index{X}]; + #endif + + #ifndef SHADOWCSMNOBLEND{X} + var frustumLength:f32 = uniforms.frustumLengths{X}[index{X}]; + var diffRatio:f32 = clamp(diff{X} / frustumLength, 0., 1.) * uniforms.cascadeBlendFactor{X}; + if (index{X} < (SHADOWCSMNUM_CASCADES{X} - 1) && diffRatio < 1.) + { + index{X} += 1; + var nextShadow: f32 = 0.; + #if defined(SHADOWPCF{X}) + #if defined(SHADOWLOWQUALITY{X}) + nextShadow = computeShadowWithCSMPCF1(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], , shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + nextShadow = computeShadowWithCSMPCF3(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + nextShadow = computeShadowWithCSMPCF5(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCSS{X}) + #if defined(SHADOWLOWQUALITY{X}) + nextShadow = computeShadowWithCSMPCSS16(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, uniforms.lightSizeUVCorrection{X}[index{X}], uniforms.depthCorrection{X}[index{X}], uniforms.penumbraDarkness{X}); + #elif defined(SHADOWMEDIUMQUALITY{X}) + nextShadow = computeShadowWithCSMPCSS32(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, uniforms.lightSizeUVCorrection{X}[index{X}], uniforms.depthCorrection{X}[index{X}], uniforms.penumbraDarkness{X}); + #else + nextShadow = computeShadowWithCSMPCSS64(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, uniforms.lightSizeUVCorrection{X}[index{X}], uniforms.depthCorrection{X}[index{X}], uniforms.penumbraDarkness{X}); + #endif + #else + nextShadow = computeShadowCSM(index{X}, vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + + shadow = mix(nextShadow, shadow, diffRatio); + #ifdef SHADOWCSMDEBUG{X} + shadowDebug{X} = mix(vec3(nextShadow) * vCascadeColorsMultiplier{X}[index{X}], shadowDebug{X}, diffRatio); + #endif + } + #endif + } + #elif defined(SHADOWCLOSEESM{X}) + #if defined(SHADOWCUBE{X}) + shadow = computeShadowWithCloseESMCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); + #else + shadow = computeShadowWithCloseESM(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWESM{X}) + #if defined(SHADOWCUBE{X}) + shadow = computeShadowWithESMCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); + #else + shadow = computeShadowWithESM(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPOISSON{X}) + #if defined(SHADOWCUBE{X}) + shadow = computeShadowWithPoissonSamplingCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.depthValues); + #else + shadow = computeShadowWithPoissonSampling(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCF{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow = computeShadowWithPCF1(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow = computeShadowWithPCF3(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + shadow = computeShadowWithPCF5(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCSS{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow = computeShadowWithPCSS16(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow = computeShadowWithPCSS32(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + shadow = computeShadowWithPCSS64(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, depthTexture{X}, depthTexture{X}Sampler, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #else + #if defined(SHADOWCUBE{X}) + shadow = computeShadowCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.depthValues); + #else + shadow = computeShadow(fragmentInputs.vPositionFromLight{X}, fragmentInputs.vDepthMetric{X}, shadowTexture{X}, shadowTexture{X}Sampler, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #endif + + #ifdef SHADOWONLY + #ifndef SHADOWINUSE + #define SHADOWINUSE + #endif + globalShadow += shadow; + shadowLightCount += 1.0; + #endif + #else + shadow = 1.; + #endif + + aggShadow += shadow; + numLights += 1.0; + + #ifndef SHADOWONLY + #ifdef CUSTOMUSERLIGHTING + diffuseBase += computeCustomDiffuseLighting(info, diffuseBase, shadow); + #ifdef SPECULARTERM + specularBase += computeCustomSpecularLighting(info, specularBase, shadow); + #endif + #elif defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) + diffuseBase += lightmapColor.rgb * shadow; + #ifdef SPECULARTERM + #ifndef LIGHTMAPNOSPECULAR{X} + specularBase += info.specular * shadow * lightmapColor.rgb; + #endif + #endif + #ifdef CLEARCOAT + #ifndef LIGHTMAPNOSPECULAR{X} + clearCoatBase += info.clearCoat.rgb * shadow * lightmapColor.rgb; + #endif + #endif + #ifdef SHEEN + #ifndef LIGHTMAPNOSPECULAR{X} + sheenBase += info.sheen.rgb * shadow; + #endif + #endif + #else + #ifdef SHADOWCSMDEBUG{X} + diffuseBase += info.diffuse * shadowDebug{X}; + #else + diffuseBase += info.diffuse * shadow; + #endif + #ifdef SPECULARTERM + specularBase += info.specular * shadow; + #endif + #ifdef CLEARCOAT + clearCoatBase += info.clearCoat.rgb * shadow; + #endif + #ifdef SHEEN + sheenBase += info.sheen.rgb * shadow; + #endif + #endif + #endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightUboDeclaration.fx new file mode 100644 index 00000000000..3a481cf9dc1 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightUboDeclaration.fx @@ -0,0 +1,100 @@ +#ifdef LIGHT{X} + struct Light{X} + { + vLightData: vec4f, + vLightDiffuse: vec4f, + vLightSpecular: vec4f, + #ifdef SPOTLIGHT{X} + vLightDirection: vec4f, + vLightFalloff: vec4f, + #elif defined(POINTLIGHT{X}) + vLightFalloff: vec4f, + #elif defined(HEMILIGHT{X}) + vLightGround: vec3f, + #endif + shadowsInfo: vec4f, + depthValues: vec2f + } ; + +var light{X} : Light{X}; + + +#ifdef PROJECTEDLIGHTTEXTURE{X} + uniform textureProjectionMatrix{X}: mat4x4f; + var projectionLightTexture{X}sampler: sampler; + var projectionLightTexture{X}: texture_2d; +#endif +#ifdef SHADOW{X} + #ifdef SHADOWCSM{X} + uniform lightMatrix{X}: array; + uniform viewFrustumZ{X}: array; + uniform frustumLengths{X}: array; + uniform cascadeBlendFactor{X}: f32; + + // Because WGSL does not allow us to have arrays as varying... + varying vPositionFromLight{X}_0: vec4f; + varying vDepthMetric{X}_0: f32; + varying vPositionFromLight{X}_1: vec4f; + varying vDepthMetric{X}_1: f32; + varying vPositionFromLight{X}_2: vec4f; + varying vDepthMetric{X}_2: f32; + varying vPositionFromLight{X}_3: vec4f; + varying vDepthMetric{X}_3: f32; + varying vPositionFromCamera{X}: vec4f; + + var vPositionFromLight{X}: array; + var vDepthMetric{X} : array; + + #if defined(SHADOWPCSS{X}) + var shadowTexture{X}Sampler: sampler_comparison; + var shadowTexture{X}: texture_depth_2d_array; + var depthTexture{X}Sampler: sampler; + var depthTexture{X}: texture_2d_array; + uniform lightSizeUVCorrection{X}: array; + uniform depthCorrection{X}: array; + uniform penumbraDarkness{X}: f32; + #elif defined(SHADOWPCF{X}) + var shadowTexture{X}Sampler: sampler_comparison; + var shadowTexture{X}: texture_depth_2d_array; + #else + var shadowTexture{X}Sampler: sampler; + var shadowTexture{X}: texture_2d_array; + #endif + + #ifdef SHADOWCSMDEBUG{X} + const vCascadeColorsMultiplier{X}: array = array + ( + vec3f ( 1.5, 0.0, 0.0 ), + vec3f ( 0.0, 1.5, 0.0 ), + vec3f ( 0.0, 0.0, 5.5 ), + vec3f ( 1.5, 0.0, 5.5 ), + vec3f ( 1.5, 1.5, 0.0 ), + vec3f ( 1.0, 1.0, 1.0 ), + vec3f ( 0.0, 1.0, 5.5 ), + vec3f ( 0.5, 3.5, 0.75 ) + ); + #endif + #elif defined(SHADOWCUBE{X}) + var shadowTexture{X}Sampler: sampler; + var shadowTexture{X}: texture_cube; + #else + varying vPositionFromLight{X}: vec4f; + varying vDepthMetric{X}: f32; + + #if defined(SHADOWPCSS{X}) + var shadowTexture{X}Sampler: sampler_comparison; + var shadowTexture{X}: texture_depth_2d; + var depthTexture{X}Sampler: sampler; + var depthTexture{X}: texture_2d; + #elif defined(SHADOWPCF{X}) + var shadowTexture{X}Sampler: sampler_comparison; + var shadowTexture{X}: texture_depth_2d; + #else + var shadowTexture{X}Sampler: sampler; + var shadowTexture{X}: texture_2d; + #endif + uniform lightMatrix{X}: mat4x4f; + #endif +#endif + +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightVxUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightVxUboDeclaration.fx new file mode 100644 index 00000000000..9a5468e921e --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightVxUboDeclaration.fx @@ -0,0 +1,44 @@ +#ifdef LIGHT{X} + struct Light{X} + { + vLightData: vec4f, + vLightDiffuse: vec4f, + vLightSpecular: vec4f, + #ifdef SPOTLIGHT{X} + vLightDirection: vec4f, + vLightFalloff: vec4f, + #elif defined(POINTLIGHT{X}) + vLightFalloff: vec4f, + #elif defined(HEMILIGHT{X}) + vLightGround: vec3f, + #endif + shadowsInfo: vec4f, + depthValues: vec2f + } ; + +var light{X} : Light{X}; + +#ifdef SHADOW{X} + #ifdef SHADOWCSM{X} + uniform lightMatrix{X}: array; + + // Because WGSL does not allow us to have arrays as varying... + varying vPositionFromLight{X}_0: vec4f; + varying vDepthMetric{X}_0: f32; + varying vPositionFromLight{X}_1: vec4f; + varying vDepthMetric{X}_1: f32; + varying vPositionFromLight{X}_2: vec4f; + varying vDepthMetric{X}_2: f32; + varying vPositionFromLight{X}_3: vec4f; + varying vDepthMetric{X}_3: f32; + varying vPositionFromCamera{X}: vec4f; + #elif defined(SHADOWCUBE{X}) + #else + varying vPositionFromLight{X}: vec4f; + varying vDepthMetric{X}: f32; + + uniform lightMatrix{X}: mat4x4f; + #endif +#endif + +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.fx new file mode 100644 index 00000000000..76e1f9b3806 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.fx @@ -0,0 +1,117 @@ +// Light Computing +struct lightingInfo +{ + diffuse: vec3f, +#ifdef SPECULARTERM + specular: vec3f, +#endif +#ifdef NDOTL + ndl: f32, +#endif +}; + +fn computeLighting(viewDirectionW: vec3f, vNormal: vec3f, lightData: vec4f, diffuseColor: vec3f, specularColor: vec3f, range: f32, glossiness: f32) -> lightingInfo { + var result: lightingInfo; + + var lightVectorW: vec3f; + var attenuation: f32 = 1.0; + if (lightData.w == 0.) + { + var direction: vec3f = lightData.xyz - vPositionW; + + var attenuation: f32 = max(0., 1.0 - length(direction) / range); + lightVectorW = normalize(direction); + } + else + { + lightVectorW = normalize(-lightData.xyz); + } + + // diffuse + var ndl: f32 = max(0., dot(vNormal, lightVectorW)); +#ifdef NDOTL + result.ndl = ndl; +#endif + result.diffuse = ndl * diffuseColor * attenuation; +#ifdef SPECULARTERM + // Specular + var angleW: vec3f = normalize(viewDirectionW + lightVectorW); + var specComp: f32 = max(0., dot(vNormal, angleW)); + specComp = pow(specComp, max(1., glossiness)); + + result.specular = specComp * specularColor * attenuation; +#endif + return result; +} + +fn computeSpotLighting(viewDirectionW: vec3f, vNormal: vec3f , lightData: vec4f, lightDirection: vec4f, diffuseColor: vec3f, specularColor: vec3f, range: f32, glossiness: f32) -> lightingInfo { + var result: lightingInfo; + + var direction: vec3f = lightData.xyz - vPositionW; + var lightVectorW: vec3f = normalize(direction); + var attenuation: f32 = max(0., 1.0 - length(direction) / range); + + // diffuse + var cosAngle: f32 = max(0., dot(lightDirection.xyz, -lightVectorW)); + + if (cosAngle >= lightDirection.w) + { + cosAngle = max(0., pow(cosAngle, lightData.w)); + attenuation *= cosAngle; + + // Diffuse + var ndl: f32 = max(0., dot(vNormal, lightVectorW)); +#ifdef NDOTL + result.ndl = ndl; +#endif + result.diffuse = ndl * diffuseColor * attenuation; +#ifdef SPECULARTERM + // Specular + var angleW: vec3f = normalize(viewDirectionW + lightVectorW); + var specComp: f32 = max(0., dot(vNormal, angleW)); + specComp = pow(specComp, max(1., glossiness)); + + result.specular = specComp * specularColor * attenuation; +#endif + return result; + } + + result.diffuse = vec3f(0.); +#ifdef SPECULARTERM + result.specular = vec3f(0.); +#endif +#ifdef NDOTL + result.ndl = 0.; +#endif + + return result; +} + +fn computeHemisphericLighting(viewDirectionW: vec3f, vNormal: vec3f, lightData: vec4f, diffuseColor: vec3f, specularColor: vec3f, groundColor: vec3f, glossiness: f32) -> lightingInfo { + var result: lightingInfo; + + // Diffuse + var ndl: f32 = dot(vNormal, lightData.xyz) * 0.5 + 0.5; +#ifdef NDOTL + result.ndl = ndl; +#endif + + result.diffuse = mix(groundColor, diffuseColor, ndl); + +#ifdef SPECULARTERM + // Specular + var angleW: vec3f = normalize(viewDirectionW + lightData.xyz); + var specComp: f32 = max(0., dot(vNormal, angleW)); + specComp = pow(specComp, max(1., glossiness)); + + result.specular = specComp * specularColor; +#endif + return result; +} + +fn computeProjectionTextureDiffuseLighting(projectionLightTexture: texture_2d, projectionLightSampler: sampler, textureProjectionMatrix: mat4x4f) -> vec3f { + var strq: vec4f = textureProjectionMatrix * vec4f(vPositionW, 1.0); + strq /= strq.w; + var textureColor: vec3f = textureSample(projectionLightTexture, projectionLightSampler, strq.xy).rgb; + return textureColor; +} \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx index 128c3a42553..afdd55ed13d 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx @@ -1,7 +1,7 @@ #ifdef MORPHTARGETS #ifdef MORPHTARGETS_TEXTURE #if {X} == 0 - for (var i = 0; i < $NUM_MORPH_INFLUENCERS$; i = i + 1) { + for (var i = 0; i < NUM_MORPH_INFLUENCERS; i = i + 1) { if (i >= uniforms.morphTargetCount) { break; } diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/shadowsFragmentFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/shadowsFragmentFunctions.fx new file mode 100644 index 00000000000..7eb47607ffe --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/shadowsFragmentFunctions.fx @@ -0,0 +1,750 @@ +#ifdef SHADOWS + #ifndef SHADOWFLOAT + // Duplicate to prevent include in include issues + fn unpack(color: vec4f) -> f32 + { + const bit_shift: vec4f = vec4f(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0); + return dot(color, bit_shift); + } + #endif + + fn computeFallOff(value: f32, clipSpace: vec2f, frustumEdgeFalloff: f32) -> f32 + { + var mask: f32 = smoothstep(1.0 - frustumEdgeFalloff, 1.00000012, clamp(dot(clipSpace, clipSpace), 0., 1.)); + return mix(value, 1.0, mask); + } + + fn computeShadowCube(worldPos: vec3f, lightPosition: vec3f, shadowTexture: texture_cube, shadowSampler: sampler, darkness: f32, depthValues: vec2f) -> f32 + { + var directionToLight: vec3f = worldPos - lightPosition; + var depth: f32 = length(directionToLight); + depth = (depth + depthValues.x) / (depthValues.y); + depth = clamp(depth, 0., 1.0); + + directionToLight = normalize(directionToLight); + directionToLight.y = -directionToLight.y; + + #ifndef SHADOWFLOAT + var shadow: f32 = unpack(textureSample(shadowTexture, shadowSampler, directionToLight)); + #else + var shadow: f32 = textureSample(shadowTexture, shadowSampler, directionToLight).x; + #endif + + return select(darkness, 1.0, depth > shadow); + } + + fn computeShadowWithPoissonSamplingCube(worldPos: vec3f, lightPosition: vec3f, shadowTexture: texture_cube, shadowSampler: sampler, mapSize: f32, darkness: f32, depthValues: vec2f) -> f32 + { + var directionToLight: vec3f = worldPos - lightPosition; + var depth: f32 = length(directionToLight); + depth = (depth + depthValues.x) / (depthValues.y); + depth = clamp(depth, 0., 1.0); + + directionToLight = normalize(directionToLight); + directionToLight.y = -directionToLight.y; + + var visibility: f32 = 1.; + + var poissonDisk: array; + poissonDisk[0] = vec3f(-1.0, 1.0, -1.0); + poissonDisk[1] = vec3f(1.0, -1.0, -1.0); + poissonDisk[2] = vec3f(-1.0, -1.0, -1.0); + poissonDisk[3] = vec3f(1.0, -1.0, 1.0); + + // Poisson Sampling + + #ifndef SHADOWFLOAT + if (unpack(textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[0] * mapSize)) < depth) {visibility -= 0.25;}; + if (unpack(textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[1] * mapSize)) < depth) {visibility -= 0.25;}; + if (unpack(textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[2] * mapSize)) < depth) {visibility -= 0.25;}; + if (unpack(textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[3] * mapSize)) < depth) {visibility -= 0.25;}; + #else + if (textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[0] * mapSize).x < depth) {visibility -= 0.25;}; + if (textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[1] * mapSize).x < depth) {visibility -= 0.25;}; + if (textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[2] * mapSize).x < depth) {visibility -= 0.25;}; + if (textureSample(shadowTexture, shadowSampler, directionToLight + poissonDisk[3] * mapSize).x < depth) {visibility -= 0.25;}; + #endif + + return min(1.0, visibility + darkness); + } + + fn computeShadowWithESMCube(worldPos: vec3f, lightPosition: vec3f, shadowTexture: texture_cube, shadowSampler: sampler, darkness: f32, depthScale: f32, depthValues: vec2f) -> f32 + { + var directionToLight: vec3f = worldPos - lightPosition; + var depth: f32 = length(directionToLight); + depth = (depth + depthValues.x) / (depthValues.y); + var shadowPixelDepth: f32 = clamp(depth, 0., 1.0); + + directionToLight = normalize(directionToLight); + directionToLight.y = -directionToLight.y; + + #ifndef SHADOWFLOAT + var shadowMapSample: f32 = unpack(textureSample(shadowTexture, shadowSampler, directionToLight)); + #else + var shadowMapSample: f32 = textureSample(shadowTexture, shadowSampler, directionToLight).x; + #endif + + var esm: f32 = 1.0 - clamp(exp(min(87., depthScale * shadowPixelDepth)) * shadowMapSample, 0., 1. - darkness); + return esm; + } + + fn computeShadowWithCloseESMCube(worldPos: vec3f, lightPosition: vec3f, shadowTexture: texture_cube, shadowSampler: sampler, darkness: f32, depthScale: f32, depthValues: vec2f) -> f32 + { + var directionToLight: vec3f = worldPos - lightPosition; + var depth: f32 = length(directionToLight); + depth = (depth + depthValues.x) / (depthValues.y); + var shadowPixelDepth: f32 = clamp(depth, 0., 1.0); + + directionToLight = normalize(directionToLight); + directionToLight.y = -directionToLight.y; + + #ifndef SHADOWFLOAT + var shadowMapSample: f32 = unpack(textureSample(shadowTexture, shadowSampler, directionToLight)); + #else + var shadowMapSample: f32 = textureSample(shadowTexture, shadowSampler, directionToLight).x; + #endif + + var esm: f32 = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))), darkness, 1.); + + return esm; + } + + fn computeShadowCSM(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_2d_array, shadowSampler: sampler, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uv: vec2f = 0.5 * clipSpace.xy + vec2f(0.5); + + var shadowPixelDepth: f32 = clamp(depthMetric, 0., 1.0); + + #ifndef SHADOWFLOAT + var shadow: f32 = unpack(textureSample(shadowTexture, shadowSampler, uv, layer)); + #else + var shadow: f32 = textureSample(shadowTexture, shadowSampler, uv, layer).x; + #endif + + return select(1., computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff), shadowPixelDepth > shadow ); + } + + fn computeShadow(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_2d, shadowSampler: sampler, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uv: vec2f = 0.5 * clipSpace.xy + vec2f(0.5); + + if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0) + { + return 1.0; + } + else + { + var shadowPixelDepth: f32 = clamp(depthMetric, 0., 1.0); + + #ifndef SHADOWFLOAT + var shadow: f32 = unpack(textureSampleLevel(shadowTexture, shadowSampler, uv, 0.)); + #else + var shadow: f32 = textureSampleLevel(shadowTexture, shadowSampler, uv, 0.).x; + #endif + + return select(1., computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff), shadowPixelDepth > shadow ); + } + } + + fn computeShadowWithPoissonSampling(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_2d, shadowSampler: sampler, mapSize: f32, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uv: vec2f = 0.5 * clipSpace.xy + vec2f(0.5); + + if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0) + { + return 1.0; + } + else + { + var shadowPixelDepth: f32 = clamp(depthMetric, 0., 1.0); + + var visibility: f32 = 1.; + + var poissonDisk: array; + poissonDisk[0] = vec2f(-0.94201624, -0.39906216); + poissonDisk[1] = vec2f(0.94558609, -0.76890725); + poissonDisk[2] = vec2f(-0.094184101, -0.92938870); + poissonDisk[3] = vec2f(0.34495938, 0.29387760); + + // Poisson Sampling + + #ifndef SHADOWFLOAT + if (unpack(textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[0] * mapSize, 0.)) < shadowPixelDepth) {visibility -= 0.25;} + if (unpack(textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[1] * mapSize, 0.)) < shadowPixelDepth) {visibility -= 0.25;} + if (unpack(textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[2] * mapSize, 0.)) < shadowPixelDepth) {visibility -= 0.25;} + if (unpack(textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[3] * mapSize, 0.)) < shadowPixelDepth) {visibility -= 0.25;} + #else + if (textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[0] * mapSize, 0.).x < shadowPixelDepth) {visibility -= 0.25;} + if (textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[1] * mapSize, 0.).x < shadowPixelDepth) {visibility -= 0.25;} + if (textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[2] * mapSize, 0.).x < shadowPixelDepth) {visibility -= 0.25;} + if (textureSampleLevel(shadowTexture, shadowSampler, uv + poissonDisk[3] * mapSize, 0.).x < shadowPixelDepth) {visibility -= 0.25;} + #endif + + return computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff); + } + } + + fn computeShadowWithESM(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_2d, shadowSampler: sampler, darkness: f32, depthScale: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uv: vec2f = 0.5 * clipSpace.xy + vec2f(0.5); + + if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0) + { + return 1.0; + } + else + { + var shadowPixelDepth: f32 = clamp(depthMetric, 0., 1.0); + + #ifndef SHADOWFLOAT + var shadowMapSample: f32 = unpack(textureSampleLevel(shadowTexture, shadowSampler, uv, 0.)); + #else + var shadowMapSample: f32 = textureSampleLevel(shadowTexture, shadowSampler, uv, 0.).x; + #endif + + var esm: f32 = 1.0 - clamp(exp(min(87., depthScale * shadowPixelDepth)) * shadowMapSample, 0., 1. - darkness); + + return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff); + } + } + + fn computeShadowWithCloseESM(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_2d, shadowSampler: sampler, darkness: f32, depthScale: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uv: vec2f = 0.5 * clipSpace.xy + vec2f(0.5); + + if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0) + { + return 1.0; + } + else + { + var shadowPixelDepth: f32 = clamp(depthMetric, 0., 1.0); + + #ifndef SHADOWFLOAT + var shadowMapSample: f32 = unpack(textureSampleLevel(shadowTexture, shadowSampler, uv, 0.)); + #else + var shadowMapSample: f32 = textureSampleLevel(shadowTexture, shadowSampler, uv, 0.).x; + #endif + + var esm: f32 = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))), darkness, 1.); + + return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff); + } + } + + fn getZInClip(clipSpace: vec3f, uvDepth: vec3f) -> f32 + { + #ifdef IS_NDC_HALF_ZRANGE + return clipSpace.z; + #else + return uvDepth.z; + #endif + } + + + const GREATEST_LESS_THAN_ONE: f32 = 0.99999994; + + // We need to disable uniformity analysis when using CSM, as there's no textureLod overload that takes a sampler2DArrayShadow. + // And the workaround that uses textureGrad (which does work with sampler2DArrayShadow) is not supported by the SpirV to WGSL conversion (from Tint) + + #define DIAGNOSTIC_OFF + + // Shadow PCF kernel size 1 with a single tap (lowest quality) + + fn computeShadowWithCSMPCF1(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + + uvDepth.z = clamp(getZInClip(clipSpace, uvDepth), 0., GREATEST_LESS_THAN_ONE); + + var shadow: f32 = textureSampleCompare(shadowTexture, shadowSampler, uvDepth.xy, layer, uvDepth.z); + shadow = mix(darkness, 1., shadow); + return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff); + } + + // Shadow PCF kernel 3*3 in only 4 taps (medium quality) + // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel + // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/ + + fn computeShadowWithCSMPCF3(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, shadowMapSizeAndInverse: vec2f, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + + uvDepth.z = clamp(getZInClip(clipSpace, uvDepth), 0., GREATEST_LESS_THAN_ONE); + + var uv: vec2f = uvDepth.xy * shadowMapSizeAndInverse.x; // uv in texel units + uv += 0.5; // offset of half to be in the center of the texel + var st: vec2f = fract(uv); // how far from the center + var base_uv: vec2f = floor(uv) - 0.5; // texel coord + base_uv *= shadowMapSizeAndInverse.y; // move back to uv coords + + // Equation resolved to fit in a 3*3 distribution like + // 1 2 1 + // 2 4 2 + // 1 2 1 + var uvw0: vec2f = 3. - 2. * st; + var uvw1: vec2f = 1. + 2. * st; + var u: vec2f = vec2f((2. - st.x) / uvw0.x - 1., st.x / uvw1.x + 1.) * shadowMapSizeAndInverse.y; + var v: vec2f = vec2f((2. - st.y) / uvw0.y - 1., st.y / uvw1.y + 1.) * shadowMapSizeAndInverse.y; + + var shadow: f32 = 0.; + shadow += uvw0.x * uvw0.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[0]), layer, uvDepth.z); + shadow += uvw1.x * uvw0.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[0]), layer, uvDepth.z); + shadow += uvw0.x * uvw1.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[1]), layer, uvDepth.z); + shadow += uvw1.x * uvw1.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[1]), layer, uvDepth.z); + shadow = shadow / 16.; + + shadow = mix(darkness, 1., shadow); + return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff); + } + + // Shadow PCF kernel 5*5 in only 9 taps (high quality) + // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel + // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/ + + fn computeShadowWithCSMPCF5(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, shadowMapSizeAndInverse: vec2f, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + + uvDepth.z = clamp(getZInClip(clipSpace, uvDepth), 0., GREATEST_LESS_THAN_ONE); + + var uv: vec2f = uvDepth.xy * shadowMapSizeAndInverse.x; // uv in texel units + uv += 0.5; // offset of half to be in the center of the texel + var st: vec2f = fract(uv); // how far from the center + var base_uv: vec2f = floor(uv) - 0.5; // texel coord + base_uv *= shadowMapSizeAndInverse.y; // move back to uv coords + + // Equation resolved to fit in a 5*5 distribution like + // 1 2 4 2 1 + var uvw0: vec2f = 4. - 3. * st; + var uvw1: vec2f = vec2f(7.); + var uvw2: vec2f = 1. + 3. * st; + + var u: vec3f = vec3f((3. - 2. * st.x) / uvw0.x - 2., (3. + st.x) / uvw1.x, st.x / uvw2.x + 2.) * shadowMapSizeAndInverse.y; + var v: vec3f = vec3f((3. - 2. * st.y) / uvw0.y - 2., (3. + st.y) / uvw1.y, st.y / uvw2.y + 2.) * shadowMapSizeAndInverse.y; + + var shadow: f32 = 0.; + shadow += uvw0.x * uvw0.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[0]), layer, uvDepth.z); + shadow += uvw1.x * uvw0.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[0]), layer, uvDepth.z); + shadow += uvw2.x * uvw0.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[2], v[0]), layer, uvDepth.z); + shadow += uvw0.x * uvw1.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[1]), layer, uvDepth.z); + shadow += uvw1.x * uvw1.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[1]), layer, uvDepth.z); + shadow += uvw2.x * uvw1.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[2], v[1]), layer, uvDepth.z); + shadow += uvw0.x * uvw2.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[2]), layer, uvDepth.z); + shadow += uvw1.x * uvw2.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[2]), layer, uvDepth.z); + shadow += uvw2.x * uvw2.y * textureSampleCompare(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[2], v[2]), layer, uvDepth.z); + shadow = shadow / 144.; + + shadow = mix(darkness, 1., shadow); + return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff); + } + + // Shadow PCF kernel size 1 with a single tap (lowest quality) + + fn computeShadowWithPCF1(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + if (depthMetric > 1.0 || depthMetric < 0.0) { + return 1.0; + } + else + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + uvDepth.z = getZInClip(clipSpace, uvDepth); + + var shadow: f32 = textureSampleCompareLevel(shadowTexture, shadowSampler, uvDepth.xy, uvDepth.z); + shadow = mix(darkness, 1., shadow); + + return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff); + } + } + + // Shadow PCF kernel 3*3 in only 4 taps (medium quality) + // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel + // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/ + + fn computeShadowWithPCF3(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, shadowMapSizeAndInverse: vec2f, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + if (depthMetric > 1.0 || depthMetric < 0.0) { + return 1.0; + } + else + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + uvDepth.z = getZInClip(clipSpace, uvDepth); + + var uv: vec2f = uvDepth.xy * shadowMapSizeAndInverse.x; // uv in texel units + uv += 0.5; // offset of half to be in the center of the texel + var st: vec2f = fract(uv); // how far from the center + var base_uv: vec2f = floor(uv) - 0.5; // texel coord + base_uv *= shadowMapSizeAndInverse.y; // move back to uv coords + + // Equation resolved to fit in a 3*3 distribution like + // 1 2 1 + // 2 4 2 + // 1 2 1 + var uvw0: vec2f = 3. - 2. * st; + var uvw1: vec2f = 1. + 2. * st; + var u: vec2f = vec2f((2. - st.x) / uvw0.x - 1., st.x / uvw1.x + 1.) * shadowMapSizeAndInverse.y; + var v: vec2f = vec2f((2. - st.y) / uvw0.y - 1., st.y / uvw1.y + 1.) * shadowMapSizeAndInverse.y; + + var shadow: f32 = 0.; + shadow += uvw0.x * uvw0.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[0]), uvDepth.z); + shadow += uvw1.x * uvw0.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[0]), uvDepth.z); + shadow += uvw0.x * uvw1.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[1]), uvDepth.z); + shadow += uvw1.x * uvw1.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[1]), uvDepth.z); + shadow = shadow / 16.; + + shadow = mix(darkness, 1., shadow); + + return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff); + } + } + + // Shadow PCF kernel 5*5 in only 9 taps (high quality) + // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel + // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/ + + fn computeShadowWithPCF5(vPositionFromLight: vec4f, depthMetric: f32, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, shadowMapSizeAndInverse: vec2f, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + if (depthMetric > 1.0 || depthMetric < 0.0) { + return 1.0; + } + else + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + uvDepth.z = getZInClip(clipSpace, uvDepth); + + var uv: vec2f = uvDepth.xy * shadowMapSizeAndInverse.x; // uv in texel units + uv += 0.5; // offset of half to be in the center of the texel + var st: vec2f = fract(uv); // how far from the center + var base_uv: vec2f = floor(uv) - 0.5; // texel coord + base_uv *= shadowMapSizeAndInverse.y; // move back to uv coords + + // Equation resolved to fit in a 5*5 distribution like + // 1 2 4 2 1 + var uvw0: vec2f = 4. - 3. * st; + var uvw1: vec2f = vec2f(7.); + var uvw2: vec2f = 1. + 3. * st; + + var u: vec3f = vec3f((3. - 2. * st.x) / uvw0.x - 2., (3. + st.x) / uvw1.x, st.x / uvw2.x + 2.) * shadowMapSizeAndInverse.y; + var v: vec3f = vec3f((3. - 2. * st.y) / uvw0.y - 2., (3. + st.y) / uvw1.y, st.y / uvw2.y + 2.) * shadowMapSizeAndInverse.y; + + var shadow: f32 = 0.; + shadow += uvw0.x * uvw0.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[0]), uvDepth.z); + shadow += uvw1.x * uvw0.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[0]), uvDepth.z); + shadow += uvw2.x * uvw0.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[2], v[0]), uvDepth.z); + shadow += uvw0.x * uvw1.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[1]), uvDepth.z); + shadow += uvw1.x * uvw1.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[1]), uvDepth.z); + shadow += uvw2.x * uvw1.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[2], v[1]), uvDepth.z); + shadow += uvw0.x * uvw2.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[0], v[2]), uvDepth.z); + shadow += uvw1.x * uvw2.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[1], v[2]), uvDepth.z); + shadow += uvw2.x * uvw2.y * textureSampleCompareLevel(shadowTexture, shadowSampler, base_uv.xy + vec2f(u[2], v[2]), uvDepth.z); + shadow = shadow / 144.; + + shadow = mix(darkness, 1., shadow); + + return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff); + } + } + + const PoissonSamplers32: array = array ( + vec3f(0.06407013, 0.05409927, 0.), + vec3f(0.7366577, 0.5789394, 0.), + vec3f(-0.6270542, -0.5320278, 0.), + vec3f(-0.4096107, 0.8411095, 0.), + vec3f(0.6849564, -0.4990818, 0.), + vec3f(-0.874181, -0.04579735, 0.), + vec3f(0.9989998, 0.0009880066, 0.), + vec3f(-0.004920578, -0.9151649, 0.), + vec3f(0.1805763, 0.9747483, 0.), + vec3f(-0.2138451, 0.2635818, 0.), + vec3f(0.109845, 0.3884785, 0.), + vec3f(0.06876755, -0.3581074, 0.), + vec3f(0.374073, -0.7661266, 0.), + vec3f(0.3079132, -0.1216763, 0.), + vec3f(-0.3794335, -0.8271583, 0.), + vec3f(-0.203878, -0.07715034, 0.), + vec3f(0.5912697, 0.1469799, 0.), + vec3f(-0.88069, 0.3031784, 0.), + vec3f(0.5040108, 0.8283722, 0.), + vec3f(-0.5844124, 0.5494877, 0.), + vec3f(0.6017799, -0.1726654, 0.), + vec3f(-0.5554981, 0.1559997, 0.), + vec3f(-0.3016369, -0.3900928, 0.), + vec3f(-0.5550632, -0.1723762, 0.), + vec3f(0.925029, 0.2995041, 0.), + vec3f(-0.2473137, 0.5538505, 0.), + vec3f(0.9183037, -0.2862392, 0.), + vec3f(0.2469421, 0.6718712, 0.), + vec3f(0.3916397, -0.4328209, 0.), + vec3f(-0.03576927, -0.6220032, 0.), + vec3f(-0.04661255, 0.7995201, 0.), + vec3f(0.4402924, 0.3640312, 0.), + + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.), + vec3f(0.) + ); + + const PoissonSamplers64: array = array ( + vec3f(-0.613392, 0.617481, 0.), + vec3f(0.170019, -0.040254, 0.), + vec3f(-0.299417, 0.791925, 0.), + vec3f(0.645680, 0.493210, 0.), + vec3f(-0.651784, 0.717887, 0.), + vec3f(0.421003, 0.027070, 0.), + vec3f(-0.817194, -0.271096, 0.), + vec3f(-0.705374, -0.668203, 0.), + vec3f(0.977050, -0.108615, 0.), + vec3f(0.063326, 0.142369, 0.), + vec3f(0.203528, 0.214331, 0.), + vec3f(-0.667531, 0.326090, 0.), + vec3f(-0.098422, -0.295755, 0.), + vec3f(-0.885922, 0.215369, 0.), + vec3f(0.566637, 0.605213, 0.), + vec3f(0.039766, -0.396100, 0.), + vec3f(0.751946, 0.453352, 0.), + vec3f(0.078707, -0.715323, 0.), + vec3f(-0.075838, -0.529344, 0.), + vec3f(0.724479, -0.580798, 0.), + vec3f(0.222999, -0.215125, 0.), + vec3f(-0.467574, -0.405438, 0.), + vec3f(-0.248268, -0.814753, 0.), + vec3f(0.354411, -0.887570, 0.), + vec3f(0.175817, 0.382366, 0.), + vec3f(0.487472, -0.063082, 0.), + vec3f(-0.084078, 0.898312, 0.), + vec3f(0.488876, -0.783441, 0.), + vec3f(0.470016, 0.217933, 0.), + vec3f(-0.696890, -0.549791, 0.), + vec3f(-0.149693, 0.605762, 0.), + vec3f(0.034211, 0.979980, 0.), + vec3f(0.503098, -0.308878, 0.), + vec3f(-0.016205, -0.872921, 0.), + vec3f(0.385784, -0.393902, 0.), + vec3f(-0.146886, -0.859249, 0.), + vec3f(0.643361, 0.164098, 0.), + vec3f(0.634388, -0.049471, 0.), + vec3f(-0.688894, 0.007843, 0.), + vec3f(0.464034, -0.188818, 0.), + vec3f(-0.440840, 0.137486, 0.), + vec3f(0.364483, 0.511704, 0.), + vec3f(0.034028, 0.325968, 0.), + vec3f(0.099094, -0.308023, 0.), + vec3f(0.693960, -0.366253, 0.), + vec3f(0.678884, -0.204688, 0.), + vec3f(0.001801, 0.780328, 0.), + vec3f(0.145177, -0.898984, 0.), + vec3f(0.062655, -0.611866, 0.), + vec3f(0.315226, -0.604297, 0.), + vec3f(-0.780145, 0.486251, 0.), + vec3f(-0.371868, 0.882138, 0.), + vec3f(0.200476, 0.494430, 0.), + vec3f(-0.494552, -0.711051, 0.), + vec3f(0.612476, 0.705252, 0.), + vec3f(-0.578845, -0.768792, 0.), + vec3f(-0.772454, -0.090976, 0.), + vec3f(0.504440, 0.372295, 0.), + vec3f(0.155736, 0.065157, 0.), + vec3f(0.391522, 0.849605, 0.), + vec3f(-0.620106, -0.328104, 0.), + vec3f(0.789239, -0.419965, 0.), + vec3f(-0.545396, 0.538133, 0.), + vec3f(-0.178564, -0.596057, 0.) + ); + + // PCSS + // This helps to achieve a contact hardening effect on the shadow + // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc. + // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf + // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf + + fn computeShadowWithCSMPCSS(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d_array, depthSampler: sampler, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32, searchTapCount: i32, pcfTapCount: i32, poissonSamplers: array, lightSizeUVCorrection: vec2f, depthCorrection: f32, penumbraDarkness: f32) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + + uvDepth.z = clamp(getZInClip(clipSpace, uvDepth), 0., GREATEST_LESS_THAN_ONE); + + var uvDepthLayer: vec4f = vec4f(uvDepth.x, uvDepth.y, f32(layer), uvDepth.z); + + var blockerDepth: f32 = 0.0; + var sumBlockerDepth: f32 = 0.0; + var numBlocker: f32 = 0.0; + for (var i: i32 = 0; i < searchTapCount; i ++) { + blockerDepth = textureSample(depthTexture, depthSampler, uvDepth.xy + (lightSizeUV * lightSizeUVCorrection * shadowMapSizeInverse * PoissonSamplers32[i].xy), layer).r; + numBlocker += select(0., 1., blockerDepth < depthMetric); + sumBlockerDepth += select(0., blockerDepth, blockerDepth < depthMetric); + } + + var avgBlockerDepth: f32 = sumBlockerDepth / numBlocker; + + // Offset preventing aliasing on contact. + var AAOffset: f32 = shadowMapSizeInverse * 10.; + // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size. + // var penumbraRatio: f32 = (depthMetric - avgBlockerDepth) / avgBlockerDepth; + var penumbraRatio: f32 = ((depthMetric - avgBlockerDepth) * depthCorrection + AAOffset); + var filterRadius: vec4f = vec4f(penumbraRatio * lightSizeUV * lightSizeUVCorrection * shadowMapSizeInverse, 0., 0.); + + var random: f32 = getRand(vPositionFromLight.xy); + var rotationAngle: f32 = random * 3.1415926; + var rotationVector: vec2f = vec2f(cos(rotationAngle), sin(rotationAngle)); + + var shadow: f32 = 0.; + for (var i: i32 = 0; i < pcfTapCount; i++) { + var offset: vec4f = vec4f(poissonSamplers[i], 0.); + // Rotated offset. + offset = vec4f(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0., 0.); + let coords = uvDepthLayer + offset * filterRadius; + shadow += textureSampleCompare(shadowTexture, shadowSampler, coords.xy, i32(coords.z), coords.w); + } + shadow /= f32(pcfTapCount); + + // Blocker distance falloff + shadow = mix(shadow, 1., min((depthMetric - avgBlockerDepth) * depthCorrection * penumbraDarkness, 1.)); + + // Apply darkness + shadow = mix(darkness, 1., shadow); + + // Apply light frustrum fallof + return select(computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff), 1.0, numBlocker < 1.0); + } + + // PCSS + // This helps to achieve a contact hardening effect on the shadow + // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc. + // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf + // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf + + fn computeShadowWithPCSS(vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d, depthSampler: sampler, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32, searchTapCount: i32, pcfTapCount: i32, poissonSamplers: array) -> f32 + { + var clipSpace: vec3f = vPositionFromLight.xyz / vPositionFromLight.w; + var uvDepth: vec3f = vec3f(0.5 * clipSpace.xyz + vec3f(0.5)); + uvDepth.z = getZInClip(clipSpace, uvDepth); + + var blockerDepth: f32 = 0.0; + var sumBlockerDepth: f32 = 0.0; + var numBlocker: f32 = 0.0; + var exitCondition: bool = depthMetric > 1.0 || depthMetric < 0.0; + for (var i: i32 = 0; i < searchTapCount; i ++) { + if (exitCondition) { + break; + } + blockerDepth = textureSampleLevel(depthTexture, depthSampler, uvDepth.xy + (lightSizeUV * shadowMapSizeInverse * PoissonSamplers32[i].xy), 0).r; + numBlocker += select(0., 1., blockerDepth < depthMetric); + sumBlockerDepth += select(0., blockerDepth, blockerDepth < depthMetric); + } + + exitCondition = exitCondition || numBlocker < 1.0; + var avgBlockerDepth: f32 = sumBlockerDepth / numBlocker; + + // Offset preventing aliasing on contact. + var AAOffset: f32 = shadowMapSizeInverse * 10.; + // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size. + // var penumbraRatio: f32 = (depthMetric - avgBlockerDepth) / avgBlockerDepth; + var penumbraRatio: f32 = ((depthMetric - avgBlockerDepth) + AAOffset); + var filterRadius: f32 = penumbraRatio * lightSizeUV * shadowMapSizeInverse; + + var random: f32 = getRand(vPositionFromLight.xy); + var rotationAngle: f32 = random * 3.1415926; + var rotationVector: vec2f = vec2f(cos(rotationAngle), sin(rotationAngle)); + + var shadow: f32 = 0.; + for (var i: i32 = 0; i < pcfTapCount; i++) { + if (exitCondition) { + break; + } + var offset: vec3f = poissonSamplers[i]; + // Rotated offset. + offset = vec3f(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0.); + let coords = uvDepth + offset * filterRadius; + shadow += textureSampleCompareLevel(shadowTexture, shadowSampler, coords.xy, coords.z); + } + shadow /= f32(pcfTapCount); + + // Blocker distance falloff + shadow = mix(shadow, 1., depthMetric - avgBlockerDepth); + + // Apply darkness + shadow = mix(darkness, 1., shadow); + + // Apply light frustrum fallof + return select(computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff), 1.0, exitCondition); + } + + + fn computeShadowWithPCSS16(vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d, depthSampler: sampler, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthTexture, depthSampler, shadowTexture, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32); + } + + + fn computeShadowWithPCSS32(vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d, depthSampler: sampler, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthTexture, depthSampler, shadowTexture, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32); + } + + + fn computeShadowWithPCSS64(vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d, depthSampler: sampler, shadowTexture: texture_depth_2d, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32) -> f32 + { + return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthTexture, depthSampler, shadowTexture, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64); + } + + + fn computeShadowWithCSMPCSS16(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d_array, depthSampler: sampler, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32, lightSizeUVCorrection: vec2f, depthCorrection: f32, penumbraDarkness: f32) -> f32 + { + return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthTexture, depthSampler, shadowTexture, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness); + } + + + fn computeShadowWithCSMPCSS32(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d_array, depthSampler: sampler, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32, lightSizeUVCorrection: vec2f, depthCorrection: f32, penumbraDarkness: f32) -> f32 + { + return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthTexture, depthSampler, shadowTexture, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness); + } + + + fn computeShadowWithCSMPCSS64(layer: i32, vPositionFromLight: vec4f, depthMetric: f32, depthTexture: texture_2d_array, depthSampler: sampler, shadowTexture: texture_depth_2d_array, shadowSampler: sampler_comparison, shadowMapSizeInverse: f32, lightSizeUV: f32, darkness: f32, frustumEdgeFalloff: f32, lightSizeUVCorrection: vec2f, depthCorrection: f32, penumbraDarkness: f32) -> f32 + { + return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthTexture, depthSampler, shadowTexture, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64, lightSizeUVCorrection, depthCorrection, penumbraDarkness); + } +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/shadowsVertex.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/shadowsVertex.fx new file mode 100644 index 00000000000..beca1ddea1f --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/shadowsVertex.fx @@ -0,0 +1,44 @@ +#ifdef SHADOWS + #if defined(SHADOWCSM{X}) + vertexOutputs.vPositionFromCamera{X} = view * worldPos; + #if SHADOWCSMNUM_CASCADES{X} > 0 + vertexOutputs.vPositionFromLight{X}_0 = uniforms.lightMatrix{X}[0] * worldPos; + #ifdef USE_REVERSE_DEPTHBUFFER + vertexOutputs.vDepthMetric{X}_0 = (-vertexOutputs.vPositionFromLight{X}_0.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #else + vertexOutputs.vDepthMetric{X}_0= (vertexOutputs.vPositionFromLight{X}_0.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #endif + #endif + #if SHADOWCSMNUM_CASCADES{X} > 1 + vertexOutputs.vPositionFromLight{X}_1 = uniforms.lightMatrix{X}[1] * worldPos; + #ifdef USE_REVERSE_DEPTHBUFFER + vertexOutputs.vDepthMetric{X}_1 = (-vertexOutputs.vPositionFromLight{X}_1.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #else + vertexOutputs.vDepthMetric{X}_1= (vertexOutputs.vPositionFromLight{X}_1.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #endif + #endif + #if SHADOWCSMNUM_CASCADES{X} > 2 + vertexOutputs.vPositionFromLight{X}_2 = uniforms.lightMatrix{X}[2] * worldPos; + #ifdef USE_REVERSE_DEPTHBUFFER + vertexOutputs.vDepthMetric{X}_2 = (-vertexOutputs.vPositionFromLight{X}_2.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #else + vertexOutputs.vDepthMetric{X}_2= (vertexOutputs.vPositionFromLight{X}_2.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #endif + #endif + #if SHADOWCSMNUM_CASCADES{X} > 3 + vertexOutputs.vPositionFromLight{X}_3 = uniforms.lightMatrix{X}[3] * worldPos; + #ifdef USE_REVERSE_DEPTHBUFFER + vertexOutputs.vDepthMetric{X}_3 = (-vertexOutputs.vPositionFromLight{X}_3.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #else + vertexOutputs.vDepthMetric{X}_3= (vertexOutputs.vPositionFromLight{X}_3.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #endif + #endif + #elif defined(SHADOW{X}) && !defined(SHADOWCUBE{X}) + vertexOutputs.vPositionFromLight{X} = uniforms.lightMatrix{X} * worldPos; + #ifdef USE_REVERSE_DEPTHBUFFER + vertexOutputs.vDepthMetric{X} = (-vertexOutputs.vPositionFromLight{X}.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #else + vertexOutputs.vDepthMetric{X} = (vertexOutputs.vPositionFromLight{X}.z + light{X}.depthValues.x) / light{X}.depthValues.y; + #endif + #endif +#endif \ No newline at end of file diff --git a/packages/tools/nodeEditor/public/index.js b/packages/tools/nodeEditor/public/index.js index 71f5264b871..808bded7ab5 100644 --- a/packages/tools/nodeEditor/public/index.js +++ b/packages/tools/nodeEditor/public/index.js @@ -211,7 +211,7 @@ checkBabylonVersionAsync().then(() => { if (xmlHttp.status == 200) { let baseUrl = location.href.replace(location.hash, "").replace(location.search, ""); let snippet = JSON.parse(xmlHttp.responseText); - let newUrl = baseUrl + "#" + snippet.id; + let newUrl = baseUrl + (useWebGPU ? "?webgpu" : "") + "#" + snippet.id; currentSnippetToken = snippet.id; if (snippet.version && snippet.version != "0") { newUrl += "#" + snippet.version;