xeokit / xeokit-sdk

Open source JavaScript SDK for viewing high-detail, full-precision 3D BIM and AEC models in the Web browser.
https://xeokit.io
Other
728 stars 287 forks source link

Logarithmic Depth Buffer #254

Closed xeolabs closed 3 years ago

xeolabs commented 4 years ago

Sometimes it's necessary to use a logarithmic depth buffer if dealing with huge differences in scale in a single scene.

Benefits

  1. Prevents distant objects from being clipped by the far clipping plane, since the plane can then be positioned very far away,
  2. prevents Z-fighting between objects by improving the precision of WebGL depth testing for objects that are close to the viewpoint, and
  3. improves the precision of Annotation occlusion culling.

Caveats

Performance

Internally, xeokit's logarithmic depth buffer uses WebGL gl_FragDepth, if available, which disables WebGL's Early Fragment Test optimization and can cause a decrease in performance.

SAO

Scalable Ambient Obscurance (SAO) doesn't work well with a very distant far clipping plane. Possibly SAO could be modified, to have it's own "virtual" far clipping plane, the distance of which can perhaps be derived from SAO scale.

Examples

Support

xeokit's logarithmic depth buffer relies on the Browser supporting the WebGL(1) extension EXT_frag_depth.

As of January 2021, browsers supporting this extension are:

Capture d’écran de 2021-01-14 13-40-24

See if your browser supports it here.

Usage

To configure our Viewer to have a logarithmic depth buffer:

const viewer = new Viewer({
    canvasId: "myCanvas",
    logDepthBufferEnabled: true // <<--- Enable logarithmic depth buffer
});

A logarithmic depth buffer enables us to specify a very distant far clipping plane for our camera.

We'll set the camera's near clipping plane to .3, and the far clipping plane to to 100 billion light years away:

viewer.camera.perspective.near = 0.3;
viewer.camera.perspective.far = 1e27;

viewer.camera.ortho.near = 0.3;
viewer.camera.ortho.far = 1e27;

Results

Disabled

The screenshot below shows a programmatically built PerformanceModel, with clipping planes set as above, and logarithmic depth buffer disabled. Note the Z-fighting among the objects.

Screenshot from 2021-01-07 20-35-25

Enabled

The same scene, with logarithmic depth buffer enabled:

Screenshot from 2021-01-07 20-37-06

Amoki commented 3 years ago
const supported = Viewer.logarithmicDepthbufferSupported; // True if supported

What feature is needed? I'd like to check the compatibility of browsers versions

xeolabs commented 3 years ago

This relies on support for the WebGL(1) extension EXT_frag_depth.

Browsers can be checked for the presence of this extension here;

Amoki commented 3 years ago

Capture d’écran de 2021-01-14 13-40-24 from: https://developer.mozilla.org/en-US/docs/Web/API/EXT_frag_depth#browser_compatibility

xeolabs commented 3 years ago

When we're running on a browser that lacks support, we should (at least):

For some models, lack of log depth support is OK, and picking is still accurate. The Lyon model doesn't seem to require log depth buf at all: https://xeokit.github.io/xeokit-sdk/examples/#logDepthBuf_XKT_Lyon_logDepthBufDisabled

I expect log depth buf becomes mandatory only as soon as the 3D scene contains a mixture of extremely near and far objects.

Amoki commented 3 years ago
const logarithmicDepthSupported = Viewer.logarithmicDepthBufferSupported;
const viewer = new Viewer({
  canvasId: this.engine3dCanvasId,
  transparent: true,
  spinnerElementId: this.spinnerCanvasId,
  enableOffsets: this.enableOffsets,
  logDepthBufferEnabled: logarithmicDepthSupported,
});
if (logarithmicDepthSupported) {
  viewer.camera.perspective.near = 1e-6;
  viewer.camera.perspective.far = 1e27;
  viewer.camera.ortho.near = 1e-6;
  viewer.camera.ortho.far = 1e27;
} else {
  viewer.camera.perspective.far = 100000.0;
  viewer.camera.ortho.far = 100000.0;
}

This is what I do.

But logarithmicDepthBufferSupported is not a static method so it is not accessible directly through the class and not an instance :/ (https://github.com/xeokit/xeokit-sdk/blob/master/src/viewer/Viewer.js#L146)

If we set it as a static method, we must update all calls from instances (like https://github.com/xeokit/xeokit-sdk/blob/568974e5b3e1002b0c6b81557431b92e3269f4e0/src/viewer/scene/PerformanceModel/lib/batching/occlusion/BatchingOcclusionShaderSource.js#L30)

Amoki commented 3 years ago

If scene.viewer.logarithmicDepthBufferSupported is checked everytime, we can do:

const viewer = new Viewer({
  canvasId: this.engine3dCanvasId,
  transparent: true,
  spinnerElementId: this.spinnerCanvasId,
  enableOffsets: this.enableOffsets,
  logDepthBufferEnabled: true,
});
const logarithmicDepthSupported = viewer.logarithmicDepthBufferSupported;
if (logarithmicDepthSupported) {
  viewer.camera.perspective.near = 1e-6;
  viewer.camera.perspective.far = 1e27;
  viewer.camera.ortho.near = 1e-6;
  viewer.camera.ortho.far = 1e27;
} else {
  viewer.camera.perspective.far = 100000.0;
  viewer.camera.ortho.far = 100000.0;
}

And we don't need to update the code, just the doc

xeolabs commented 3 years ago

Oops. It should be static. And I was assuming that a JS static property would be always accessible as an instance property also. I'll fix.

xeolabs commented 3 years ago

Fixed in master - logarithmicDepthBufferSupported is now both a static and an instance property of Viewer.

Amoki commented 3 years ago

Capture d’écran de 2021-01-14 18-02-59

I have this with logDepthBufferEnabled = false AND logDepthBufferEnabled = true (with the 19_Marc_Antoine_Petit.xkt you already have)

On both Firefox and Chrome, on Linux

    const logarithmicDepthSupported = Viewer.logarithmicDepthBufferSupported;
    const viewer = new Viewer({
      canvasId: this.canvasId,
      transparent: true,
      logDepthBufferEnabled: logarithmicDepthSupported,
    });

    if (logarithmicDepthSupported) {
      viewer.camera.perspective.near = 1e-6;
      viewer.camera.perspective.far = 1e27;
      viewer.camera.ortho.near = 1e-6;
      viewer.camera.ortho.far = 1e27;
    } else {
      viewer.camera.perspective.far = 100000.0;
      viewer.camera.ortho.far = 100000.0;
    }
xeolabs commented 3 years ago

For this model, it seems that the near plane causes inaccuracy when it's set below 0.001. At that value, I saw slight jittering, then as I decreased it to 0.0001, it looked like this:

Screenshot from 2021-01-14 18-31-39

In any case, a general rule for depth buffer accuracy is to push the near plane back as far as possible.

1e-6 is really just for fun, and is too close for BIM model viewing I think.

I think 0.1 is a reasonable value to use, or even as far as 0.5.

Using values like those, this model looked OK.