Suggestion: Overlay.Color/DynamicSprite.Color

Started by ThreeOhFour, Fri 13/12/2024 04:22:11

Previous topic - Next topic

ThreeOhFour

The additions of overlays and blending modes means that AGS is starting to be capable of some lovely little visual effects that were either not possible or less easy to do previously. However currently the Tint function is a bit limited - perfectly fine for making characters and objects take on a bit of colour, but not that great for more intricate work.

My first thought would be to use DynamicSprite.CopyTransparencyMask but the manual warns that this is quite slow, and so I haven't even attempted to experiment with it. We've all seen that efficient use of AGS script is hardly my specialty.

What I'm proposing would be an effect that recolours all pixels of a sprite to an RGB value while leaving the alpha channel unchanged. Similar to Tint, but instead of an adjustment, a complete recoloring. Here's a simple use case to help showcase how this might be useful:



Here I've taken a character sprite and placed them behind a screen and in the angled light of windows. I want them to be seen through the screen, but I don't want any value variation on the silhouette there, so I make a copy of the ViewFrame with dynamic sprite, crop it down to the dimensions of the screen, put it on a room overlay and then recolour it. For the angled light effect, I take the same ViewFrame onto a dynamic sprite, recolor it, draw some black lines over the top of it using the DynamicSprite's drawing surface, then set this to a room overlay's sprite and set the blend mode to Add. Because additive blending mode doesn't do anything with black, only the orange recoloured parts show up, and we get a nice, dynamic angled lighting effect that reacts to the player's positioning.

If this is too specific or not easily possible, not a problem, but I figure it's worth asking! I do this kind of thing all the time in making pre-rendered VFX and it would be very nice to start to be able to do this at runtime more easily.

Crimson Wizard

#1
DynamicSprite.CopyTransparencyMask is relatively slow. The manual was first written around early-mid 2000-ies, alot changed since then, including computer power. Applying an effect to just few objects in low-res games should not lead to any noticable slowdown, and if this sprite is prepared once on room load, then there's no problem at all.

Both recoloring DynamicSprite and recoloring a sprite on render is technically possible. The latter would be very fast if done by a hardware accelerated gfx driver. I just recently experimented with this, it's couple of lines in the renderer code. The problem is mostly in how to organize this in script API and object data.

ThreeOhFour

Right, because I was thinking of doing things as a character animated & changed position, I figured I'd have to be calling it every game loop (or at least when a character moved or animated, hence my reluctance to rely on that function. But I'm willing to give it a try! Thanks for the information, although I don't know much about organising script apis and the like

Crimson Wizard

#3
I have to admit that I was not reading the description of a use case thoroughly. Now when I see that you need to display a silhouette behind the screen, I am not completely certain that this Color property is the best suitable approach for that.

Maybe there's a way to achieve this with another blend mode too. Because the idea is that one sprite is layered over another, making the latter pixels change value.

ThreeOhFour

Yes, that's fair, although the second part of my use case with the additive blending mode + colour adjustments is not something I can think of an easier way to do. (and this was just a simple example of how I might use it, I was actually trying to do some other things as well and found a workaround for them, but thought this feature would have made those easier + better to do).

Again, not something I'm pushing for too hard because I realize there are lots of things on the roadmap! Just something that I know would be useful to some of the ways I work with layering effects (effectively this becomes a substitute for a clipping mask, and I use clipping masks all day every day in doing graphics stuff).

eri0o

Thinking a bit on the clipping mask idea, I guess this would be like, you stamp a list of overlays in a buffer and then sample the alpha from this buffer and the colors from a second buffer to then write ove this second buffer which then you use it as texture to something (perhaps an overlay).

Just thinking about this in this way in my head feels like the idea of hardware accelerated drawing sprite.

But in this specific case I agree I would try using copy transparency and see if it is slow for it - my guess is the resolution is low enough and the object is small enough it would just work fine.

Crimson Wizard

#6
Quote from: eri0o on Sat 14/12/2024 00:27:53stamp a list of overlays in a buffer and then sample the alpha from this buffer and the colors from a second buffer to then write ove this second buffer which then you use it as texture to something (perhaps an overlay).

If we look in this article:
https://www.khronos.org/opengl/wiki/Blending
specifically the "Blend Equations" and "Blending Parameters" section,
we can see that there's a way to ask for certain color components from source and dest.

I.e. the quote:
QuoteWhen computing the output color, two equations are used: one for the RGB portion of the output color, and one for the alpha of the output color. This is useful if you want to do something like blend the source and dest colors but take the source alpha as the fragment output produced it.


The problem that I see with the case described above is that only character has to become a dark silhouette, but not the background image. Idk how realistic this effect is... but if the idea is to have different images behind the "screen" object to be affected differently, then this has to use a more complicated path rather than a single blend of screen object over everything that's behind...

I.e. something more like - be able to blend 2 objects together using one operation, and then blend the result to the rest of the room using another operation.

So... yes, if that is a wanted effect, then this has to be some kind of composite dynamic sprite.

eri0o

@Crimson Wizard when you were looking into cameras rotation I think, I remember you saying something about stencil, I don't remember what or exactly the details, but are those like something for a clipping mask?

ThreeOhFour

This might be a better breakdown of how I envision using this kind of feature step by step, in case that would help the discussion!



I should clarify - this isn't the game I'm working on, just some old assets I had laying around to demonstrate a couple of use cases! So rather than trying to solve this specific problem, I'm trying to support my proposal by showing how it could be used! Sorry if I wasn't clear about this.

Crimson Wizard

Quote from: eri0o on Sat 14/12/2024 01:27:45@Crimson Wizard when you were looking into cameras rotation I think, I remember you saying something about stencil, I don't remember what or exactly the details, but are those like something for a clipping mask?

Hmm, was not it GUI clipping?
I don't know if that's the only use for stencil, but at least one use is a clip mask of arbitrary shape.
The first time I checked stencils was when a user asked for a faster equivalent of CopyTransparencyMask, something that would carve a non-rectangular shape from an object.

eri0o

#10
Uhm, perhaps it was, I searched for PRs with the word stencil but didn't found anything relevant, so don't remember.

I guess the issue is much higher in the design of the API. Doing DynamicSprites as hardware accelerated is hard - at least in my head there are things I am unsure - but a different API, I think very few commands like in my imgi module would suffice (note it uses Dynamic Sprites, but I was thinking in an abstract world)

https://github.com/ericoporto/ImGi/blob/d5aec7a977adf16e23fa5b3518e083ecef29a7b9/imgi_demo/imgi.asc#L2766

From this, I think only the draw sprite and the stencil would be actually hardware accelerated and the rest would be pushed through the draw sprite itself with an intermediary bitmap drawing. Can't tell if this would actually make things faster.

ThreeOhFour

The topic where you were discussing masks/stencils might be this one?

Crimson Wizard

#12
Quote from: ThreeOhFour on Sat 14/12/2024 11:34:35The topic where you were discussing masks/stencils might be this one?

That was the person whose case caused my interest in stencils, but the discussion contains only the DynamicSprite solution. We talked on AGS Discord server afterwards, and I made a test build of the engine, with hardcoded stencil over some sprite. It was too ugly to make any PRs.

Technically the idea was that
- you need to enable stencil buffer (renderer must have something configured in them);
- user needs a way to tell "use this sprite as a stencil mask" and "use it with this object/sprite";
- renderer creates a stencil mask from one sprite and then later applies it when drawing another.
- stencil mask itself is just another texture. I think it should have low bitness (1 - 8 bit) to work correctly? don't remember this part by heart.
BTW this was not supported back then, but in latest ags4 you can demand to import/load certain image as a sprite while keeping it low-bit
(engine converts sprites to the game's depth by default).
- naturally, since we support Software renderer, the API should be suitable to also apply this mask in software mode.

SMF spam blocked by CleanTalk