Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clipping mask problem with Canvas renderer #494

Closed
juxtapus opened this issue Dec 10, 2020 · 12 comments · Fixed by #498
Closed

Clipping mask problem with Canvas renderer #494

juxtapus opened this issue Dec 10, 2020 · 12 comments · Fixed by #498
Labels

Comments

@juxtapus
Copy link

I am considering switching from WebGL to either Canvas or SVG renderer but I noticed that there are some unexpected differences. I was initially using WebGL, but found that as I added more elements to the screen, the frame rate would suffer noticeably. Here is a link to a video that shows this, followed by a second link that shows the clipping mask problem after I switch to a Canvas renderer.

https://www.dropbox.com/s/y7qf5wxhk9xz05z/two_js%20with%20webgl.mp4?dl=0

https://www.dropbox.com/s/gt705c60f2t1sss/two_js%20with%20canvas.mp4?dl=0

Is this a known issue? And if so, is there a list of known issues or differences between the renderers? I mean, I am aware that semi-transparencies are only available in the SVG renderer for example. It would be good if these differences were a part of the documentation, otherwise choosing a renderer seems to be an exercise in trial and error

@juxtapus
Copy link
Author

By the way, SVG renderer is not viable for me because it does not seem to be capable of drawing radial gradients. I need radial gradients to create the corners of my drop shadows.

@adroitwhiz
Copy link
Contributor

Are you comfortable sharing the source code or creating a more minimal reproduction case? I can try to dig into this.

@juxtapus
Copy link
Author

juxtapus commented Dec 10, 2020

ProblemCode.zip

Thanks. I've attached a minimal reproduction case.
If you change it between webgl and canvas you will find the issue that I described, plus, something I discovered as I made this minimal file right now. To get this to work, I was forced to put all the elements into a group called "mainGroup". Otherwise the clipping problem is even worse.

Right now, the file has the following from lines 98 to 103:

        mainGroup.translation.set(mainXYWH.x, mainXYWH.y);
        mainGroup.add(layerGroup);
        mainGroup.add(mask);
        mainGroup.add(border);

        two.add(mainGroup);

But if you add these directly to "two" instead of to mainGroup, then the red square disappears (with the canvas renderer). Like this:

        two.add(layerGroup);
        two.add(mask);
        two.add(border);

@adroitwhiz
Copy link
Contributor

The issue appears to be that in the SVG and canvas renderers, transforming a group also transforms its mask, even though the mask is not a child of the group. This seems to have been the case since masks were first implemented. @jonobr1, is this the intended behavior for masks?

@juxtapus
Copy link
Author

Sounds like you've identified the issue. But just to be sure that it is addressed fully I have made a small update to the reproduction code to be 100% that I've fully reproduced the problem. I've added a green square to the masked area that should pan into view after the arrow is clicked, but fails to do so in svg/canvas renderers.

ProblemCode2.zip

@adroitwhiz
Copy link
Contributor

There are a couple other issues you mentioned:

I am aware that semi-transparencies are only available in the SVG renderer

SVG renderer is not viable for me because it does not seem to be capable of drawing radial gradients

Can you provide test cases for these as well?

@juxtapus
Copy link
Author

juxtapus commented Dec 10, 2020

No worries. I have updated the code to include these things. I have added 3 shapes to the right-hand side of the screen: two with gradients, and one with a normal fill. The gradients have one stop with an opacity of 0.5, and when using the SVG renderer this causes the gradient in the large circle to be semi-transparent. You can see that it is semi-transparent because it allows the blue square behind it to be visible. But the other renderers do not allow it to be semi-transparent: the blue square is hidden.

However, the second problem (with radial gradients) only occurs when the circle with the radial gradient is in the same group as a rectangle with a linear gradient. When in SVG rendering mode, the every circle with a radial gradient will disappear completely if a rectangle with a linear gradient is added. Remove the rectangle gradient from the group to bring the circle back.
ProblemCode3.zip

@jonobr1
Copy link
Owner

jonobr1 commented Dec 10, 2020

Thanks for posting these issues @juxtapus and for following up @adroitwhiz. Re: masks, I'll confirm today which one is the intended behavior.

I love the idea of having a matrix on the documentation to outline the known differences between renderers.

Yes, SVG is the only renderer that can do semitransparent gradients, however, you can get around this by using this css directive when setting the stop's color:

stop.color = 'rgba(255, 50, 50, 0.5)';

@jonobr1
Copy link
Owner

jonobr1 commented Dec 10, 2020

First response, I'll respond to the clipping mask in another issue. The problem is actually not with radial gradients, but how SVG is its own node scenegraph / hierarchy. You're using the same Two.Stop instantiations on both the linear gradient and radial gradient. In SVG, the same node can't be in two places so it defaults to the latter one (in your code the linear gradient). If you clone the stops then the radial gradient shows up.

To be honest, I think Two.js should handle this so that it is consistent between renderers.., but I'm not sure the best way to deal with that yet.

@jonobr1
Copy link
Owner

jonobr1 commented Dec 10, 2020

After some playing around, the intended behavior resides with SVG and Canvas renderers. In @juxtapus's example, you wouldn't actually set the mask to layerGroup, but to mainGroup because layerGroup is doing all the moving. The WebGL implementation of masks was implemented either this year or last year, so it was supposed to be consistent with that. But, not surprisingly the WebGL renderer is using stencil buffers to achieve the masking and so it's way more generic in how it can be used than in SVG and Canvas.

I need to do a bit more poking around, but I'm also open to new ideas of implementation if you anyone feels strongly. For me, I think it's important for the transformations to be consistent, not only between renderers, but between also between masks, gradients, and images / texture because they're all treated as objects in the scenegraph without actually being traditionally added to a parent via an add method.

@juxtapus
Copy link
Author

In @juxtapus's example, you wouldn't actually set the mask to layerGroup, but to mainGroup because layerGroup is doing all the moving.

Ok thanks. I deleted my recent comment because I made an error. This way of doing things does make sense. But I think perhaps there should be a lengthier explanation (or an example) in the documentation. Its an easy mistake to make.

@jonobr1
Copy link
Owner

jonobr1 commented Dec 11, 2020

Yeah, thanks for pointing it out. I agree that the transformations with masks need more work and clarity. Glad you were able to get things working!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants