Jump to content

Reverse Sprite Masking, I can do this in WebGL, how do I do this in PIXI?


phot
 Share

Recommended Posts

I've searched google for a bit and found that apparently this was something discussed here https://github.com/pixijs/pixi.js/issues/252 a while back in pixi.js  Despite it being simple and doable and extremely important, it was rejected from being implemented at the time.  The implementation was even shown by one of the users which was all of a few lines of code.  I would like to know how to go about doing this inverse masking approach through PIXI.js's current api, or changing code locally to make it work.  Reading through the comments it seems this was re-added as a milestone in 2015, with v3, yet I still don't see how to do this in v4?  I also searched for pieces of the code snippit:

if(renderable.mask.invertMask) {
    func = gl.EQUAL;
} else {
    func = gl.NOTEQUAL;
}
gl.stencilFunc(func,0,0xff);

and WebGLRenderGroup.js and couldn't find either in the repository.   Any help would be appreciated. 

Link to comment
Share on other sites

Well we added alpha masking, at which point this mostly becomes moot. If you can draw to canvas to create an alpha mask, you can just invert it to mask the inverse (see my comment here: https://github.com/pixijs/pixi.js/issues/252#issuecomment-97086970).

We also have an open PR that we will hopefully get into v4.2 that adds alpha masking to the canvas renderer. So you should be able to do this in WebGL renderer now, and Canvas in the near future.

Link to comment
Share on other sites

1 hour ago, xerver said:

Well we added alpha masking, at which point this mostly becomes moot. If you can draw to canvas to create an alpha mask, you can just invert it to mask the inverse (see my comment here: https://github.com/pixijs/pixi.js/issues/252#issuecomment-97086970).

We also have an open PR that we will hopefully get into v4.2 that adds alpha masking to the canvas renderer. So you should be able to do this in WebGL renderer now, and Canvas in the near future.

lets say have a texture of a circle, and transparent everywhere but the circle, but I want to make sure that when I mask another texture with the circle, only the circle is masked out, so a hole would appear in the texture/sprite. I'm confused on what you mean by alpha mask, your comment is confusing.  Any part of the texture that isn't transparent results in %alphad drawn in that area.  You suggest inverting the mask, but I see no way to do that programmically, are you implying I should take a square PNG image, and reverse where the transparency is?  That wouldn't make sense, since I would think there would be obvious masking issues since your mask would look like a box with a circle cut out of it, instead of just a circle cut out. 

Edited by phot
tested and it works as intended
Link to comment
Share on other sites

Sprites are boxes, everything is a quad. With sprite masking you mask the sprite using another texture, where that other texture's alpha channel is the mask. So parts you want to disappear when applying the mask you draw as transparent in the mask texture. If you draw a 32x32 texture, use a 32x32 mask texture.

If you want to mask out everything except a circle in the center of your sprite, draw a white circle to a canvas and use that as the mask. If you want to draw everything except a circle then fill a canvas with white, and clear a circle in the center, then use that as a mask.

Hepefully that makes sense?

Link to comment
Share on other sites

21 minutes ago, xerver said:

Sprites are boxes, everything is a quad. With sprite masking you mask the sprite using another texture, where that other texture's alpha channel is the mask. So parts you want to disappear when applying the mask you draw as transparent in the mask texture. If you draw a 32x32 texture, use a 32x32 mask texture.

If you want to mask out everything except a circle in the center of your sprite, draw a white circle to a canvas and use that as the mask. If you want to draw everything except a circle then fill a canvas with white, and clear a circle in the center, then use that as a mask.

Hepefully that makes sense?

See that was my concern, so if I clear the circle in the center, but then move the mask around or change literally any dimension of the mask it will cause other parts of the image to be clipped right? So this requires the size of the mask to be huge, but still prohibits scaling of the mask to get smaller holes and other transformations because it will inadvertently cause the target sprite to suffer masking in the wrong areas. At that point you might as well have made the texture look like the mask in the first place and not even bothered inverting it.  I just tried what you suggested and my fears were realized, we've got squares with holes in them.  

Link to comment
Share on other sites

The mask just needs to be the size of the texture it is masking. Instead of moving the mask around by transforming the sprite that is the mask, just change what you drew to the canvas. You can apply transforms to the canvas and move your hole around, then update your masking texture.

The unfortunate reality is that we don't have inverse masking (vector or alpha). If you feel like implementing it, I'd be more than happy to look at a PR! But the discussion we are having is not "whats the best way to do inverse masking" it is "how can you achieve the effect you want with the tools that pixi has right now".

Link to comment
Share on other sites

19 minutes ago, xerver said:

The mask just needs to be the size of the texture it is masking. Instead of moving the mask around by transforming the sprite that is the mask, just change what you drew to the canvas.

The unfortunate reality is that we don't have inverse masking (vector or alpha). If you feel like implementing it, I'd be more than happy to look at a PR! But the discussion we are having is not "whats the best way to do inverse masking" it is "how can you achieve the effect you want with the tools that pixi has right now".

 

Here's my problem and maybe you can tell me an alternative solution.  I need to replace primitives for use in a separate piece of code.  Apparently PIXI graphics are too slow (or that is what I was told by the person I'm writing the code for)  and I need a way to recreate circles with line size that doesn't change with scale of the circle (ie the width of the circle doesn't change with the radius).  I proposed to do this via inverse masking, where you take two of the same circle textures, and apply one as an inverse mask of the other.  If you keep the radii of the two circles with in some difference of each-other, say x, then the line width will stay x wide.  This would remove the need for circle graphics.  In my scenario these circles aren't going to have a static radii, they change over time, so the two textures would need to scale necisarily, and I would need to be able to have an arbitrary thickness of the line of the circle. 

