StephanieP Posted October 28, 2018 Share Posted October 28, 2018 Hello everyone, I’m quite new to PixiJS but I love it so far! However, I just came across a problem that I find surprisingly confusing and I’m not quite sure which option would be the most effective. I have two stages, a background stage and a front stage, both of which are `PIXI.Container()`. The background stage has a transparent background but is filled with multiple elements (Sprites and Graphics) that are animated. The front stage only has a title in the form of a `PIXI.Text()`. I want that title to be revealed by the multiple elements from the background stage, meaning it should only be visible when there is an element from the background stage passing behind it. Now, masking in PIXI seems to only work with `PIXI.Graphics()` and `PIXI.Sprite()`. My background stage isn’t a Graphics, and I can’t use `PIXI.Texture.fromCanvas()` because my background stage is in the same canvas. Also, it would probably be really costly to generate a texture every frame of the animation to update the mask. Is there any clever trick that I can’t yet think of right now? What would be the most elegant way to do this, performance-wise? It feels like there should be, but again, I’m new and I can’t quite figure it out yet. Thank you! ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted October 28, 2018 Share Posted October 28, 2018 Hi! Its possible if you use SpriteMaskFilter directly and pass there a sprite with texture from "pixi-display" plugin feature called "getRenderTexture()" . https://github.com/pixijs/pixi-display That requires understanding of both pixi-display examples and SpriteMaskFilter direct usage. Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted October 28, 2018 Share Posted October 28, 2018 Also check if you can use MULTIPLY blendMode instead: https://pixijs.io/examples/#/layers/lighting.js . That should be easier. StephanieP 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted October 28, 2018 Share Posted October 28, 2018 Congratulations with first post! Quote Link to comment Share on other sites More sharing options...
StephanieP Posted October 28, 2018 Author Share Posted October 28, 2018 Quote Congratulations with first post! Thank you! ? And thanks also for the quick reply! I’ll take a look at Pixi Display, it seems really interesting! I continued fiddling around and indeed stumbled the Multiply blend mode trick, which works quite well in my case! However, I realized something that probably changes a lot of things when it comes to my understanding of the issue: in my very specific case, the fact that the basic masking relies on the pixel color to work is quite annoying. I actually don’t need that. Instead, the front stage should be fully visible at the areas where there is an element from the background stage, and fully invisible where there is nothing, regardless of the color of the element that is passing behind it. So, for instance, if there is a black square or a white square behind a letter from the title, that letter should be revealed in both cases. Which means there’s really only the alpha channel that should matter. In that case, I’m really not sure which technique to use, or even if there are any. ? Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted October 28, 2018 Share Posted October 28, 2018 Make your own filter based on SpriteMaskFilter, though you dont need those coords tricks with matrix because you are using fullScreen filters (container.filterArea=app.screen), it should map two layers 1:1. Your case requires some time spent in those examples, modifying them, experimenting. Or you can wait for someone else to make a demo like yours, that will take much more time. Also dont forget that colors are premultiplied everywhere StephanieP 1 Quote Link to comment Share on other sites More sharing options...
StephanieP Posted October 29, 2018 Author Share Posted October 29, 2018 I followed your advice and went with a custom PIXI.Filter. I wrote a very small shader that receives a generated texture from the background stage that I use to determine whether the title should be visible or not : precision mediump float; varying vec2 vTextureCoord; uniform vec2 res; uniform sampler2D uSampler; uniform sampler2D maskTexture; void main() { vec4 texture = texture2D(uSampler, vTextureCoord); vec4 mask = texture2D(maskTexture, vTextureCoord); gl_FragColor = vec4(texture.r, texture.g, texture.b, mask.a * texture.a); } I generated the maskTexture with a PIXI.RenderTexture: const maskTexture = PIXI.RenderTexture.create(app.screen.width, app.screen.height) const filter = new PIXI.Filter(null, shader) filter.uniforms.maskTexture = maskTexture frontStage.filters = [filter] app.ticker.add(() => { app.renderer.render(backgroundStage, maskTexture, true, null, false) }) It works really well, but... Oddly enough, it seems like other DisplayObjects from containers outside the backgroundStage are included in the texture that is passed to the shader, which obviously causes the mask not to be what I want. What I find weird is that, if I create a PIXI.Sprite from the generated texture and add it the stage, that sprite only includes the DisplayObjects from the backgroundStage. So... const renderSprite = new PIXI.Sprite(maskTexture) app.stage.addChild(renderSprite) ... will only display the elements from the backgroungStage, which is what I expect. Why then does my shader pick up elements from containers outside the backgroundStage? What am I missing? ? Thanks again! Quote Link to comment Share on other sites More sharing options...
StephanieP Posted October 29, 2018 Author Share Posted October 29, 2018 I made a fiddle to better illustrate my issue. Hopefully it’ll be clearer that way. ? https://jsfiddle.net/bsath8Le/4/ jonforum 1 Quote Link to comment Share on other sites More sharing options...
StephanieP Posted October 30, 2018 Author Share Posted October 30, 2018 So, I realized ivan.popelyshev actually answered my question before I had even asked it: Quote Also dont forget that colors are premultiplied everywhere I think I didn’t realize what it truly meant when I first read it. But now I’ve updated my fiddle to reflect that. I tweaked the shader to take into account that premultiplication. Visually, it now works! Is there anything else I could improve in the way I built that scene? Thanks again, Ivan! Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted October 30, 2018 Share Posted October 30, 2018 51 minutes ago, StephanieP said: So, I realized ivan.popelyshev actually answered my question before I had even asked it: I think I didn’t realize what it truly meant when I first read it. But now I’ve updated my fiddle to reflect that. I tweaked the shader to take into account that premultiplication. Visually, it now works! Is there anything else I could improve in the way I built that scene? Thanks again, Ivan! float alpha = mask.a * texture.a; vec3 rgb = texture.rgb * alpha; texture.rgb already multiplied just "gl_FragColor = texture * mask.a" should work. Otherwise, colors on non-opaque parts of texture will be changed. Btw, that's a bug in SpriteMaskFilter, i know about it StephanieP and jonforum 1 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.