Skip to content

Commit 4e378b4

Browse files
authored
feat(volume-rendering): picking, depth testing, and blending improvements (#589)
* feat: add a mode override to VR layers this allows to run second passes in different modes in the background, without affecting the UI value * feat: run a second rendering pass on VR layers for picking * feat: detect camera move in perspective panel * feat: allow render context to know camera move * feat: only two pass render VR pick when no camera movement * refactor: simplify gl state setting in render loop for VR * feat: only redraw if VR present as only VR uses redraw * temp: second pass in single draw call progress * temp: establish more shader control * fix: show on VR color * fix: correct setting of chunk translation * feat: add context tracking of continous camera motion * feat: link perspective panel to context camera tracking Also only redraw if volume rendering is present after camera motion * fix: rename camera movement in layers * feat: mark rotation in panel as continuous * feat: mark rendered data panel actions as camera move * feat: mark slice rotations as continous camera * refactor: clearer gl state tracking for vr on/max * refactor: interface shader uniforms and reduce duplication * fix: set new source outside of loop, ensure mode override can't get stuck on * refactor: unify naming of camera motion in contexts * fix: make unvarying const * refactor: cleaner integration of new camera tracking * fix: logic for when VR picking is available * fix: correctly draw VR to new VR buffer in max/normal hybrid * fix: OIT blend VR layer and other transparent layers after new buffer made * refactor: normalize naming across files * refactor: clearer buffer interaction during VR rendering * fix: correctly set back to state and buffer during VR if a second pass or histogram is used * refactor: re-use shader uniform setting function in max and non max VR * refactor: fix typo in var name * chore: fix formatting * fix: turn off pre-depth testing in VR to allow in shader test instead * feat: remove unused epth shader * fix: enable required depth test for second pass max projection * fix: allow wireframe mode in VR to work again * feat: allow 8x VR downsampling * refactor: remove unused "needToCleanUpVolumeRendering" variable This always had the same value as "hasVolumeRendering", so "hasVolumeRendering" is used instead
1 parent 5c8744d commit 4e378b4

File tree

3 files changed

+348
-164
lines changed

3 files changed

+348
-164
lines changed

src/perspective_view/panel.ts

Lines changed: 109 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -196,17 +196,17 @@ v4f_fragColor = vec4(accum.rgb / accum.a, revealage);
196196
`);
197197
}
198198

199-
// Copy the depth from opaque pass to the depth buffer for OIT.
200-
// This copy is required because the OIT depth buffer might be
201-
// smaller than the main depth buffer.
202-
function defineDepthCopyShader(builder: ShaderBuilder) {
199+
function defineTransparentToTransparentCopyShader(builder: ShaderBuilder) {
203200
builder.addOutputBuffer("vec4", "v4f_fragData0", 0);
204201
builder.addOutputBuffer("vec4", "v4f_fragData1", 1);
202+
builder.addFragmentCode(glsl_perspectivePanelEmitOIT);
205203
builder.setFragmentMain(`
206-
v4f_fragData0 = vec4(0.0, 0.0, 0.0, 1.0);
207-
v4f_fragData1 = vec4(0.0, 0.0, 0.0, 1.0);
208-
vec4 v0 = getValue0();
209-
gl_FragDepth = 1.0 - v0.r;
204+
vec4 v0 = getValue0();
205+
vec4 v1 = getValue1();
206+
vec4 accum = vec4(v0.rgb, v1.r);
207+
float revealage = v0.a;
208+
209+
emitAccumAndRevealage(accum, 1.0 - revealage, 0u);
210210
`);
211211
}
212212

@@ -276,6 +276,7 @@ export class PerspectivePanel extends RenderedDataPanel {
276276
protected visibleLayerTracker: Owned<
277277
VisibleRenderLayerTracker<PerspectivePanel, PerspectiveViewRenderLayer>
278278
>;
279+
private hasVolumeRendering = false;
279280

280281
get rpc() {
281282
return this.sharedObject.rpc!;
@@ -293,20 +294,18 @@ export class PerspectivePanel extends RenderedDataPanel {
293294
// to avoid flickering when the camera is moving
294295
private frameRateCalculator = new DownsamplingBasedOnFrameRateCalculator(
295296
6 /* numberOfStoredFrameDeltas */,
296-
4 /* maxDownsamplingFactor */,
297+
8 /* maxDownsamplingFactor */,
297298
8 /* desiredFrameTimingMs */,
298299
60 /* downsamplingPersistenceDurationInFrames */,
299300
);
300-
private isCameraInContinuousMotion = false;
301+
private isContinuousCameraMotionInProgress = false;
301302
get shouldDownsample() {
302303
return (
303304
this.viewer.enableAdaptiveDownsampling.value &&
304-
this.isCameraInContinuousMotion &&
305+
this.isContinuousCameraMotionInProgress &&
305306
this.hasVolumeRendering
306307
);
307308
}
308-
private hasVolumeRendering = false;
309-
private hasTransparent = false;
310309

311310
/**
312311
* If boolean value is true, sliceView is shown unconditionally, regardless of the value of
@@ -374,12 +373,16 @@ export class PerspectivePanel extends RenderedDataPanel {
374373
protected transparencyCopyHelper = this.registerDisposer(
375374
OffscreenCopyHelper.get(this.gl, defineTransparencyCopyShader, 2),
376375
);
376+
protected transparentToTransparentCopyHelper = this.registerDisposer(
377+
OffscreenCopyHelper.get(
378+
this.gl,
379+
defineTransparentToTransparentCopyShader,
380+
2,
381+
),
382+
);
377383
protected maxProjectionColorCopyHelper = this.registerDisposer(
378384
OffscreenCopyHelper.get(this.gl, defineMaxProjectionColorCopyShader, 2),
379385
);
380-
protected offscreenDepthCopyHelper = this.registerDisposer(
381-
OffscreenCopyHelper.get(this.gl, defineDepthCopyShader, 1),
382-
);
383386
protected maxProjectionPickCopyHelper = this.registerDisposer(
384387
OffscreenCopyHelper.get(this.gl, defineMaxProjectionPickCopyShader, 2),
385388
);
@@ -472,7 +475,7 @@ export class PerspectivePanel extends RenderedDataPanel {
472475

473476
this.registerDisposer(
474477
this.context.continuousCameraMotionFinished.add(() => {
475-
this.isCameraInContinuousMotion = false;
478+
this.isContinuousCameraMotionInProgress = false;
476479
if (this.hasVolumeRendering) {
477480
this.scheduleRedraw();
478481
this.frameRateCalculator.resetForNewFrameSet();
@@ -481,7 +484,7 @@ export class PerspectivePanel extends RenderedDataPanel {
481484
);
482485
this.registerDisposer(
483486
this.context.continuousCameraMotionStarted.add(() => {
484-
this.isCameraInContinuousMotion = true;
487+
this.isContinuousCameraMotionInProgress = true;
485488
}),
486489
);
487490

@@ -995,7 +998,7 @@ export class PerspectivePanel extends RenderedDataPanel {
995998
frameNumber: this.context.frameNumber,
996999
sliceViewsPresent: this.sliceViews.size > 0,
9971000
isContinuousCameraMotionInProgress:
998-
this.context.isContinuousCameraMotionInProgress,
1001+
this.isContinuousCameraMotionInProgress,
9991002
};
10001003

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

10061009
const { visibleLayers } = this.visibleLayerTracker;
10071010

1008-
this.hasTransparent = false;
1009-
let hasMaxProjection = false;
1011+
let hasTransparent = false;
1012+
let hasVolumeRenderingPick = false;
10101013
let hasAnnotation = false;
10111014
let hasVolumeRendering = false;
10121015

@@ -1019,11 +1022,14 @@ export class PerspectivePanel extends RenderedDataPanel {
10191022
hasAnnotation = true;
10201023
}
10211024
} else {
1022-
this.hasTransparent = true;
1025+
hasTransparent = true;
10231026
if (renderLayer.isVolumeRendering) {
10241027
hasVolumeRendering = true;
1025-
hasMaxProjection =
1026-
hasMaxProjection ||
1028+
// Volume rendering layers are not pickable when the camera is moving.
1029+
// Unless the layer is a projection layer.
1030+
hasVolumeRenderingPick =
1031+
hasVolumeRenderingPick ||
1032+
!this.isContinuousCameraMotionInProgress ||
10271033
isProjectionLayer(renderLayer as VolumeRenderingRenderLayer);
10281034
}
10291035
}
@@ -1070,7 +1076,7 @@ export class PerspectivePanel extends RenderedDataPanel {
10701076
/*dppass=*/ WebGL2RenderingContext.KEEP,
10711077
);
10721078

1073-
if (this.hasTransparent) {
1079+
if (hasTransparent) {
10741080
//Draw transparent objects.
10751081

10761082
let volumeRenderingBufferWidth = width;
@@ -1095,10 +1101,13 @@ export class PerspectivePanel extends RenderedDataPanel {
10951101
}
10961102
}
10971103

1098-
// Create max projection buffer if needed.
1104+
// Create volume rendering related buffers.
10991105
let bindMaxProjectionBuffer: () => void = () => {};
11001106
let bindMaxProjectionPickingBuffer: () => void = () => {};
1101-
if (hasMaxProjection) {
1107+
let bindVolumeRenderingBuffer: () => void = () => {};
1108+
if (this.hasVolumeRendering) {
1109+
// Max projection setup
1110+
renderContext.maxProjectionEmit = maxProjectionEmit;
11021111
const { maxProjectionConfiguration } = this;
11031112
bindMaxProjectionBuffer = () => {
11041113
maxProjectionConfiguration.bind(
@@ -1116,6 +1125,7 @@ export class PerspectivePanel extends RenderedDataPanel {
11161125
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
11171126
);
11181127

1128+
// Max projection picking setup
11191129
const { maxProjectionPickConfiguration } = this;
11201130
bindMaxProjectionPickingBuffer = () => {
11211131
maxProjectionPickConfiguration.bind(
@@ -1128,28 +1138,22 @@ export class PerspectivePanel extends RenderedDataPanel {
11281138
WebGL2RenderingContext.COLOR_BUFFER_BIT |
11291139
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
11301140
);
1131-
}
11321141

1133-
let bindVolumeRenderingBuffer: () => void = () => {};
1134-
if (hasVolumeRendering) {
1142+
// Volume rendering setup
11351143
bindVolumeRenderingBuffer = () => {
11361144
this.volumeRenderingConfiguration.bind(
11371145
volumeRenderingBufferWidth,
11381146
volumeRenderingBufferHeight,
11391147
);
11401148
};
11411149
bindVolumeRenderingBuffer();
1142-
// Copy the depth buffer from the offscreen framebuffer to the volume rendering framebuffer.
1143-
gl.depthMask(true);
1150+
renderContext.bindVolumeRenderingBuffer = bindVolumeRenderingBuffer;
11441151
gl.clearDepth(1.0);
11451152
gl.clearColor(0.0, 0.0, 0.0, 1.0);
11461153
gl.clear(
11471154
WebGL2RenderingContext.COLOR_BUFFER_BIT |
11481155
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
11491156
);
1150-
this.offscreenDepthCopyHelper.draw(
1151-
this.offscreenFramebuffer.colorBuffers[OffscreenTextures.Z].texture,
1152-
);
11531157
}
11541158

11551159
const { transparentConfiguration } = this;
@@ -1174,30 +1178,58 @@ export class PerspectivePanel extends RenderedDataPanel {
11741178
let currentTransparentRenderingState =
11751179
TransparentRenderingState.TRANSPARENT;
11761180
for (const [renderLayer, attachment] of visibleLayers) {
1177-
if (renderLayer.isTransparent) {
1181+
if (renderLayer.isVolumeRendering) {
11781182
renderContext.depthBufferTexture =
11791183
this.offscreenFramebuffer.colorBuffers[OffscreenTextures.Z].texture;
1180-
}
1181-
// Draw max projection layers
1182-
if (
1183-
renderLayer.isVolumeRendering &&
1184-
isProjectionLayer(renderLayer as VolumeRenderingRenderLayer)
1185-
) {
1186-
// Set state for max projection mode and draw
1187-
gl.depthMask(true);
1188-
gl.disable(WebGL2RenderingContext.BLEND);
1189-
gl.depthFunc(WebGL2RenderingContext.GREATER);
11901184

1191-
if (
1192-
currentTransparentRenderingState !==
1193-
TransparentRenderingState.MAX_PROJECTION
1194-
) {
1195-
renderContext.emitter = maxProjectionEmit;
1196-
bindMaxProjectionBuffer();
1185+
const isVolumeProjectionLayer = isProjectionLayer(
1186+
renderLayer as VolumeRenderingRenderLayer,
1187+
);
1188+
const needsSecondPickingPass =
1189+
!isVolumeProjectionLayer &&
1190+
!this.isContinuousCameraMotionInProgress &&
1191+
!renderContext.wireFrame;
1192+
1193+
// Bind the appropriate buffer and set state
1194+
if (isVolumeProjectionLayer) {
1195+
gl.depthMask(true);
1196+
gl.disable(WebGL2RenderingContext.BLEND);
1197+
gl.depthFunc(WebGL2RenderingContext.GREATER);
1198+
if (
1199+
currentTransparentRenderingState !==
1200+
TransparentRenderingState.MAX_PROJECTION
1201+
) {
1202+
renderContext.emitter = maxProjectionEmit;
1203+
bindMaxProjectionBuffer();
1204+
}
1205+
} else {
1206+
if (
1207+
currentTransparentRenderingState !==
1208+
TransparentRenderingState.VOLUME_RENDERING
1209+
) {
1210+
renderContext.emitter = perspectivePanelEmitOIT;
1211+
bindVolumeRenderingBuffer();
1212+
}
1213+
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
1214+
currentTransparentRenderingState =
1215+
TransparentRenderingState.VOLUME_RENDERING;
11971216
}
1217+
1218+
// Two cases for volume rendering layers
1219+
// Either way, a draw call is needed first
11981220
renderLayer.draw(renderContext, attachment);
1221+
gl.enable(WebGL2RenderingContext.DEPTH_TEST);
11991222

1200-
// Copy max projection result to picking buffer
1223+
// Case 1 - No picking pass needed and not a projection layer
1224+
// we already have the color information, so we skip the max projection pass
1225+
if (!needsSecondPickingPass && !isVolumeProjectionLayer) {
1226+
continue;
1227+
}
1228+
1229+
// Case 2 - Picking will be computed from a max projection
1230+
// And a second pass may be needed to do this picking
1231+
1232+
// Copy the volume rendering picking result to the main picking buffer
12011233
// Depth testing on to combine max layers into one pick buffer via depth
12021234
bindMaxProjectionPickingBuffer();
12031235
this.maxProjectionToPickCopyHelper.draw(
@@ -1207,25 +1239,30 @@ export class PerspectivePanel extends RenderedDataPanel {
12071239
this.maxProjectionConfiguration.colorBuffers[3 /*pick*/].texture,
12081240
);
12091241

1210-
// Copy max projection color result to the transparent buffer with OIT
1211-
// Depth testing off to combine max layers into one color via blend
1212-
bindVolumeRenderingBuffer();
1213-
gl.depthMask(false);
1214-
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
1242+
// Turn back on OIT blending
12151243
gl.enable(WebGL2RenderingContext.BLEND);
12161244
gl.blendFuncSeparate(
12171245
WebGL2RenderingContext.ONE,
12181246
WebGL2RenderingContext.ONE,
12191247
WebGL2RenderingContext.ZERO,
12201248
WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA,
12211249
);
1222-
this.maxProjectionColorCopyHelper.draw(
1223-
this.maxProjectionConfiguration.colorBuffers[0 /*color*/].texture,
1224-
this.maxProjectionConfiguration.colorBuffers[1 /*depth*/].texture,
1225-
);
12261250

1227-
// Reset the max projection buffer
1251+
// Copy max projection color result to the transparent buffer with OIT
1252+
// Depth testing off to combine max layers into one color via blending
1253+
if (isVolumeProjectionLayer) {
1254+
bindVolumeRenderingBuffer();
1255+
gl.depthMask(false);
1256+
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
1257+
this.maxProjectionColorCopyHelper.draw(
1258+
this.maxProjectionConfiguration.colorBuffers[0 /*color*/].texture,
1259+
this.maxProjectionConfiguration.colorBuffers[1 /*depth*/].texture,
1260+
);
1261+
}
1262+
1263+
// Reset the max projection color, depth, and picking buffer
12281264
bindMaxProjectionBuffer();
1265+
renderContext.emitter = maxProjectionEmit;
12291266
gl.depthMask(true);
12301267
gl.clearColor(0.0, 0.0, 0.0, 0.0);
12311268
gl.clearDepth(0.0);
@@ -1234,7 +1271,7 @@ export class PerspectivePanel extends RenderedDataPanel {
12341271
WebGL2RenderingContext.DEPTH_BUFFER_BIT,
12351272
);
12361273

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

12441281
currentTransparentRenderingState =
12451282
TransparentRenderingState.MAX_PROJECTION;
1246-
} else if (renderLayer.isVolumeRendering) {
1247-
if (
1248-
currentTransparentRenderingState !==
1249-
TransparentRenderingState.VOLUME_RENDERING
1250-
) {
1251-
renderContext.emitter = perspectivePanelEmitOIT;
1252-
bindVolumeRenderingBuffer();
1253-
}
1254-
currentTransparentRenderingState =
1255-
TransparentRenderingState.VOLUME_RENDERING;
1256-
renderLayer.draw(renderContext, attachment);
12571283
}
12581284
// Draw regular transparent layers
12591285
else if (renderLayer.isTransparent) {
@@ -1271,18 +1297,18 @@ export class PerspectivePanel extends RenderedDataPanel {
12711297
}
12721298
// Copy transparent rendering result back to primary buffer.
12731299
gl.disable(WebGL2RenderingContext.DEPTH_TEST);
1274-
gl.viewport(0, 0, width, height);
1275-
this.offscreenFramebuffer.bindSingle(OffscreenTextures.COLOR);
1276-
gl.blendFunc(
1277-
WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA,
1278-
WebGL2RenderingContext.SRC_ALPHA,
1279-
);
12801300
if (hasVolumeRendering) {
1281-
this.transparencyCopyHelper.draw(
1301+
renderContext.bindFramebuffer();
1302+
this.transparentToTransparentCopyHelper.draw(
12821303
this.volumeRenderingConfiguration.colorBuffers[0].texture,
12831304
this.volumeRenderingConfiguration.colorBuffers[1].texture,
12841305
);
12851306
}
1307+
gl.blendFunc(
1308+
WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA,
1309+
WebGL2RenderingContext.SRC_ALPHA,
1310+
);
1311+
this.offscreenFramebuffer.bindSingle(OffscreenTextures.COLOR);
12861312
this.transparencyCopyHelper.draw(
12871313
transparentConfiguration.colorBuffers[0].texture,
12881314
transparentConfiguration.colorBuffers[1].texture,
@@ -1319,7 +1345,7 @@ export class PerspectivePanel extends RenderedDataPanel {
13191345
/*dppass=*/ WebGL2RenderingContext.REPLACE,
13201346
);
13211347
gl.stencilMask(2);
1322-
if (hasMaxProjection) {
1348+
if (hasVolumeRenderingPick) {
13231349
this.maxProjectionPickCopyHelper.draw(
13241350
this.maxProjectionPickConfiguration.colorBuffers[0].texture /*depth*/,
13251351
this.maxProjectionPickConfiguration.colorBuffers[1].texture /*pick*/,

0 commit comments

Comments
 (0)