mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
103.03k stars 35.41k forks source link

OutlineEffect lines not meeting at corner #19096

Open stdmn opened 4 years ago

stdmn commented 4 years ago
Description of the problem

I've been working to implement an OutlineEffect for a Collada file and have been having problems with the lines not meeting at the corners. I modified the Toon example that you created with cubes instead of spheres and am having the same problem so it doesn't seem to be my implementation: https://jsfiddle.net/4170Lv2z/1/

And here's a screenshot of my model:

Screen Shot 2020-04-09 at 3 25 51 PM

Is there a quick fix for this?

Three.js version
Browser
OS
Hardware Requirements (graphics card, VR Device, ...)
takahirox commented 4 years ago

Hi, thanks for the report. This is a known limitation. The basic idea of OutlineEffect is rendering a little bigger same model with black color.

Currently "a little bigger" is implemented by moving vertices towards the direction of normal. Then for example corners of a box can't render outline correctly.

One solution in my mind is using a little bigger scale parameter instead of moving vertices to the direction of normal. I'd like to try when I have a time.

gkjohnson commented 4 years ago

One solution in my mind is using a little bigger scale parameter instead of moving vertices to the direction of normal. I'd like to try when I have a time.

Unfortunately I think this will only have worse results and is significantly dependent on the origin of the model. Here's a quick and dirty image mockup of what might happen if if you used the scale technique but the origin was quite a bit below the model:

image

As @takahirox mentioned the corners aren't meeting because every vertex is moved along its normal so any vertices that don't have the same normal will diverge. Here are some other solutions that come to mind:

stdmn commented 4 years ago

@takahirox Understood--looking at the model that makes a lot of sense. How does your algorithm differ from the OutlinePass function? I don't have any problems with the vertices when I use this although it looks like it only recognizes the outline of the entire object, ignoring meshes that are in front of other meshes (see below).

Screen Shot 2020-04-10 at 10 44 37 AM
mrdoob commented 4 years ago

We may want to remove OutlineEffect... it's a bit of a hack and people struggle to get good results. What do you think @takahirox?

Mugen87 commented 4 years ago

At least ToonMaterial should have an outline effect:

https://threejs.org/examples/webgl_materials_variations_toon

Otherwise an important visual aspect of this material would be missing.

WestLangley commented 4 years ago

IMO, OutlineEffect works well when used in conjunction with MeshToonMaterial.

Screen Shot 2020-04-11 at 1 22 52 PM

Edge detection is good -- much better than with Sobel, for example.

takahirox commented 4 years ago

We may want to remove OutlineEffect... it's a bit of a hack and people struggle to get good results. What do you think @takahirox?

MMD needs OutlineEffect. It seems to use rendering "little bigger" same model technique rather than post-processing edge detection. The latter one doesn't render outline expectedly for some MMD models.

So I want to keep OutlineEffect. If it's struggled to get good result for non-MMD model, I'd like to suggest renaming to indicate it's primary designed for MMD like MMDOutlineEffect.

(Plus, I have a plan to simplify and clean up OutlineEffect in my mind but lack of time...)

looeee commented 4 years ago

Hi, thanks for the report. This is a known limitation. The basic idea of OutlineEffect is rendering a little bigger same model with black color.

What about rendering a little smaller instead? Or perhaps the ability to switch between inset and outset lines?

takahirox commented 4 years ago

Thanks for the comment @gkjohnson. Yeah, whether the scale approach works very depends on the origin of a model. And also it doesn't work for vertex at (0,0,0). We may need another solution.

I found that Unity outline shader has the same problem. It seems this problem is common (and the technique used in OutlineEffect is also common).

image

The problem is common so that there may be a common solution. I'll investigate more.

gkjohnson commented 4 years ago

@takahirox

The problem is common so that there may be a common solution. I'll investigate more.

Maybe a good first step would just be documenting the limitations of the effect as is. This technique will work with smooth normals, though -- you'd just have to clone the geometry and generate smooth vertex normals. In modern engines you could generate those in a geometry shader, but we don't have access that that. Maybe the are other approaches to address it, though?

This is maybe just tangentially related but the GDC talk on achieving Guilty Gear Xrds anime art style is really fascinating and they touch if only briefly on outlines. They use the normal extrusion technique and use separate vertex attributes to modulate the outline thickness, as well. The discussion on lines starts at 23:00 but the whole talk is great:

https://www.youtube.com/watch?v=yhGjCzxJV3E

@WestLangley

IMO, OutlineEffect works well when used in conjunction with MeshToonMaterial. Edge detection is good -- much better than with Sobel, for example.

