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

Feature Suggestion: Add perspective size attenuation option to LineMaterial #16912

Merged
merged 21 commits into from
Aug 24, 2021

Conversation

gkjohnson
Copy link
Collaborator

@gkjohnson gkjohnson commented Jun 26, 2019

Adds a screenSpaceWidth uniform to LineMaterial which enables or disables a consistent screen space thickness. This is similar to the sizeAttenuation uniform on PointsMaterial.

For testing:
https://rawgit.com/gkjohnson/three.js/screen-space-lines-uniform/examples/webgl_lines_fat.html

@WestLangley

examples/js/lines/LineMaterial.js Outdated Show resolved Hide resolved
examples/jsm/lines/LineMaterial.js Outdated Show resolved Hide resolved
@WestLangley
Copy link
Collaborator

Adds a screenSpaceWidth uniform to LineMaterial which enables or disables a consistent screen space thickness.

Can you please see #11349 (comment) and explain again what you are proposing here?

@gkjohnson
Copy link
Collaborator Author

@WestLangley
I am proposing adding a perspective size attenuation option to LineMaterial, similar to the sizeAttenuation option on PointsMaterial. I see now that you decided not to add this initially but I think it would be a good feature to have. Regarding your concern:

With that feature, lines close to the camera can be as wide as the screen, and they look terrible.

It's true that zooming in the camera to the scene can cause a line to be large but that's the case with any 3d model and just the nature of perspective I feel. I think it should be up to the application developer to choose the right approach for their work and handle any problematic corner cases they may run into if any arise. For my use case I'd like the lines to feel more situated in world space by enabling perspective attenuation.

I just made a couple quick changes to enable attenuation in the shader but I'll make any changes to the implementation that you'd like to see. And as mentioned above I'd probably rename the field on the material to perspectiveAttenuation or sizeAttenuation (instead of screenSpaceWidth) to be more clear and consistent with PointsMaterial.

@gkjohnson gkjohnson changed the title Feature Suggestion: Enable toggling screen space thickness for LineMaterial Feature Suggestion: Add perspective size attenuation option to LineMaterial Jun 30, 2019
@WestLangley
Copy link
Collaborator

My feeling is that lines are used to render perspective; they are not drawn in perspective. So I am not a big fan of this idea.

But to continue the discussion, adding sizeAttenuation support means linewidth would have to be specified in world units when sizeAttenuation is true.

That means you will now have the following use cases to support:

1. PerspectiveCamera     sizeAttenuation: true     linewidth is in world units
2. PerspectiveCamera     sizeAttenuation: false    linewidth is in pixels
3. OrthographicCamera    sizeAttenuation: false    linewidth can be in pixels
4. OrthographicCamera    sizeAttenuation: false    linewidth can also be in world units

@gkjohnson
Copy link
Collaborator Author

I understand what you're saying and I definitely agree that that's a big use case but there are others that require specifying the width in world units. For comparison Unity's LineRenderer only renders lines in world units (I don't think it even has an option to render in screenspace units) and can be used for in-world effects such as rope or chain. It's also just a matter of aesthetic choice that imo should be up to the end application.

But to continue the discussion, adding sizeAttenuation support means linewidth would have to be specified in world units when sizeAttenuation is true.

I agree and I haven't tested with an orthographic camera so I'll get that done. If I put the time in to implement and test this fully with the updates we discussed will you support the change?

@WestLangley
Copy link
Collaborator

Rethinking the use cases, there is no need for a sizeAttenuation flag, as it has natural settings by default:

1. linewidth in world units    PerspectiveCamera     sizeAttenuation: automatically true
2. linewidth in world units    OrthographicCamera    sizeAttenuation: does not apply

3. linewidth in pixels         PerspectiveCamera     sizeAttenuation: automatically false
4. linewidth in pixels         OrthographicCamera    sizeAttenuation: does not apply

So you only need a worldUnits flag, which defaults to false.

