Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/examples/src/examples/sprite/ExampleSprite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ class Showcase extends Stage {
// two effects: drawn apart and composited, at the opacity it
// fades to like any other sprite
minion.addPostEffect(
new OutlineEffect(app.renderer, { color: [1, 0.9, 0.85], width: 2 }),
new OutlineEffect(app.renderer, { color: [1, 0.9, 0.85], width: 15 }),
);
minion.addPostEffect(
new GlowEffect(app.renderer, {
color: [1.0, 0.5, 0.4],
width: 8,
width: 60,
intensity: 1.5,
}),
);
Expand Down Expand Up @@ -162,7 +162,7 @@ class Showcase extends Stage {
width: 9,
intensity: 3,
});
const outline = new OutlineEffect(r, { color: [1, 1, 1], width: 5 });
const outline = new OutlineEffect(r, { color: [1, 1, 1], width: 9 });
const shine = new ShineEffect(r, {
color: [1.0, 0.95, 0.75],
speed: 0.6,
Expand Down
3 changes: 3 additions & 0 deletions packages/melonjs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
- State: a `state.change()` asked for while a transition covers the screen is made by that transition, to the last state asked for and with its arguments, as without a transition ([#1787](https://github.com/melonjs/melonJS/issues/1787)). It put up a second cover, and the first stayed on the camera for good, the screen black over a game still running; a change to a state with no transition of its own is still made at once, and takes the cover down
- Effects: the built-in effects that place a pattern place it on the sprite itself, cut from an atlas or with other effects after it, and `SwayEffect` measures its strength across the sprite ([#1407](https://github.com/melonjs/melonJS/issues/1407)). On an atlas sprite, `SwayEffect` and an anchored `WaveEffect` moved its roots and `VignetteEffect`, `RadialGradientEffect` and `ScanlineEffect`'s curve landed on the atlas; with two or more effects, every pattern, `noise_uv` and `ShineEffect`'s sweep included, sat on the canvas and slid with the camera
- Effects: a renderable drawn through two or more effects keeps its opacity, its own and its containers', on WebGL and WebGPU, as does a `UIPanel`, `ProgressBar` or `Trail` with one ([#1791](https://github.com/melonjs/melonJS/issues/1791)). Their effects were composited at full opacity, so `setOpacity()` or a fade changed nothing while the effects were on
- Effects: through two or more effects (or one, on a `UIPanel`, `ProgressBar` or `Trail`), a glow, outline, blur, drop shadow, chromatic aberration, pixelate or bloom measures its width, offset or size in the renderable's own pixels, as it does alone, at any scale and under a camera's zoom. It measured in the screen's pixels there, so adding a second effect made an outline on a sprite drawn at 0.13 about 8 times thicker; a game that tuned such widths on a scaled renderable should check them, and an effect given a `textureSize` keeps it
- Effects: a renderable drawn through two or more effects (or one, on a `UIPanel`, `ProgressBar` or `Trail`) stays inside the clip it is drawn under, such as a minimap or split-screen camera's view or a clipping container. Its effects were composited over the whole canvas, so a minimap's sprites with effects spilled over the main view
- Effects: on WebGPU, an effect reads what it captured (two or more effects on a renderable, or any on a camera) at the game's texture filter, as on WebGL, so under `"nearest"` (or `antiAlias` off) a width or an offset that falls between two pixels stays crisp. WebGPU always blended there

## [20.11.0] (melonJS 2) - _2026-10-27_

Expand Down
17 changes: 15 additions & 2 deletions packages/melonjs/skills/melonjs-effects-and-shaders/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,28 @@ sprite.
**Their widths, offsets and sizes are in pixels of the texture they draw, with
nothing to pass.** Each of those effects takes the size of what it draws: the
sprite's whole texture (the atlas image, not the frame), the screen for a
camera's effect, a mesh's texture. Do not pass `textureSize` to get this: a
camera's effect, a mesh's texture. With other effects after it, it still
measures in the sprite's own pixels, at any scale and under a camera's zoom
(on a container, in the container's own), so adding a second effect never
changes how wide the first comes out. Do not pass `textureSize` to get this: a
`textureSize` (or `setTextureSize()`) fixes the size instead, for a width
meant in some other texture's pixels.

```js
// 2 pixels of the sprite's art, whatever the size of its atlas
// 2 pixels of the sprite's art, whatever the size of its atlas, its scale,
// the camera's zoom, or the effects after it
sprite.addPostEffect(new OutlineEffect(app.renderer, { width: 2 }));
sprite.addPostEffect(new GlowEffect(app.renderer, { width: 6 }));
```

With two or more effects, the sprite is drawn into a capture and each effect
reads that, at the game's texture filter, as a sprite reads its own texture:
under `"nearest"` (or `antiAlias` off) a width that lands between two screen
pixels, such as 2 pixels of a sprite drawn at 0.3, stays crisp, and under
`"linear"` it blends, on WebGL and WebGPU alike. The result lands inside the
clip the sprite is drawn under, a minimap camera's view or a clipping
container, as with one effect.

`SwayEffect` leans a sprite or a layer from side to side, as if in a breeze: the
top sways most and everything from its `anchor` row down stays put, so a tree's
trunk or a reed's root stays planted. It suits anything planted at its bottom;
Expand Down
5 changes: 3 additions & 2 deletions packages/melonjs/src/video/effects/bloom.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ ${tapList("vec2f", (at) => {
* sprite's silhouette and does nothing at all on an opaque frame.
*
* Its reach is in texels of what it filters: the screen for a camera's
* effect, the sprite's texture on a sprite, so it follows a resize by itself.
* effect, the sprite's texture on a sprite (alone or with other effects, at
* any scale or zoom), so it follows a resize by itself.
* @category Effects
* @see {@link Renderable#addPostEffect} for usage
* @example
Expand All @@ -158,7 +159,7 @@ export default class BloomEffect extends ShaderEffect {
* @param {number} [options.threshold=0.75] - luminance a pixel must pass before it blooms, `0` (everything blooms) to `1` (only white does)
* @param {number} [options.intensity=1.0] - how much of the gathered light is added back
* @param {number} [options.radius=2.0] - halo reach, in texels
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its reach is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its reach is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, { glsl: glslFragment, wgsl: wgslFragment });
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/effects/blur.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default class BlurEffect extends ShaderEffect {
* @param {Renderer} renderer - the current renderer instance
* @param {object} [options] - effect options
* @param {number} [options.strength=1.0] - blur strength in texels
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its strength is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its strength is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, {
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/effects/chromaticAberration.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default class ChromaticAberrationEffect extends ShaderEffect {
* @param {Renderer} renderer - the current renderer instance
* @param {object} [options] - effect options
* @param {number} [options.offset=3.0] - channel offset in texels
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its offset is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its offset is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, {
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/effects/dropShadow.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default class DropShadowEffect extends ShaderEffect {
* @param {number} [options.offsetY=2.0] - shadow y offset in texels
* @param {number[]} [options.color=[0.0, 0.0, 0.0]] - shadow color as [r, g, b] or [r, g, b, a] (0.0–1.0); the alpha scales `opacity`
* @param {number} [options.opacity=0.5] - shadow opacity (0.0–1.0)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its offset is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its offset is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, {
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/effects/glow.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export default class GlowEffect extends ShaderEffect {
* @param {number[]} [options.color=[1.0, 1.0, 1.0]] - glow color as [r, g, b] or [r, g, b, a] (0.0–1.0); the alpha is the halo's opacity
* @param {number} [options.width=3.0] - glow spread in pixels
* @param {number} [options.intensity=1.0] - glow brightness multiplier
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its spread is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its spread is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, {
Expand Down
5 changes: 3 additions & 2 deletions packages/melonjs/src/video/effects/local_space.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
* renderable even when it is drawn in several quads. `ME_art` stays `uv`,
* and `ME_to_uv` turns a local displacement into one of `uv`.
* - 2, a pass of a renderable's effect chain: `ME_local` the same way, over
* the capture, and `ME_art` the renderable's frame in its texture
* (`ME_art_rect`, origin and size) at that point. `noise_uv` is
* the capture (whose pixels the camera's projection, its zoom included,
* put the renderable on), and `ME_art` the renderable's frame in its
* texture (`ME_art_rect`, origin and size) at that point. `noise_uv` is
* `ME_local` too, so it runs across the renderable as it does when the
* renderable is drawn with its one effect.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/effects/outline.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default class OutlineEffect extends ShaderEffect {
* @param {object} [options] - effect options
* @param {number[]} [options.color=[1.0, 1.0, 1.0]] - outline color as [r, g, b] or [r, g, b, a] (0.0–1.0); the alpha is the outline's opacity
* @param {number} [options.width=1.0] - outline width in pixels
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its width is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its width is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, {
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/effects/pixelate.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default class PixelateEffect extends ShaderEffect {
* @param {Renderer} renderer - the current renderer instance
* @param {object} [options] - effect options
* @param {number} [options.size=4.0] - pixel size in texels (higher = more pixelated)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its pixel size is measured in; by default the texture it draws (a sprite's own, the screen for a camera's effect)
* @param {number[]} [options.textureSize] - the size, `[width, height]`, of the texture its pixel size is measured in; by default the texture it draws: a sprite's own, alone or with other effects and at any scale or zoom, the screen for a camera's effect
*/
constructor(renderer, options = {}) {
super(renderer, {
Expand Down
12 changes: 10 additions & 2 deletions packages/melonjs/src/video/effects/shadereffect.js
Original file line number Diff line number Diff line change
Expand Up @@ -778,15 +778,23 @@ export default class ShaderEffect {
/**
* For a pass over a whole texture (a render target blit), which draws
* its quad with no frame: `frameTexel()` then samples as it always did,
* clamped by the texture's own wrap mode.
* clamped by the texture's own wrap mode. An effect measuring in texels
* (see `_initTextureSize`) measures in the texture's pixels, or, on a
* pass of a renderable's chain, in the renderable's own (a sprite's
* texels), as it does on the renderable drawn with it alone.
* @param {number} [width] - the texture's width, in pixels
* @param {number} [height] - the texture's height, in pixels
* @param {number[]|null} [scale=null] - on a pass of a renderable's chain, how many of the texture's pixels each pixel of the renderable covers, across and down (see `Renderer#_effectScaleOf`)
* @ignore
* @internal
*/
_clearFrameRect(width, height) {
_clearFrameRect(width, height, scale = null) {
this._writeFrameHalf(0, 0);
if (width !== undefined && height !== undefined) {
if (scale !== null) {
width /= scale[0];
height /= scale[1];
}
this._setSourceSize(width, height);
}
}
Expand Down
Loading
Loading