What are you comparing this to out of curiosity? I haven't done any work or comparisons on this myself but I noticed that the three.js sobel edge detection example is operating on the color buffer as input as opposed to a depth or normal buffer so it's not necessarily indicative of what a toon outline could look like. Borderlands -- probably one the games best known for it's for it's toon-like style -- uses sobel edge detection for outlines. The technique is outlined (heh) here along with some other cool stuff:

https://www.youtube.com/watch?v=-cw2gXq83n8

WestLangley commented 4 years ago

@gkjohnson Very interesting... Yes, I was specifically referring to the lack of "inside edges" as described in the video. The Borderlands rendering looks much better.

takahirox commented 4 years ago

Maybe a good first step would just be documenting the limitations of the effect as is

I agree with it. I investigated the existing solutions but they are pre-processing approaches, like pre-calculating or baking smooth normal for hard edge beforehand. We may do them in OutlineEffect or script but I'm not sure if we should make it further complex. Interesting to do with geometry shader but yeah it isn't in WebGL. So documenting the limitation and alternative options sounds reasonable.

As I wrote above the technique used in OutlineEffect seems one of the common techniques to render outline. OutlineEffect and post-processing approach would have different pros and cons each. So I think having both in Three.js isn't bad. Even if user faces a problem with OutlineEffect, they can know why and alternatives in the new documentation. So I hope we keep having OutlineEffect.

cptSwing commented 4 years ago
  • Create a copy of the geometry to outline, merge all the vertices and generate new vertex normals for each vertex. This way all the normals for shared vertex positions will be the same and therefore wont diverge. However the outline thickness at any given face will depend on the angle of a face relative to its neighbors. This could probably be alleviated with an extra generated outline scale vertex attribute or something similar.

If post-processing is too heavy a solution or there are other reasons not to use it, using the above works well enough in my experience:

image

While the underlying mesh has split normals in several cases and would usually result in the "cube" effect discussed elsewhere in this thread, merging -> averaging -> pushing along vertex normal is a good way to do things. Of course this results in twice the geometry (actually a little less than exactly 2x since there are no vertex splits), there's that to consider. And I seem to remember having to convert to/from BufferGeo in order to merge vertices and recalculate normals.

sdraper69 commented 4 years ago

Hi there, My app would benefit greatly if the outline effect could be improved. Unfortunately, I personally do not have the skills to do it...however since it would benefit me a lot, is there any formal method of donating to ThreeJs in order to prioritise things?

cptSwing commented 4 years ago

What are you lacking, though?

sdraper69 commented 4 years ago

I'm not sure this is the place to discuss my shortcomings as a software developer, or my lack of time to improve such skills. This seems like quite a common request, and as mentioned would help me out. ThreeJs must have some overheads, I was simply seeing if there's a formal way of putting a bounty on issues.

WestLangley commented 4 years ago

@sdraper69 There is not. I suggest you post more specifics about your needs on the three.js forum. There you can get feedback as to the feasibility of your request, and perhaps offers to help you.

gkjohnson commented 4 years ago

There was a thread over on the three.js forum by Prisoner849 who had the idea to generalize the LDraw outline effect for any geometry. It has it's own quirks but it's another potential approach for outlining objects and seemed relevant here:

https://discourse.threejs.org/t/ldraw-like-edges/17100/18

Here's an example I put together showing the effect on a model:

image

mrdoob commented 4 years ago

@gkjohnson That looks great! Do you think it could replace the current OutlineEffect?

gkjohnson commented 4 years ago

Do you think it could replace the current OutlineEffect?

I see them as different effects with different trade offs. The MMD example wouldn't get the same outline feel, for example, with varying line weights. This one has a very consistent thickness throughout the whole model and requires geometry processing (arguably is the geometry is processed for Outline Effect we could fix the discontinuities at corners).

takahirox commented 4 years ago

@gkjohnson Do you mind if posting the screenshots of MMD and Toon material examples with your new outline effect?

I have been thinking of rewriting the current outline effect as material for simplicity and more user friendly API. The new outline effect looks nice so far and resolves the hard edge problem. So how about replacing with the new outline effect, and I create new (MMD)OutlineMaterial (as ShaderMaterial?) in example directory? Even if it sounds good, I'm glad if we can keep current outline effect as MMDOutlineEffect for keeping better MMD support until I create new outline material.

(If having (exposing) three type outline (outline pass, outline effect, and outline material) is confusing to users, I may hide new outline material in MMDLoader.)

gkjohnson commented 4 years ago

It'll be a bit before I can work on / contribute anything related to this. The OutlineEffect in place now fits better with the look of a classic "toon effect" in my opinion so I'm hesitant to get "replace" with this one but I agree it could be improved. The code and demo for the image I posted are available here and here if you want to poke a bit more.