I would not object as long as the shader code is modified in a clear manner -- and is correct.

@gkjohnson
Copy link
Collaborator Author

@WestLangley I've made the changes.

When LineMaterial.worldUnits is true then the lineWidth field is specified in world units. Otherwise it is specified in pixels.

This is true even with orthographic cameras because if we consider lineWidth to be specified in world units then we should expect that a line would be the same size as a 1 unit wide block if lineWidth is set to 1 regardless of render resolution. It's also much simpler to implement this way :)

I'm tempted to suggest that PointsMaterial should behave the same way but that can be discussed another day.

@WestLangley
Copy link
Collaborator

My guess is you are going to have to adhere to rendering the lines in screen=space...

Screen Shot

@gkjohnson
Copy link
Collaborator Author

Oof you're right -- thanks for the catch. I believe I've fixed it by deriving the line direction in screen space rather than world space. I think it looks much better now.

Before

image

After

image

And showing that a line with a world-units width of 1 is the same width as a unit cube:

image

At extremely skewed angles the endcaps will still have a bit of an odd look but I don't think there's much that can be done about that (Unity's LineRenderer exhibits the same behavior).

image

@WestLangley
Copy link
Collaborator

but I don't think there's much that can be done about that

Implementing a feature that clearly is not correct will lead to complaints. I think the correct math can be implemented with some additional effort.

I would then be more supportive of this feature request.

@gkjohnson
Copy link
Collaborator Author

I think the correct math can be implemented with some additional effort.

I'll put a bit more time into it and see what I can come up with -- any resources or direction you have on it would be greatly appreciated. And just so we're on the same page am I to understand that "correct" here means the line strip should look like a capsule when viewed from all angles?

Implementing a feature that clearly is not correct will lead to complaints.

As an aside would this be alleviated if proper docs were written for the Line2 example? Generally my feeling is that limitations like this are okay as long as they're declared up front and a user knows what to expect. Most graphics cheats break down at some point and I feel it's just a matter of knowing what those limitations are and how to avoid them. The transparency artifacts that appear on the line, for example, would also benefit from being explained up front a bit, too.

Thanks again!

@WestLangley
Copy link
Collaborator

"correct" here means the line strip should look like a capsule when viewed from all angles?

No, it should look like a projected 3D capsule -- at least in theory.

As an aside would this be alleviated if proper docs were written for the Line2 example?

No, it is only alleviated if the projection is correct.

@gkjohnson
Copy link
Collaborator Author

@WestLangley

I've updated the shader such that a line segment looks like a projected capsule:

line-segment

Every resource I found indicated that the aforementioned artifacts are inherent with the projected quad technique so the render approach is now a bit more complicated. When rendering with world units the quad is projected such that the the area will cover where the capsule is projected. Then the fragment shader does a ray-line distance check and discards a pixel if the ray is too far from the line. Here's the new demo:

https://rawgit.com/gkjohnson/three.js/161915d/examples/webgl_lines_fat.html

The ray-line-distance check is referenced from Paul Bourke's write up on topic, which I can add an inline reference to if you want.

Thanks for the push!

@WestLangley
Copy link
Collaborator

@gkjohnson This definitely is the correct look. Thanks for doing this.

Note that in most use cases, 99% of what is rendered are the lines themselves; the end-caps are a small part.

The lines can be rendered with two triangles. Unless there is a gotcha that I am missing, that should be doable -- and a far superior approach to computing the distance to a line for every texel.

I think is would involve a minimal change to the program, although I admit I have not tried it. Would you be willing to try? I can share some of the calculations I have made. You have convinced me that size attenuation is a nice feature.

@gkjohnson
Copy link
Collaborator Author

@WestLangley Awesome!

Yeah it's definitely more computationally intensive than the other approach. I can look into what it might take to implement what you're suggesting but as it is there are cases where the center quad has pixels discarded because they aren't intersecting the capsule. It's definitely the case that the quad could have a tighter containment around the capsule, though. Here are some pictures illustrating the what's happening -- the green and yellow quads are the endcap tris while the magenta one are the line "body" tris you're talking about:

Looking perpendicular to the line:
image

And looking at a skewed angle:
image

The magenta triangles are no longer centered along the line being drawn. Instead they are situated at the forward edge of the capsules surface to ensure the whole projected capsule area is contained. Clearly there are cases where the endcap quads aren't being used at all, though, and that could probably be improved.

Hopefully that makes sense -- I'm definitely open to hearing what kinds of changes you're imagining, as well! I'm also interested in whether or not the logic in the shader is clear otherwise I can add some more clarifying comments or try to explain it better.

@WestLangley WestLangley mentioned this pull request Aug 2, 2019
@dyarosla
Copy link

@gkjohnson first of all, AMAZING work + shader!

Is there any chance you can elaborate on the camera clipping part of the shader? Is there a way to not make it an estimate? I’ve tried to port the logic to C# + Unity and its only this part that sometimes gives me trouble in the final output on specifically lines close to the camera.

fredizzimo added a commit to fredizzimo/klipper_dash_visualizer that referenced this pull request Apr 19, 2020
@lauri-codes
Copy link

lauri-codes commented Sep 14, 2020

What is the current status of this PR?

@WestLangley
Copy link
Collaborator

@lauri-codes Try THREE.MeshLine. It supports size attenuation.

@lauri-codes
Copy link

Thanks for the prompt reply. The size attenuation in THREE.MeshLine does not work correctly with an orthographic camera. I had to use a fork at https://github.com/ryanking1809/threejs-meshline that seems to be have more functionality.

It would, however, be great if three.js would have this feature built-in.

@WestLangley
Copy link
Collaborator

It would, however, be great if three.js would have this feature built-in.

FYI, I had reasons for omitting that feature, but it is not my decision to make.

@joezappie
Copy link

Any update on this? I've looked at using MeshLine but seems like there's no real maintainer for it now. I agree with @lauri-codes that having this built in would be awesome. I work on path planning software and we currently use the normal Line but it would look a lot nicer with a thicker line and be more usable.

…form

# Conflicts:
#	examples/js/lines/LineMaterial.js
#	examples/jsm/lines/LineMaterial.js
#	examples/webgl_lines_fat.html
@gkjohnson
Copy link
Collaborator Author

I've just updated the PR from my more recent version over in this repo that adds alpha to coverage support and resolved conflicts .

A quick note on performance because if I recall one concern about this approach that may have prevented support-- I did a quick perf comparison on my lower end 2017 Surface Laptop using the GPUStatsPanel and when adjusting the perspective lines vs the pixel lines to have roughly the same visual footprint the performance seemed to be very similar between them. Both approaches took ~1.4-1.5ms GPU time per frame. I used 5px lines for screen space lines and 0.3 unit lines for the perspective lines which had a similar look on my display but would be great if someone could verify.

Regarding comments about world-space lines looking bad if the camera gets close to them I think this is up to the user to decide and usecase dependent. Many 3d models will look bad when you zoom in too far for a variety of reasons, as well. I think there are reasonable use cases that keep the camera far from the lines but benefit from perspective attenuation.

Having said that the caveats as I see them at this point are that geometry intersections won't have a round profile and of course this PR doesn't add support for raycasting against world-unit lines which has now been added for screen space lines. It would be nice if this were included but I don't have strong opinions on including this at this point. That's some my updated thinking on the topic, anyway.

@WestLangley
Copy link
Collaborator

I support whatever @gkjohnson wants to do. :-)

@mrdoob
Copy link
Owner

mrdoob commented Aug 24, 2021

Lets merge it then! 👍

@mrdoob mrdoob merged commit 38ce808 into mrdoob:dev Aug 24, 2021
@mrdoob
Copy link
Owner

mrdoob commented Aug 24, 2021

Thanks!

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

Successfully merging this pull request may close these issues.

7 participants