Now your comments about having inverse masking in the first place are another story.  First I'm still confused and annoyed why this isn't already implemented when very clearly there was an extremely simple solution posted three years ago.  Not sure what happened but it appeared that the original reason for not doing so was because they wanted to have canvas compatibility, which I guess, fine, what ever still annoying, but the problem is that clearly isn't that big of an issue because several functions don't even have canvas compatibility now.

Second, I did actually ask for non PIXI.js api solutions if no PIXI.js solutions are available, and my problem is that I would have just done what the linked users solution had done, but I can't even see where the entry point for that kind of code would be within the documentation on pixijs, in fact I'm having difficulty finding where the actual code for masking is done, it looks like it is just a shader, which I'm more than willing to attempt to implement but its not clear how things are being done now, or why the functionality that previously wasn't exposed, but was there, now no longer exists (and how everything that the user referenced seems to be completely wiped from the repo).  It seems almost redundant work to submit a pull request when PIXI.js already had the capability of doing this. 

Link to comment
Share on other sites

I think I understand what you are going for, you want to stroke a circle and change the stroke width of that circle by modifying the mask.

There are a few things I want to address though:

Quote

Apparently PIXI graphics are too slow

I'd like to see where this came from. Drawing a circle then scaling it should be almost as fast as drawing a sprite. And unless you are talking about drawing thousands of objects there isn't much difference. And at that point you can use cacheAsBitmap methods to improve performance if necessary. If your drawing hundreds or thousands of circles though, performance is going to be way worse using a mask method than using PIXI.Graphics. Graphics are batched, filtered sprites are not, and neither are vector masked sprites.

Quote

First I'm still confused and annoyed why this isn't already implemented when very clearly there was an extremely simple solution posted three years ago.

Because there hasn't been a need for it, that OP's use case was solved using the method I have described. The code that other user hacked his change into doesn't exist anymore because that was three years ago. We've gone through a few major versions since then, and lots of refactoring. Since that post this is the first time I can remember someone mentioning inverse masking.

Quote

but the problem is that clearly isn't that big of an issue because several functions don't even have canvas compatibility now.

We've changed priorities in the past three years, as has the browser landscape. Canvas parity was a big goal for us back then, now not as much. Now, we try to get things to generally work in Canvas but are fine with having more advanced features working only in WebGL.

Quote

in fact I'm having difficulty finding where the actual code for masking is done

Depends on which type of masking, and which renderer. Most of the code is in the MaskManager for WebGL alpha/sprite masking and the StencilManager for the vector masking.

