diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index c03b2f12ba4..022628335be 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -572,6 +572,7 @@ function draw_atomic(screen::Screen, scene::Scene, gl_attributes[:distancefield] = get_texture!(atlas) gl_attributes[:visible] = plot.visible gl_attributes[:fxaa] = get(plot, :fxaa, Observable(false)) + gl_attributes[:depthsorting] = get(plot, :depthsorting, false) cam = scene.camera # gl_attributes[:preprojection] = Observable(Mat4f(I)) gl_attributes[:preprojection] = lift(plot, space, markerspace, cam.projectionview, cam.resolution) do s, ms, pv, res diff --git a/GLMakie/src/glshaders/particles.jl b/GLMakie/src/glshaders/particles.jl index 90e3aab1575..b0066970c23 100644 --- a/GLMakie/src/glshaders/particles.jl +++ b/GLMakie/src/glshaders/particles.jl @@ -176,6 +176,20 @@ function draw_scatter(screen, (marker, position), data) rot = vec2quaternion(rot) delete!(data, :rotation) + if to_value(pop!(data, :depthsorting, false)) + data[:indices] = map( + data[:projectionview], data[:preprojection], data[:model], + position + ) do pv, pp, m, pos + T = pv * pp * m + depth_vals = map(pos) do p + p4d = T * to_ndim(Point4f, to_ndim(Point3f, p, 0f0), 1f0) + p4d[3] / p4d[4] + end + UInt32.(sortperm(depth_vals, rev = true) .- 1) + end |> indexbuffer + end + @gen_defaults! data begin shape = Cint(0) position = position => GLBuffer @@ -194,6 +208,7 @@ function draw_scatter(screen, (marker, position), data) return shape end end + @gen_defaults! data begin quad_offset = Vec2f(0) => GLBuffer intensity = nothing => GLBuffer @@ -226,6 +241,7 @@ function draw_scatter(screen, (marker, position), data) scale_primitive = true gl_primitive = GL_POINTS end + # Exception for intensity, to make it possible to handle intensity with a # different length compared to position. Intensities will be interpolated in that case data[:intensity] = intensity_convert(intensity, position) diff --git a/NEWS.md b/NEWS.md index 4470869990b..cb7aa747491 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ ## master +- Add `depthsorting` as a hidden attribute for scatter plots in GLMakie as an alternative fix for outline artifacts. [#3432](https://github.com/MakieOrg/Makie.jl/pull/3432) - Disable SDF based anti-aliasing in scatter, text and lines plots when `fxaa = true` in GLMakie. This allows removing outline artifacts at the cost of quality. [#3408](https://github.com/MakieOrg/Makie.jl/pull/3408) - DataInspector Fixes: Fixed depth order, positional labels being in transformed space and `:inspector_clear` not getting called when moving from one plot to another. [#3454](https://github.com/MakieOrg/Makie.jl/pull/3454) - Fixed bug in GLMakie where the update from a (i, j) sized GPU buffer to a (j, i) sized buffer would fail [#3456](https://github.com/MakieOrg/Makie.jl/pull/3456). diff --git a/docs/reference/plots/scatter.md b/docs/reference/plots/scatter.md index ee4be9fcdfa..b91750eb1cb 100644 --- a/docs/reference/plots/scatter.md +++ b/docs/reference/plots/scatter.md @@ -378,6 +378,7 @@ Currently there are a few ways to mitigate this problem, but they all come at a - `fxaa = true` will disable the native anti-aliasing of scatter markers and use fxaa instead. This results in less detailed markers, especially for thin markers like characters. - `transparency = true` will disable depth testing to a degree, resulting in all markers being rendered without artifacts. However with this markers always have some level of transparency - `overdraw = true` will disable depth testing entirely (read and write) for the plot, removing artifacts. This will however change the z-order of markers and allow plots rendered later to show up on top of the scatter plot +- `depthsorting = true` will sort markers by depth before rendering to fix the issue. This only works within a plot call, so when other plots are involved the issue may reappear. \begin{examplefigure}{} ```julia @@ -386,15 +387,22 @@ GLMakie.activate!() # hide ps = rand(Point3f, 500) cs = rand(500) -f = Figure(size = (600, 650)) +f = Figure(size = (900, 650)) Label(f[1, 1], "base", tellwidth = false) scatter(f[2, 1], ps, color = cs, markersize = 20, fxaa = false) Label(f[1, 2], "fxaa = true", tellwidth = false) scatter(f[2, 2], ps, color = cs, markersize = 20, fxaa = true) + Label(f[3, 1], "transparency = true", tellwidth = false) scatter(f[4, 1], ps, color = cs, markersize = 20, transparency = true) Label(f[3, 2], "overdraw = true", tellwidth = false) scatter(f[4, 2], ps, color = cs, markersize = 20, overdraw = true) + +Label(f[1, 3], "depthsorting = true", tellwidth = false) +scatter(f[2, 3], ps, color = cs, markersize = 20, depthsorting = true) +Label(f[3, 3], "depthsorting = true", tellwidth = false) +scatter(f[4, 3], ps, color = cs, markersize = 20, depthsorting = true) +mesh!(Rect3f(Point3f(0), Vec3f(0.9, 0.9, 0.9)), color = :orange) f ``` \end{examplefigure} \ No newline at end of file