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

feat(volume-rendering): picking, depth testing, and blending improvements #589

Merged
merged 44 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
dd4b6e5
feat: add a mode override to VR layers
seankmartin May 20, 2024
f8acb83
feat: run a second rendering pass on VR layers for picking
seankmartin May 20, 2024
d8e414c
feat: detect camera move in perspective panel
seankmartin May 20, 2024
b7aac11
feat: allow render context to know camera move
seankmartin May 20, 2024
fb6b3f3
Merge branch 'feature/detect-camera-movement-perspective-panel' into …
seankmartin May 20, 2024
128953e
feat: only two pass render VR pick when no camera movement
seankmartin May 20, 2024
f0df602
refactor: simplify gl state setting in render loop for VR
seankmartin May 20, 2024
fb26891
feat: only redraw if VR present as only VR uses redraw
seankmartin May 23, 2024
79bf01c
Merge branch 'master' into feature/picking-in-non-max-vr
seankmartin Jun 12, 2024
ac1aadd
temp: second pass in single draw call progress
seankmartin Jun 12, 2024
4be3f64
temp: establish more shader control
seankmartin Jun 12, 2024
be927c0
fix: show on VR color
seankmartin Jun 17, 2024
d8188a4
fix: correct setting of chunk translation
seankmartin Jun 17, 2024
742a1bd
feat: add context tracking of continous camera motion
seankmartin Jun 20, 2024
487e06b
feat: link perspective panel to context camera tracking
seankmartin Jun 20, 2024
59d8ec7
fix: rename camera movement in layers
seankmartin Jun 20, 2024
dfef0b6
feat: mark rotation in panel as continuous
seankmartin Jun 20, 2024
d66c649
feat: mark rendered data panel actions as camera move
seankmartin Jun 20, 2024
75de9a7
feat: mark slice rotations as continous camera
seankmartin Jun 20, 2024
41827c2
Merge branch 'master' into feature/picking-in-non-max-vr
seankmartin Jun 20, 2024
b340302
refactor: clearer gl state tracking for vr on/max
seankmartin Jun 21, 2024
71ef934
refactor: interface shader uniforms and reduce duplication
seankmartin Jun 21, 2024
3b1b05b
fix: set new source outside of loop, ensure mode override can't get s…
seankmartin Jun 21, 2024
6c639b9
refactor: unify naming of camera motion in contexts
seankmartin Jun 21, 2024
e95e7c8
Merge branch 'feature/continous-camera-movement-tracking' into featur…
seankmartin Jun 21, 2024
4dc0d99
fix: make unvarying const
seankmartin Jun 21, 2024
f4c0921
Merge branch 'master' into feature/picking-in-non-max-vr
seankmartin Jul 4, 2024
63441b6
refactor: cleaner integration of new camera tracking
seankmartin Jul 4, 2024
f7eae85
fix: logic for when VR picking is available
seankmartin Jul 4, 2024
b7d5dc9
fix: correctly draw VR to new VR buffer in max/normal hybrid
seankmartin Jul 4, 2024
bdb9f26
fix: OIT blend VR layer and other transparent layers after new buffer…
seankmartin Jul 4, 2024
4bf89e9
refactor: normalize naming across files
seankmartin Jul 8, 2024
9eb8ca2
refactor: clearer buffer interaction during VR rendering
seankmartin Jul 8, 2024
73573ca
fix: correctly set back to state and buffer during VR if a second pas…
seankmartin Jul 8, 2024
0fe477e
refactor: re-use shader uniform setting function in max and non max VR
seankmartin Jul 8, 2024
266cd14
refactor: fix typo in var name
seankmartin Jul 8, 2024
5f3fce7
chore: fix formatting
seankmartin Jul 8, 2024
f2fd54e
fix: turn off pre-depth testing in VR to allow in shader test instead
seankmartin Jul 8, 2024
effa49e
feat: remove unused epth shader
seankmartin Jul 8, 2024
19072b4
fix: enable required depth test for second pass max projection
seankmartin Jul 8, 2024
cd4fc53
fix: allow wireframe mode in VR to work again
seankmartin Jul 11, 2024
fec5e62
feat: allow 8x VR downsampling
seankmartin Jul 11, 2024
62ebd99
refactor: remove unused "needToCleanUpVolumeRendering" variable
seankmartin Jul 24, 2024
286a53e
Merge branch 'master' into feature/picking-in-non-max-vr
seankmartin Jul 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 109 additions & 83 deletions src/perspective_view/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,17 @@ v4f_fragColor = vec4(accum.rgb / accum.a, revealage);
`);
}

// Copy the depth from opaque pass to the depth buffer for OIT.
// This copy is required because the OIT depth buffer might be
// smaller than the main depth buffer.
function defineDepthCopyShader(builder: ShaderBuilder) {
function defineTransparentToTransparentCopyShader(builder: ShaderBuilder) {
builder.addOutputBuffer("vec4", "v4f_fragData0", 0);
builder.addOutputBuffer("vec4", "v4f_fragData1", 1);
builder.addFragmentCode(glsl_perspectivePanelEmitOIT);
builder.setFragmentMain(`
v4f_fragData0 = vec4(0.0, 0.0, 0.0, 1.0);
v4f_fragData1 = vec4(0.0, 0.0, 0.0, 1.0);
vec4 v0 = getValue0();
gl_FragDepth = 1.0 - v0.r;
vec4 v0 = getValue0();
vec4 v1 = getValue1();
vec4 accum = vec4(v0.rgb, v1.r);
float revealage = v0.a;

emitAccumAndRevealage(accum, 1.0 - revealage, 0u);
`);
}

Expand Down Expand Up @@ -276,6 +276,7 @@ export class PerspectivePanel extends RenderedDataPanel {
protected visibleLayerTracker: Owned<
VisibleRenderLayerTracker<PerspectivePanel, PerspectiveViewRenderLayer>
>;
private hasVolumeRendering = false;

get rpc() {
return this.sharedObject.rpc!;
Expand All @@ -293,20 +294,18 @@ export class PerspectivePanel extends RenderedDataPanel {
// to avoid flickering when the camera is moving
private frameRateCalculator = new DownsamplingBasedOnFrameRateCalculator(
6 /* numberOfStoredFrameDeltas */,
4 /* maxDownsamplingFactor */,
8 /* maxDownsamplingFactor */,
8 /* desiredFrameTimingMs */,
60 /* downsamplingPersistenceDurationInFrames */,
);
private isCameraInContinuousMotion = false;
private isContinuousCameraMotionInProgress = false;
get shouldDownsample() {
return (
this.viewer.enableAdaptiveDownsampling.value &&
this.isCameraInContinuousMotion &&
this.isContinuousCameraMotionInProgress &&
this.hasVolumeRendering
);
}
private hasVolumeRendering = false;
private hasTransparent = false;

/**
* If boolean value is true, sliceView is shown unconditionally, regardless of the value of
Expand Down Expand Up @@ -374,12 +373,16 @@ export class PerspectivePanel extends RenderedDataPanel {
protected transparencyCopyHelper = this.registerDisposer(
OffscreenCopyHelper.get(this.gl, defineTransparencyCopyShader, 2),
);
protected transparentToTransparentCopyHelper = this.registerDisposer(
OffscreenCopyHelper.get(
this.gl,
defineTransparentToTransparentCopyShader,
2,
),
);
protected maxProjectionColorCopyHelper = this.registerDisposer(
OffscreenCopyHelper.get(this.gl, defineMaxProjectionColorCopyShader, 2),
);
protected offscreenDepthCopyHelper = this.registerDisposer(
OffscreenCopyHelper.get(this.gl, defineDepthCopyShader, 1),
);
protected maxProjectionPickCopyHelper = this.registerDisposer(
OffscreenCopyHelper.get(this.gl, defineMaxProjectionPickCopyShader, 2),
);
Expand Down Expand Up @@ -472,7 +475,7 @@ export class PerspectivePanel extends RenderedDataPanel {

this.registerDisposer(
this.context.continuousCameraMotionFinished.add(() => {
this.isCameraInContinuousMotion = false;
this.isContinuousCameraMotionInProgress = false;
if (this.hasVolumeRendering) {
this.scheduleRedraw();
this.frameRateCalculator.resetForNewFrameSet();
Expand All @@ -481,7 +484,7 @@ export class PerspectivePanel extends RenderedDataPanel {
);
this.registerDisposer(
this.context.continuousCameraMotionStarted.add(() => {
this.isCameraInContinuousMotion = true;
this.isContinuousCameraMotionInProgress = true;
}),
);

Expand Down Expand Up @@ -995,7 +998,7 @@ export class PerspectivePanel extends RenderedDataPanel {
frameNumber: this.context.frameNumber,
sliceViewsPresent: this.sliceViews.size > 0,
isContinuousCameraMotionInProgress:
this.context.isContinuousCameraMotionInProgress,
this.isContinuousCameraMotionInProgress,
};

mat4.copy(
Expand All @@ -1005,8 +1008,8 @@ export class PerspectivePanel extends RenderedDataPanel {

const { visibleLayers } = this.visibleLayerTracker;

this.hasTransparent = false;
let hasMaxProjection = false;
let hasTransparent = false;
let hasVolumeRenderingPick = false;
let hasAnnotation = false;
let hasVolumeRendering = false;

Expand All @@ -1019,11 +1022,14 @@ export class PerspectivePanel extends RenderedDataPanel {
hasAnnotation = true;
}
} else {
this.hasTransparent = true;
hasTransparent = true;
if (renderLayer.isVolumeRendering) {
hasVolumeRendering = true;
hasMaxProjection =
hasMaxProjection ||
// Volume rendering layers are not pickable when the camera is moving.
// Unless the layer is a projection layer.
hasVolumeRenderingPick =
hasVolumeRenderingPick ||
!this.isContinuousCameraMotionInProgress ||
isProjectionLayer(renderLayer as VolumeRenderingRenderLayer);
}
}
Expand Down Expand Up @@ -1070,7 +1076,7 @@ export class PerspectivePanel extends RenderedDataPanel {
/*dppass=*/ WebGL2RenderingContext.KEEP,
);

if (this.hasTransparent) {
if (hasTransparent) {
//Draw transparent objects.

let volumeRenderingBufferWidth = width;
Expand All @@ -1095,10 +1101,13 @@ export class PerspectivePanel extends RenderedDataPanel {
}
}

// Create max projection buffer if needed.
// Create volume rendering related buffers.
let bindMaxProjectionBuffer: () => void = () => {};
let bindMaxProjectionPickingBuffer: () => void = () => {};
if (hasMaxProjection) {
let bindVolumeRenderingBuffer: () => void = () => {};
if (this.hasVolumeRendering) {
// Max projection setup
renderContext.maxProjectionEmit = maxProjectionEmit;
const { maxProjectionConfiguration } = this;
bindMaxProjectionBuffer = () => {
maxProjectionConfiguration.bind(
Expand All @@ -1116,6 +1125,7 @@ export class PerspectivePanel extends RenderedDataPanel {
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
);

// Max projection picking setup
const { maxProjectionPickConfiguration } = this;
bindMaxProjectionPickingBuffer = () => {
maxProjectionPickConfiguration.bind(
Expand All @@ -1128,28 +1138,22 @@ export class PerspectivePanel extends RenderedDataPanel {
WebGL2RenderingContext.COLOR_BUFFER_BIT |
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
);
}

let bindVolumeRenderingBuffer: () => void = () => {};
if (hasVolumeRendering) {
// Volume rendering setup
bindVolumeRenderingBuffer = () => {
this.volumeRenderingConfiguration.bind(
volumeRenderingBufferWidth,
volumeRenderingBufferHeight,
);
};
bindVolumeRenderingBuffer();
// Copy the depth buffer from the offscreen framebuffer to the volume rendering framebuffer.
gl.depthMask(true);
renderContext.bindVolumeRenderingBuffer = bindVolumeRenderingBuffer;
gl.clearDepth(1.0);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(
WebGL2RenderingContext.COLOR_BUFFER_BIT |
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
);
this.offscreenDepthCopyHelper.draw(
this.offscreenFramebuffer.colorBuffers[OffscreenTextures.Z].texture,
);
}

const { transparentConfiguration } = this;
Expand All @@ -1174,30 +1178,58 @@ export class PerspectivePanel extends RenderedDataPanel {
let currentTransparentRenderingState =
TransparentRenderingState.TRANSPARENT;
for (const [renderLayer, attachment] of visibleLayers) {
if (renderLayer.isTransparent) {
if (renderLayer.isVolumeRendering) {
renderContext.depthBufferTexture =
this.offscreenFramebuffer.colorBuffers[OffscreenTextures.Z].texture;
}
// Draw max projection layers
if (
renderLayer.isVolumeRendering &&
isProjectionLayer(renderLayer as VolumeRenderingRenderLayer)
) {
// Set state for max projection mode and draw
gl.depthMask(true);
gl.disable(WebGL2RenderingContext.BLEND);
gl.depthFunc(WebGL2RenderingContext.GREATER);

if (
currentTransparentRenderingState !==
TransparentRenderingState.MAX_PROJECTION
) {
renderContext.emitter = maxProjectionEmit;
bindMaxProjectionBuffer();
const isVolumeProjectionLayer = isProjectionLayer(
renderLayer as VolumeRenderingRenderLayer,
);
const needsSecondPickingPass =
!isVolumeProjectionLayer &&
!this.isContinuousCameraMotionInProgress &&
!renderContext.wireFrame;

// Bind the appropriate buffer and set state
if (isVolumeProjectionLayer) {
gl.depthMask(true);
gl.disable(WebGL2RenderingContext.BLEND);
gl.depthFunc(WebGL2RenderingContext.GREATER);
if (
currentTransparentRenderingState !==
TransparentRenderingState.MAX_PROJECTION
) {
renderContext.emitter = maxProjectionEmit;
bindMaxProjectionBuffer();
}
} else {
if (
currentTransparentRenderingState !==
TransparentRenderingState.VOLUME_RENDERING
) {
renderContext.emitter = perspectivePanelEmitOIT;
bindVolumeRenderingBuffer();
}
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
currentTransparentRenderingState =
TransparentRenderingState.VOLUME_RENDERING;
}

// Two cases for volume rendering layers
// Either way, a draw call is needed first
renderLayer.draw(renderContext, attachment);
gl.enable(WebGL2RenderingContext.DEPTH_TEST);

// Copy max projection result to picking buffer
// Case 1 - No picking pass needed and not a projection layer
// we already have the color information, so we skip the max projection pass
if (!needsSecondPickingPass && !isVolumeProjectionLayer) {
continue;
}

// Case 2 - Picking will be computed from a max projection
// And a second pass may be needed to do this picking

// Copy the volume rendering picking result to the main picking buffer
// Depth testing on to combine max layers into one pick buffer via depth
bindMaxProjectionPickingBuffer();
this.maxProjectionToPickCopyHelper.draw(
Expand All @@ -1207,25 +1239,30 @@ export class PerspectivePanel extends RenderedDataPanel {
this.maxProjectionConfiguration.colorBuffers[3 /*pick*/].texture,
);

// Copy max projection color result to the transparent buffer with OIT
// Depth testing off to combine max layers into one color via blend
bindVolumeRenderingBuffer();
gl.depthMask(false);
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
// Turn back on OIT blending
gl.enable(WebGL2RenderingContext.BLEND);
gl.blendFuncSeparate(
WebGL2RenderingContext.ONE,
WebGL2RenderingContext.ONE,
WebGL2RenderingContext.ZERO,
WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA,
);
this.maxProjectionColorCopyHelper.draw(
this.maxProjectionConfiguration.colorBuffers[0 /*color*/].texture,
this.maxProjectionConfiguration.colorBuffers[1 /*depth*/].texture,
);

// Reset the max projection buffer
// Copy max projection color result to the transparent buffer with OIT
// Depth testing off to combine max layers into one color via blending
if (isVolumeProjectionLayer) {
bindVolumeRenderingBuffer();
gl.depthMask(false);
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
this.maxProjectionColorCopyHelper.draw(
this.maxProjectionConfiguration.colorBuffers[0 /*color*/].texture,
this.maxProjectionConfiguration.colorBuffers[1 /*depth*/].texture,
);
}

// Reset the max projection color, depth, and picking buffer
bindMaxProjectionBuffer();
renderContext.emitter = maxProjectionEmit;
gl.depthMask(true);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clearDepth(0.0);
Expand All @@ -1234,7 +1271,7 @@ export class PerspectivePanel extends RenderedDataPanel {
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
);

// Set back to non-max projection state
// Set some values back to non-max projection state
gl.clearDepth(1.0);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.depthMask(false);
Expand All @@ -1243,17 +1280,6 @@ export class PerspectivePanel extends RenderedDataPanel {

currentTransparentRenderingState =
TransparentRenderingState.MAX_PROJECTION;
} else if (renderLayer.isVolumeRendering) {
if (
currentTransparentRenderingState !==
TransparentRenderingState.VOLUME_RENDERING
) {
renderContext.emitter = perspectivePanelEmitOIT;
bindVolumeRenderingBuffer();
}
currentTransparentRenderingState =
TransparentRenderingState.VOLUME_RENDERING;
renderLayer.draw(renderContext, attachment);
}
// Draw regular transparent layers
else if (renderLayer.isTransparent) {
Expand All @@ -1271,18 +1297,18 @@ export class PerspectivePanel extends RenderedDataPanel {
}
// Copy transparent rendering result back to primary buffer.
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
gl.viewport(0, 0, width, height);
this.offscreenFramebuffer.bindSingle(OffscreenTextures.COLOR);
gl.blendFunc(
WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA,
WebGL2RenderingContext.SRC_ALPHA,
);
if (hasVolumeRendering) {
this.transparencyCopyHelper.draw(
renderContext.bindFramebuffer();
this.transparentToTransparentCopyHelper.draw(
this.volumeRenderingConfiguration.colorBuffers[0].texture,
this.volumeRenderingConfiguration.colorBuffers[1].texture,
);
}
gl.blendFunc(
WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA,
WebGL2RenderingContext.SRC_ALPHA,
);
this.offscreenFramebuffer.bindSingle(OffscreenTextures.COLOR);
this.transparencyCopyHelper.draw(
transparentConfiguration.colorBuffers[0].texture,
transparentConfiguration.colorBuffers[1].texture,
Expand Down Expand Up @@ -1319,7 +1345,7 @@ export class PerspectivePanel extends RenderedDataPanel {
/*dppass=*/ WebGL2RenderingContext.REPLACE,
);
gl.stencilMask(2);
if (hasMaxProjection) {
if (hasVolumeRenderingPick) {
this.maxProjectionPickCopyHelper.draw(
this.maxProjectionPickConfiguration.colorBuffers[0].texture /*depth*/,
this.maxProjectionPickConfiguration.colorBuffers[1].texture /*pick*/,
Expand Down
Loading
Loading