Quote

why the functionality that previously wasn't exposed, but was there, now no longer exists (and how everything that the user referenced seems to be completely wiped from the repo).  It seems almost redundant work to submit a pull request when PIXI.js already had the capability of doing this. 

Not sure what you are referring to, pixi has never had inverse masks nor did it expose anything that any user in the issue changed. A user hacked into their project's copy of pixi a way for them to do it by directly accessing the GL context, pixi has never had the support for inverse masking. However, you can see the lines that he patched are now located in the StencilManager here and here, where we change the stencil function used to apply the mask.

 

Even with all that, here's the catch: even if we did integrate that user's solution it wouldn't even help you right now. That code is for vector masks, which use PIXI.Graphics, which you said you can't use. Instead of implementing this feature, we implemented alpha masking which a lot more flexibility and can cover most cases (including the ones in that bug); but had we integrated that solution you linked, it wouldn't help you unless you could create the mask by drawing a circle using PIXI.Graphics.

I don't know what effect you are trying to achieve, so I can't tell you what the best method to do it is. All I know is you want to draw a circle, and the best way to do that is to use PIXI.Graphics. And by best I mean easiest API and the fastest to render. If you are trying to do some kind of "bubble" effect where there are many many circles then I would recommend drawing a circle, generating a texture from it, make a sprite per bubble and putting them in a ParticleContainer. You can then translate and scale them to make it look like different bubble sizes.

Hope this helps.

Link to comment
Share on other sites

4 minutes ago, xerver said:

Even with all that, here's the catch: even if we did integrate that user's solution it wouldn't even help you right now. That code is for vector masks, which use PIXI.Graphics, which you said you can't use. Instead of implementing this feature, we implemented alpha masking which a lot more flexibility and can cover most cases (including the ones in that bug); but had we integrated that solution you linked, it wouldn't help you unless you could create the mask by drawing a circle using PIXI.Graphics.

 

First I want to say thank you for taking your time to reply to me, it makes much more sense why this isn't in for sprites.  I'm trying to make a bubble like effect, which I've already done effectively for graphics, and it works fine, the only reason I wanted to switch is because I was told it would be faster to do sprite methods and I should avoid graphics at all costs.    The bubbles change shape (get bigger) and I tried just replacing them with sprites of bubbles, however I ran into the issue where, because the width of the lines for the bubbles scale with the image (obviously) it looks very strange at small scales and produces odd graphical artifacts.   The specific effect i'm trying to get is to scale these bubbles over time (get larger) and maintain the width of the bubble lines as they get bigger in radius (the bubble line width stays constant no matter the size).  I've implemented everything in graphics already, but if I won't get a performance benefit then that solves my problem 

Link to comment
Share on other sites

I would stick with Graphics until you actually encounter a performance problem. Then maybe you could do something like pre-rendering a sprite sheet of the different sizes to a sheet and switch textures between them to animate the effect and that would probably be "more performant". But it would also be more complex. You don't necessarily need that complexity unless you are actually encountering problems IMO. Good luck with your project!

Link to comment
Share on other sites

3 minutes ago, xerver said:

I would stick with Graphics until you actually encounter a performance problem. Then maybe you could do something like pre-rendering a sprite sheet of the different sizes to a sheet and switch textures between them to animate the effect and that would probably be "more performant". But it would also be more complex. You don't necessarily need that complexity unless you are actually encountering problems IMO. Good luck with your project!

Thanks,  Also I took a look at the repo, found where I think I found the shaders I would need to change in order to get inverse masking to work, however I found difficulty understanding what was going on, eg. there's a part in the fragment shader that states:

vec2 text = abs( vMaskCoord - 0.5 );

text = step(0.5, text);

and talks about clipping the bleeding from the mask. and if I remove the respective code, sure enough the mask seems to be duplicated on the outer edges. Where can I discuss these issues and find out what is going on with certain segments of the code which have no in line comments? (lots of the code is left completely undocumented except for JDOC headers and even then lack sufficient descriptions)

Link to comment
Share on other sites

  • 3 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...