Makie.jl icon indicating copy to clipboard operation
Makie.jl copied to clipboard

Add clipping options to GLMakie

Open ffreyer opened this issue 1 year ago • 6 comments

Description

This adds the option to specify a world space clipping box which excludes anything outside it from rendering.

f, a, p = mesh(
    Sphere(Point3f(0), 1.2f0), color = :yellow, 
    clip_planes = Rect3f(Vec3f(-1), Vec3f(2))
)

Screenshot from 2023-03-17 23-05-33

Currently this is a per-plot attribute that defaults to what the scene defines. Can be nothing, a Rect3 or WorldAxisLimits.

Working so far

  • [x] mesh
  • [x] meshscatter
  • [x] scatter, text
  • [x] volume
  • [x] surface
  • [x] heatmap, image
  • [x] lines, linesegments

lines, linesegements, scatter and text may need to work differently since they transform to along the way.

Type of change

  • [x] New feature (non-breaking change which adds functionality)

Checklist

  • [ ] Added an entry in NEWS.md (for new features and breaking changes)
  • [ ] Added or changed relevant sections in the documentation
  • [ ] Added unit tests for new algorithms, conversion methods, etc.
  • [ ] Added reference image tests for new plotting functions, recipes, visual options, etc.

ffreyer avatar Mar 17 '23 22:03 ffreyer

Compile Times benchmark

Note, that these numbers may fluctuate on the CI servers, so take them with a grain of salt. All benchmark results are based on the mean time and negative percent mean faster than the base branch. Note, that GLMakie + WGLMakie run on an emulated GPU, so the runtime benchmark is much slower. Results are from running:

using_time = @ctime using Backend
# Compile time
create_time = @ctime fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @ctime Makie.colorbuffer(display(fig))
# Runtime
create_time = @benchmark fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @benchmark Makie.colorbuffer(display(fig))
using create display create display
GLMakie 51.93s (51.20, 53.89) 0.95+- 23.61s (23.00, 24.38) 0.49+- 22.06s (21.24, 23.05) 0.67+- 17.71ms (17.25, 18.15) 0.32+- 197.88ms (194.11, 208.37) 4.99+-
master 52.07s (51.23, 53.91) 0.88+- 23.56s (23.15, 24.10) 0.34+- 21.77s (21.30, 22.32) 0.39+- 17.59ms (17.45, 17.72) 0.11+- 195.59ms (187.37, 203.53) 5.03+-
evaluation -0.27%, -0.14s invariant (-0.15d, 0.78p, 0.91std) +0.22%, 0.05s invariant (0.12d, 0.82p, 0.42std) +1.30%, 0.29s invariant (0.52d, 0.36p, 0.53std) +0.72%, 0.13ms invariant (0.53d, 0.35p, 0.22std) +1.16%, 2.29ms invariant (0.46d, 0.41p, 5.01std)
CairoMakie 35.38s (35.17, 35.63) 0.18+- 17.56s (17.23, 18.11) 0.30+- 2.56s (2.51, 2.68) 0.06+- 11.47ms (11.24, 11.65) 0.17+- 5.57ms (5.33, 5.92) 0.23+-
master 35.27s (35.13, 35.45) 0.13+- 17.49s (17.35, 17.77) 0.16+- 2.57s (2.52, 2.71) 0.07+- 11.41ms (11.11, 11.93) 0.26+- 5.81ms (5.55, 6.16) 0.19+-
evaluation +0.29%, 0.1s invariant (0.65d, 0.25p, 0.16std) +0.38%, 0.07s invariant (0.27d, 0.62p, 0.23std) -0.47%, -0.01s invariant (-0.20d, 0.72p, 0.06std) +0.49%, 0.06ms invariant (0.26d, 0.64p, 0.21std) -4.22%, -0.23ms invariant (-1.12d, 0.06p, 0.21std)
WGLMakie 43.50s (43.26, 43.92) 0.24+- 21.52s (21.20, 21.69) 0.19+- 21.77s (21.41, 22.28) 0.30+- 13.66ms (13.25, 14.08) 0.35+- 769.96ms (729.65, 830.57) 37.01+-
master 43.70s (43.49, 44.13) 0.23+- 21.64s (21.43, 21.96) 0.18+- 21.85s (21.43, 22.45) 0.34+- 13.71ms (13.06, 14.08) 0.41+- 751.34ms (731.07, 771.44) 14.11+-
evaluation -0.47%, -0.2s invariant (-0.87d, 0.13p, 0.23std) -0.55%, -0.12s invariant (-0.65d, 0.25p, 0.18std) -0.36%, -0.08s invariant (-0.25d, 0.65p, 0.32std) -0.33%, -0.05ms invariant (-0.12d, 0.83p, 0.38std) +2.42%, 18.62ms invariant (0.66d, 0.25p, 25.56std)

MakieBot avatar Mar 17 '23 22:03 MakieBot

I tried two options for lines - one uploading two buffers, one with world space positions and one with screen space positions; and one that only uploads world space positions, i.e. lets the screen space positions calculated for last_len go to waste. In the end they both perform about equal for me, but the latter saves some memory which I assume is valuable for some. It's also a bit less code, so that's what I went with.

ffreyer avatar Mar 19 '23 23:03 ffreyer

https://stackoverflow.com/questions/22628186/glclipplane-is-there-an-equivalent-in-webgl I guess for WGLMakie we could use the three.js implementation for clipping planes?

ffreyer avatar Mar 20 '23 14:03 ffreyer

we also need polygons for clipping, for example for nonlinear axes. is that something that would fit the scope of this pr or not at all?

jkrumbiegel avatar May 05 '23 15:05 jkrumbiegel

Short summary of how this works: Each vertex has a ClipDistance which can hold some number of float distances. These values each get interpolated when going to the fragment shader, and if any of them fall below 0 the fragment will be discarded. For a plane you can get inside vs outside by doing something like distance = plane_distance_from_zero - dot(plane_normal, position).

If you have a complex polygon or mesh to check against you'll need lots of clip planes, and you can only pass so many ClipDistances. (Minimum 8) So you'll probably run into issues quickly like this. A circle would be very easy to do though, since that's just one distance.

If the clipping shapes are 2D you can also go through stencil buffers. Conceptually it shouldn't be too difficult to render an arbitrary poylgon to the stencil buffer and then use that as a mask for clipping. In practice our renderloop needs to be more adjustable for this, I think.

ffreyer avatar May 05 '23 15:05 ffreyer

If the clipping shapes are 2D you can also go through stencil buffers. Conceptually it shouldn't be too difficult to render an arbitrary poylgon to the stencil buffer and then use that as a mask for clipping. In practice our renderloop needs to be more adjustable for this, I think.

Yes this would have to be the mechanism for weird axis shapes, I think.

jkrumbiegel avatar May 05 '23 16:05 jkrumbiegel

Closing this in favor of #3958

ffreyer avatar Jun 13 '24 13:06 ffreyer