Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

World grid part 1/2: add world grid renderer to re_renderer #8230

Merged
merged 17 commits into from
Nov 28, 2024

Conversation

Wumpf
Copy link
Member

@Wumpf Wumpf commented Nov 27, 2024

Related

This is the first half of

The second half is here:

What

Implements a "world grid" to be used later in the spatial views of the viewer.
Haven't tested yet how suitable this is for 2D views, might need some adjustments.

Video

Youtube unfortunately compresses this a lot and Github doesn't let me upload any significant amount of video
(click me)
youtube video of world grid v1

Screenshots

Analytical anti-aliasing (shown is what is supposed to be a 1ui unit == 2pixel wide line):
image

Distance has only the cardinal lines (== every tenth line). Those fade eventually as well:
image

Fading is based on "how many lines coincide here" which makes fading view dependent rather than distance dependent:
image
(this makes this hopefully robust for many usecases)

Grid intersects non-transparent geometry (transparent geometry will be ignored in the future. previous surveying showed that this is common and not a biggy)
image

Grid fades at acute viewing angles (because empirically and unsurprisingly it looks weird if we don't!)
image

Tested image quality to be satisfying on high dpi (ui unit == 2 pixels) and low dpi (ui unit == pixel).

How does it work

  • Draw a large shader generated plane (mostly because setting up vertex buffers is just more hassle 😄 )
    • depth test enabled
    • depth write disabled
    • premultiplied alpha blend
    • make sure we draw this after the background (reminder: we first draw solid, then background)
  • Fragment shader looks at 2d coordinate on the plane and decides how "liney" it is
    • using screen space derivatives (ddx/ddy) we figure out how far to go on the plane to achieve a certain line thickness
    • fades:
      • fade if the line density gets too high
      • fade if view angle is too acute relative
    • ... lot's of details documented in the shader code!

I considered just drawing a screen space triangle, but drawing a plane that moves with the camera has lots of advantages:

  • don't have to manipulate depth
    • faster since early z just works
    • less thinking required!
  • don't cover things above the horizon - even if only the grid is visible, less pixels will be shaded

Known shortcomings

  • various hardcoded values around how fading works. Tricky to formalize this better, but likely good enough
    • Doesn't look equally good at all pixel widths, but decent for the range we care
  • every tenth line is a "cardinal line", that's nice but it stops there - "infinite" amount of cardinal lines would be nicer
    • Experimented with that but so far didn't get anything that was compelling. Having many order-of-magnitude lines looks way too busy imho, it needs a mechanism that limits that to two.
    • Blender's 2D view does this really well, but in 3D they only do two cardinals as well.

Try it yourself

Controls:

  • Space: Pause
  • G: Toggle camera mode

native:

cargo run -p re_renderer_examples --bin world_grid

web:

cargo run-wasm -p re_renderer_examples --bin world_grid

Backend testing matrix

  • Metal
  • Vulkan
  • WebGL
  • WebGPU

@Wumpf Wumpf added the 🔺 re_renderer affects re_renderer itself label Nov 27, 2024
Copy link

github-actions bot commented Nov 27, 2024

Web viewer built successfully. If applicable, you should also test it:

  • I have tested the web viewer
Result Commit Link
359eeb5 https://rerun.io/viewer/pr/8230

Note: This comment is updated whenever you push a commit.

@Wumpf Wumpf added 📺 re_viewer affects re_viewer itself include in changelog labels Nov 27, 2024
Comment on lines +127 to +128
// X and Y are combined like akin to premultiplied alpha operations.
let intensity_combined = saturate(cardinal_and_regular.x * (1.0 - cardinal_and_regular.y) + cardinal_and_regular.y);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh not so sure about this anymore. Sounded like a compelling idea in Ben's article, but looking at the close-ups I actually don't like the change in color when grid lines meet.
So maybe more like

Suggested change
// X and Y are combined like akin to premultiplied alpha operations.
let intensity_combined = saturate(cardinal_and_regular.x * (1.0 - cardinal_and_regular.y) + cardinal_and_regular.y);
let intensity_combined = max(cardinal_and_regular.x, cardinal_and_regular.y);

It's very subtle though either way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let intensity_combined = saturate(cardinal_and_regular.x * (1.0 - cardinal_and_regular.y) + cardinal_and_regular.y);

Screenshot 2024-11-27 at 21 37 30

let intensity_combined = max(cardinal_and_regular.x, cardinal_and_regular.y);

Screenshot 2024-11-27 at 21 36 58

I think the first is better at this particular angle (less Moiré). Hard to test without camera control

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wow yeah so much better, agreed
more visual refinements on the way in the follow-up pr

@Wumpf
Copy link
Member Author

Wumpf commented Nov 27, 2024

started integrating this into the viewer and will have adjustments to the style based on experiences in real examples. Not feeding those back in because I don't want to redo all those screenshots

@Wumpf
Copy link
Member Author

Wumpf commented Nov 27, 2024

view angle fade is one of the things that isn't holding up: The implementation here is too simplistic. Also the "line density aware fade" is taking care of this well enough

@emilk emilk self-requested a review November 27, 2024 20:13
Copy link
Member

@emilk emilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So beautiful 🤩
sparkle-eyes

crates/viewer/re_renderer/shader/world_grid.wgsl Outdated Show resolved Hide resolved
crates/viewer/re_renderer/shader/world_grid.wgsl Outdated Show resolved Hide resolved
crates/viewer/re_renderer/shader/world_grid.wgsl Outdated Show resolved Hide resolved
crates/viewer/re_renderer/shader/world_grid.wgsl Outdated Show resolved Hide resolved
crates/viewer/re_renderer/shader/world_grid.wgsl Outdated Show resolved Hide resolved
Comment on lines +114 to +115
// It seems that if we want to go down this path, we should ensure that there's only two levels of lines on screen at a time.
const CARDINAL_LINE_FACTOR: f32 = 10.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a reference: in egui_plot I have an infinite level of lines, but with only three levels viisble at any time, with a smooth fade to avoid discontinuities: https://emilk.github.io/egui_plot/

This means no matter how small or big your plots are, you always have thicker and thinner lines to guide you

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have some new ideas that I want to try in the follow-up pr
the main issue I found was that having more than two "types" of lines on screen looks bad. And in a 3d perspective I found it very tricky to select the right levels. That said, maybe it's really not that hard, I want to try some more. Thanks for the link!

Comment on lines +127 to +128
// X and Y are combined like akin to premultiplied alpha operations.
let intensity_combined = saturate(cardinal_and_regular.x * (1.0 - cardinal_and_regular.y) + cardinal_and_regular.y);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let intensity_combined = saturate(cardinal_and_regular.x * (1.0 - cardinal_and_regular.y) + cardinal_and_regular.y);

Screenshot 2024-11-27 at 21 37 30

let intensity_combined = max(cardinal_and_regular.x, cardinal_and_regular.y);

Screenshot 2024-11-27 at 21 36 58

I think the first is better at this particular angle (less Moiré). Hard to test without camera control

crates/viewer/re_renderer_examples/2d.rs Show resolved Hide resolved
@Wumpf Wumpf force-pushed the andreas/world-grid branch from 381febc to 16af40b Compare November 28, 2024 09:41
@Wumpf Wumpf merged commit a54c2c5 into main Nov 28, 2024
31 checks passed
@Wumpf Wumpf deleted the andreas/world-grid branch November 28, 2024 10:05
Wumpf added a commit that referenced this pull request Dec 6, 2024
### Related

* Follow-up of part 1: #8230
* Fixes #872

### What

Adds new line grid property to the 3D view and enables it by default.

Furthermore...
* improves the grid shader with infinite cardinal lines while showing
only two types of "cardinalitities"
* this is simply based on log10(camera distance from plane) and then
blends between two levels of grid
* improve grid's handling of extreme zoom levels. Still some instability
on WebGL, but overall much more robust (less flickering/cutoff)
* Introduce a general `Plane3D` component which is meant for later reuse
in other contexts

The new view property is best described its fbs:
```rust
/// Configuration for the 3D line grid.
table LineGrid3D (
    "attr.rerun.scope": "blueprint"
) {
    /// Whether the grid is visible.
    ///
    /// Defaults to true.
    visible: rerun.blueprint.components.Visible ("attr.rerun.component_optional", nullable, order: 1000);

    /// Space between grid lines spacing of one line to the next in scene units.
    ///
    /// As you zoom out, successively only every tenth line is shown.
    /// This controls the closest zoom level.
    spacing: rerun.blueprint.components.GridSpacing ("attr.rerun.component_optional", nullable, order: 2000);

    /// In what plane the grid is drawn.
    ///
    /// Defaults to whatever plane is determined as the plane at zero units up/down as defined by [components.ViewCoordinates] if present.
    plane: rerun.components.Plane3D ("attr.rerun.component_optional", nullable, order: 3000);

    /// How thick the lines should be in ui units.
    ///
    /// Default is 1.0 ui unit.
    stroke_width: rerun.components.StrokeWidth ("attr.rerun.component_optional", nullable, order: 5000);

    /// Color used for the grid.
    ///
    /// Transparency via alpha channel is supported.
    /// Defaults to a slightly transparent light gray.
    color: rerun.components.Color ("attr.rerun.component_optional", nullable, order: 6000);
}

```

Properties in the viewer in default selection panel width
<img width="291" alt="image"
src="https://github.com/user-attachments/assets/8292f7bb-e2a4-42b4-9147-7bbd72fc38c7">

New plane controls expanded:
<img width="281" alt="image"
src="https://github.com/user-attachments/assets/0cc19ce7-1031-4b58-bc24-7c2699d5e25b">



Examples of the grid in action (in default settings):
<img width="1326" alt="image"
src="https://github.com/user-attachments/assets/fe167eef-29e4-478f-a6e2-d2fb82f30a91">
<img width="1563" alt="image"
src="https://github.com/user-attachments/assets/d1a878be-16b0-4c9b-873c-fcf1abcd67fd">


Bad case - grid is not fine enough and through the scene:
<img width="1154" alt="image"
src="https://github.com/user-attachments/assets/cb76355a-778d-4bd9-a724-b4d9b1dffd84">
Bad case - grid is above the scene.
<img width="1464" alt="image"
src="https://github.com/user-attachments/assets/0a150db3-f344-444a-a4a0-33c877ea38ae">

## Testing 

* [x] Metal
* [x] Vulkan
* [x] WebGPU
* [x] WebGL

##  Future work

* tweak some examples to disable the grid or change its spacing
* Extend grid to 2d scenes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
include in changelog 🔺 re_renderer affects re_renderer itself 📺 re_viewer affects re_viewer itself
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants