diff --git a/src/core/Config.js b/src/core/Config.js index 3c9bef969..69344977e 100644 --- a/src/core/Config.js +++ b/src/core/Config.js @@ -395,6 +395,11 @@ var Config = new Class({ */ this.roundPixels = GetValue(renderConfig, 'roundPixels', true, config); + /** + * @const {boolean} Phaser.Core.Config#selfShadow - On textured objects with lighting, this enables self-shadowing based on the diffuse map. + */ + this.selfShadow = GetValue(renderConfig, 'selfShadow', false, config); + /** * @const {number} Phaser.Core.Config#pathDetailThreshold - Threshold for combining points into a single path in the WebGL renderer for Graphics objects. This can be overridden at the Graphics object level. */ diff --git a/src/core/typedefs/RenderConfig.js b/src/core/typedefs/RenderConfig.js index 174f9be1f..1aad3e3ed 100644 --- a/src/core/typedefs/RenderConfig.js +++ b/src/core/typedefs/RenderConfig.js @@ -7,6 +7,7 @@ * @property {boolean} [desynchronized=false] - When set to `true` it will create a desynchronized context for both 2D and WebGL. See https://developers.google.com/web/updates/2019/05/desynchronized for details. * @property {boolean} [pixelArt=false] - Sets `antialias` to false and `roundPixels` to true. This is the best setting for pixel-art games. * @property {boolean} [roundPixels=true] - Draw texture-based Game Objects at only whole-integer positions. Game Objects without textures, like Graphics, ignore this property. + * @property {boolean} [selfShadow=false] - On textured objects with lighting, this enables self-shadowing based on the diffuse map. * @property {number} [pathDetailThreshold=1] - Threshold for combining points into a single path in the WebGL renderer for Graphics objects. This can be overridden at the Graphics object level. * @property {boolean} [transparent=false] - Whether the game canvas will be transparent. Boolean that indicates if the canvas contains an alpha channel. If set to false, the browser now knows that the backdrop is always opaque, which can speed up drawing of transparent content and images. * @property {boolean} [clearBeforeRender=true] - Whether the game canvas will be cleared between each rendering frame. diff --git a/src/renderer/webgl/renderNodes/BatchHandlerQuadLightShadow.js b/src/renderer/webgl/renderNodes/BatchHandlerQuadLightShadow.js new file mode 100644 index 000000000..58d0ff4d6 --- /dev/null +++ b/src/renderer/webgl/renderNodes/BatchHandlerQuadLightShadow.js @@ -0,0 +1,138 @@ +/** + * @author Benjamin D. Richards + * @copyright 2013-2024 Phaser Studio Inc. + * @license {@link https://opensource.org/licenses/MIT|MIT License} + */ + +var Class = require('../../../utils/Class'); +var LightShaderSourceFS = require('../shaders/LightShadow-frag'); +var ShaderSourceVS = require('../shaders/Multi-vert'); +var BatchHandlerQuadLight = require('./BatchHandlerQuadLight'); + +/** + * @classdesc + * The BatchHandlerQuadLightShadow is a special type of BatchHandlerQuadLight + * that supports self-shadowing based on the diffuse map. + * + * The shader uses the diffuse map to determine the concavity of the surface. + * Darker areas are assumed to be more concave, thus they can only receive light + * from a smaller range of angles. Light outside that range is cut off, + * creating a shadow. + * + * Because most game art wasn't created with these characteristics in mind, + * you may need to adjust the `diffuseFlatThreshold` and `penumbra` values + * to get the desired effect. + * + * To use this RenderNode in your game, you must set the option + * `render.selfShadow` to `true` in your game configuration. + * It will affect all textured objects with lighting enabled + * (technically, all objects that use the `BatchHandlerQuadLight` RenderNode). + * + * @class BatchHandlerQuadLightShadow + * @extends Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuadLight + * @memberof Phaser.Renderer.WebGL.RenderNodes + * @constructor + * @since 3.90.0 + * @param {Phaser.Renderer.WebGL.WebGLRenderer} manager - The WebGLRenderer instance that owns this handler. + * @param {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig} config - The configuration object for this handler. + */ +var BatchHandlerQuadLightShadow = new Class({ + Extends: BatchHandlerQuadLight, + + initialize: function BatchHandlerQuadLightShadow (manager, config) + { + BatchHandlerQuadLight.call(this, manager, config); + + /** + * The threshold at which the diffuse lighting will be considered flat. + * This is used to derive self-shadowing from the diffuse map. + * + * This is a brightness value in the range 0-1. + * Because art is usually not pure white, the default is 1/3, + * a darker value, which is more likely to be considered flat. + * You should adjust this value based on the art in your game. + * + * @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuadLightShadow#diffuseFlatThreshold + * @type {number} + * @default 1 + * @since 3.90.0 + */ + this.diffuseFlatThreshold = 1 / 3; + + /** + * The penumbra value for the shadow. + * This smooths the edge of self-shadowing. + * A lower value will create a sharper but more jagged shadow. + * + * @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuadLightShadow#penumbra + * @type {number} + * @default 0.5 + * @since 3.90.0 + */ + this.penumbra = 0.5; + }, + + /** + * The default configuration settings for BatchHandlerQuadLightShadow. + * + * @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuadLightShadow#defaultConfig + * @type {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig} + * @since 3.90.0 + * @readonly + */ + defaultConfig: { + name: 'BatchHandlerQuadLight', + verticesPerInstance: 4, + indicesPerInstance: 6, + vertexSource: ShaderSourceVS, + fragmentSource: LightShaderSourceFS, + vertexBufferLayout: { + usage: 'DYNAMIC_DRAW', + layout: [ + { + name: 'inPosition', + size: 2 + }, + { + name: 'inTexCoord', + size: 2 + }, + { + name: 'inTintEffect' + }, + { + name: 'inTint', + size: 4, + type: 'UNSIGNED_BYTE', + normalized: true + } + ] + } + }, + + /** + * Called at the start of the run loop. + * + * @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuadLightShadow#onRunBegin + * @since 3.90.0 + * @param {Phaser.Renderer.WebGL.WebGLPipeline} drawingContext - The drawing context. + */ + onRunBegin: function (drawingContext) + { + BatchHandlerQuadLight.prototype.onRunBegin.call(this, drawingContext); + + var program = this.program; + + program.setUniform( + 'uDiffuseFlatThreshold', + this.diffuseFlatThreshold * 3 + ); + + program.setUniform( + 'uPenumbra', + this.penumbra + ); + } +}); + +module.exports = BatchHandlerQuadLightShadow; diff --git a/src/renderer/webgl/renderNodes/RenderNodeManager.js b/src/renderer/webgl/renderNodes/RenderNodeManager.js index 32e683ae3..de4afb855 100644 --- a/src/renderer/webgl/renderNodes/RenderNodeManager.js +++ b/src/renderer/webgl/renderNodes/RenderNodeManager.js @@ -19,6 +19,7 @@ var DefaultPointLightNodes = require('./defaults/DefaultPointLightNodes'); var BatchHandlerPointLight = require('./BatchHandlerPointLight'); var BatchHandlerQuad = require('./BatchHandlerQuad'); var BatchHandlerQuadLight = require('./BatchHandlerQuadLight'); +var BatchHandlerQuadLightShadow = require('./BatchHandlerQuadLightShadow'); var BatchHandlerTriFlat = require('./BatchHandlerTriFlat'); var BatchHandlerTriFlatLight = require('./BatchHandlerTriFlatLight'); var Camera = require('./Camera'); @@ -159,6 +160,11 @@ var RenderNodeManager = new Class({ YieldContext: YieldContext }; + if (game.config.selfShadow) + { + this._nodeConstructors.BatchHandlerQuadLight = BatchHandlerQuadLightShadow; + } + /** * The RenderNode which is currently being filled. * This is stored so that it can be completed when another type of diff --git a/src/renderer/webgl/renderNodes/index.js b/src/renderer/webgl/renderNodes/index.js index 9d1fefe89..d5434d507 100644 --- a/src/renderer/webgl/renderNodes/index.js +++ b/src/renderer/webgl/renderNodes/index.js @@ -13,6 +13,7 @@ var RenderNodes = { BatchHandlerPointLight: require('./BatchHandlerPointLight'), BatchHandlerQuad: require('./BatchHandlerQuad'), BatchHandlerQuadLight: require('./BatchHandlerQuadLight'), + BatchHandlerQuadLightShadow: require('./BatchHandlerQuadLightShadow'), BatchHandlerTriFlat: require('./BatchHandlerTriFlat'), BatchHandlerTriFlatLight: require('./BatchHandlerTriFlatLight'), Camera: require('./Camera'), diff --git a/src/renderer/webgl/shaders/index.js b/src/renderer/webgl/shaders/index.js index 4e29a53c9..5a8d5d865 100644 --- a/src/renderer/webgl/shaders/index.js +++ b/src/renderer/webgl/shaders/index.js @@ -36,6 +36,7 @@ module.exports = { FlatVert: require('./Flat-vert.js'), FlatLightFrag: require('./FlatLight-frag.js'), LightFrag: require('./Light-frag.js'), + LightShadowFrag: require('./LightShadow-frag.js'), LinearBlendFrag: require('./LinearBlend-frag.js'), MeshFrag: require('./Mesh-frag.js'), MeshVert: require('./Mesh-vert.js'),