Files
cgal/GraphicsView/include
Sebastien Loriot dfac790cff Basic_viewer: anti-aliased edges, filled tube joins, and no per-vertex inverse(u_Mvp) (#9528)
## Description

### Summary of Changes

Improves the edge and vertex rendering of the Basic_viewer, following
the GSoC "Shaders for Basic Viewer" project idea. It rewrites the
wide-edge geometry shader to be more robust, draws the flat edges opaque
(anti-aliased by multisampling), fills the tube-edge joins with spheres
drawn from the edges, makes the on-screen sizing scale-independent,
correct on HiDPI displays, and consistent between the flat and cylinder
modes, fixes the Shift and minus size key, and removes a now-unused
uniform.

### What this changes

**Edge geometry shader**

- Builds the wide-edge quad in clip space instead of using a per-vertex
`inverse(u_Mvp)`. Since NDC = clip.xy / clip.w, a screen-space offset
becomes offset * w in clip space, so the quad vertices are emitted
directly with the correct depth and no matrix inverse. This is
numerically more stable and a little cheaper per edge.
- Removes the dangling `g_Color[]` input.

**Flat edges (opaque, anti-aliased by multisampling)**

- The flat edges are drawn opaque, and 4x framebuffer multisampling
smooths the silhouette, so they stay fully solid at any width and a thin
edge no longer fades toward the background. The earlier version blended
an analytical coverage alpha in the fragment shader, which left a faint
one-pixel border that read as slightly transparent; multisampling does
the anti-aliasing instead and keeps the edge solid.
- The geometry shader emits the exact square-capped edge rectangle, and
the minimum one-pixel half-width clamp keeps a sub-pixel edge solid.
Square caps are kept (see the note below).

**Tube edges (Ctrl+E)**

- The cylinder-edge mode fills the joints where the open tubes meet by
drawing a small sphere at each edge endpoint, in the cylinder color, the
analogue of ParaView's `RenderLinesAsTubes` plus
`RenderPointsAsSpheres`.
- The spheres are drawn from the edges, not from the scene's point set.
Drawing from the point set could place a sphere where no cylinder is: a
2D triangulation's point set includes its infinite vertex, and some
meshes include the endpoint of a zero-length degenerate edge whose
cylinder is invisible but whose sphere still shows, which appeared as a
stray floating mark near a junction. A geometry shader now takes each
edge, skips it when its two endpoints coincide, and emits a sphere at
each endpoint, so a sphere only ever lands where a real tube ends.
- Each endpoint sphere takes that endpoint's color, so the joints match
the cylinders even when the edges are colored individually.
- Known limitation: at very large edge widths the join sphere does not
completely fill the sharp corner where three thick tubes meet, so a thin
sliver of the face can show. At normal sizes it is clean. A better
large-width join is planned on a follow-up branch.

**Sizing**

- Edge width and vertex size are constant screen-pixel sizes,
independent of the scene scale and identical in 2D and 3D, so the
initial size is correct for a mesh at any scale and the +/- step is a
consistent pixel increment. Previously they were derived from the
bounding-box diagonal, which made flat edges sub-pixel on small meshes
and oversized on large ones.
- The cylinder (tube) radius and the sphere-vertex radius follow the
same screen-pixel size as the flat edges and points, using the camera
pixel-to-world ratio (`pixelGLRatio`), so a size-N edge is about N
pixels thick in both flat and tube mode, and a vertex matches between
the point and sphere modes. When zoomed in very close the tube looks
slightly thicker, since it is real 3D geometry while the flat edge is
screen space, but at normal framing they match.

**Size keys**

- Shift and the minus key now decreases the edge and vertex size. The
handlers matched `Qt::Key_Minus`, but on most keyboard layouts Shift and
minus reports `Qt::Key_Underscore`, so decreasing was silently ignored
while Shift and plus (which reports `Key_Plus`) worked. The minus
handlers now also accept `Key_Underscore`, for both the edge size and
the Ctrl vertex size. This was pre-existing in the size-key handling,
not introduced by the shader work.

**HiDPI / thin-edge fix**

- The edge and point sizes are scaled by the device pixel ratio to match
`u_Viewport`, so edges are not drawn too thin on HiDPI displays.
- The geometry shader clamps a minimum half-width, so a very thin edge
keeps a solid one-pixel core instead of fading into the background
color.

**Cleanup**

- Removes the now-unused `u_IsOrthographic` uniform and its setters (the
orthographic special-case was dropped when the sizes became constant
pixels).

### Testing

Tested on a hexahedron (3D), a 2D Delaunay triangulation, and a dense
surface mesh, at unit scale and at about 500-unit scale, with flat edges
and with tube edges (Ctrl+E), and at a simulated 2x HiDPI display. Edges
and vertices render with the correct, consistent size and color in all
cases, including after pressing +/- and Shift and minus to change the
size. The flat edges stay solid down to the smallest size, the flat and
tube thicknesses match at the same size, and the clipping-plane and
transparent rendering path still works. The tube joins are filled with
no stray sphere on the 2D triangulation (infinite vertex) or on a mesh
with a degenerate edge, and a clean cube fills correctly. Before/after
screenshots are on the CGAL wiki.

### Note on caps (square vs round)

Flat edges use square caps. Round caps fill the corners where two edges
meet but add a slight rounded bulge at free ends, and on a dense mesh at
large width they overshoot each vertex and overlap into a fish-scale
pattern. Square caps avoid that and only leave a small corner gap that
shows at large width. After comparing both on real renders and
discussing with the mentors, square caps are kept as the default, with
round documented as the alternative. The filled-join case is the tube
mode (cylinders plus per-endpoint spheres).
2026-07-08 09:22:40 +02:00
..