MakieOrg / Makie.jl

Interactive data visualizations and plotting in Julia
https://docs.makie.org/stable
MIT License
2.4k stars 308 forks source link

Edges/strokes not reliably visible #1378

Open Kevin-Mattheus-Moerman opened 3 years ago

Kevin-Mattheus-Moerman commented 3 years ago

The below example (I created with the help of @SimonDanisch) and the gif animation show that for certain view angles the edges (or strokes) become invisible.

Perhaps this is an "in front / behind" or ray tracing error? Or is the stroke width defined in a funny way with respect to the camera angle?

] add Makie@0.15.2 GLMakie@0.4.6
using GLMakie, GeometryBasics

V=Point3f[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (2, 1, 1), (2, 0, 1)]
F=QuadFace{Int}[(1, 2, 3, 4), (2, 3, 5, 6)] 

f = Figure()
ax=Axis3(f[1, 1], aspect = :data, xlabel = "x label", ylabel = "y label", zlabel = "z label", title = "Title")
poly!(GeometryBasics.Mesh(V, F), strokewidth=5,shading=true)
f

Ubuntu 20.04 Julia 1.6.3

Peek 2021-10-13 09-54

ffreyer commented 3 years ago

Perhaps this is an "in front / behind" error?

Yea, z-fighting. If you always want the lines to be visible you could set the wireframe/lines subplot to overdraw.

p = poly!(...)
p.plots[2].overdraw[] = true 

Though typically that's not what you want because you can see line far behind other objects.

Maybe we could tweak the line shader to think of lines as 3D tubes rather than 2D planes?

jkrumbiegel commented 3 years ago

Oh that would be good, wireframe over mesh is pretty bad looking right now

ffreyer commented 3 years ago

Another thing we could do is add a depth_shift acting on clip space coordinates (i.e. when the +z points out of the screen) in vertex shaders:

uniform float depth_shift;
...
gl_Position = projection * view * model * world_pos; // or equivalent
gl_Position.z += gl_Position.w * depth_shift; 
// If I understand correctly gl_Position will get divided by gl_Position.w later.
// We probably want to counter that to keep depth_shift on a normalized scale

With that you could bump a plot object to the front or back. (Just to be extra clear - this would not affect the position whatsoever. It just affects the z-order/depth of the plot.) With that added to line_segments.vert:

let
    fig = Figure()
    left = LScene(fig[1, 1])
    poly!(left, GeometryBasics.Mesh(V, F), strokewidth=5, shading=true)

    right = LScene(fig[1, 2])
    p = poly!(right, GeometryBasics.Mesh(V, F), strokewidth=5, shading=true)
    p.plots[2].plots[1].attributes[:depth_shift] = Node(-0.01f0)

    fig
end

Screenshot from 2021-10-13 14-05-25

Kevin-Mattheus-Moerman commented 3 years ago

Tubes would look good perhaps but it does sound graphically more "expensive", especially if one thinks of visualizing finite element meshes with lots of edges (my application)

I was wondering. Do the lines have an orientation for the way they are drawn? E.g. In terms of a "front" direction?

The gif below shows a comparison between Makie and MATLAB to render this scene. As you can see the lines appear rotation invariant in MATLAB. For Makie the lines seem to flip-flop in terms of thickness (or is it orientation as I hinted at?) or become hidden. If orientation is a thing perhaps render the edges in multiple orientations or rotate the draw orientation with a change in orientation to the edge front faces the viewer.

Peek 2021-10-13 13-09

Kevin-Mattheus-Moerman commented 3 years ago

@ffreyer is the z-order thing rotation invariant? Like it applies to both the viewers direction so works for the top and the bottom of the mesh? Would the Node(-0.01f0) bit work for all mesh densities and edge thickness levels or is it something to tweak?

ffreyer commented 3 years ago

The depth_shift applies after the model, view (camera) and projection matrices, so it's independent of rotation, scale and translation of the camera and the plot.

You would have to tweak depth_shift. For example if you look at your example "from above" the mesh gets closer to the front as you go left (or right depending on orientation) from the center line. How much of the line gets occluded depends on how the adjusted line depth value compared to the mesh depth value for each pixel.

let
    fig = Figure()
    left = LScene(fig[1, 1])
    p = poly!(left, GeometryBasics.Mesh(V, F), strokewidth=5, shading=true)
    p.plots[2].plots[1].attributes[:depth_shift] = Node(-0.0001f0)

    right = LScene(fig[1, 2])
    p = poly!(right, GeometryBasics.Mesh(V, F), strokewidth=5, shading=true)
    p.plots[2].plots[1].attributes[:depth_shift] = Node(-0.1f0)

    fig
end

Screenshot from 2021-10-13 14-58-43

SimonDanisch commented 3 years ago

I think the only way to do this correctly is write a new mesh shader that supports edge lines... but it's a bit complicated especially for quads etc, so I haven't tried yet... The depth shift can work, although I had some problems finding the right parameters that always looked correct... But yours looks pretty good, so maybe it works well enough to enable it as an option!

ffreyer commented 4 months ago

Might be a bit far fetched, but looking into decal buffers might give us some better ideas for this. https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.0/manual/renderer-feature-decal.html