Skip to content

Commit

Permalink
feat: option to hide cross section background in 3D slice views (#663)
Browse files Browse the repository at this point in the history
  • Loading branch information
seankmartin authored Feb 24, 2025
1 parent 0534765 commit f2074d7
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 24 deletions.
4 changes: 4 additions & 0 deletions docs/json_schema/viewer_state.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ properties:
type: boolean
title: "Indicates whether to show cross sections in the 3-d view of `~DataPanelLayoutType.4panel` and `~DataPanelLayoutType.4panel-alt` layouts."
default: true
hideCrossSectionBackground3D:
type: boolean
title: "Indicates whether to hide the background of cross-section views in the 3-d view of `~DataPanelLayoutType.4panel` and `~DataPanelLayoutType.4panel-alt` layouts."
default: false
gpuMemoryLimit:
type: integer
title: "GPU memory limit, in bytes."
Expand Down
3 changes: 3 additions & 0 deletions python/neuroglancer/viewer_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,9 @@ class ViewerState(JsonObjectWrapper):
"projectionOrientation", optional(array_wrapper(np.float32, 4))
)
show_slices = showSlices = wrapped_property("showSlices", optional(bool, True))
hide_cross_section_background_3d = hideCrossSectionBackground3D = wrapped_property(
"hideCrossSectionBackground3D", optional(bool, False)
)
show_axis_lines = showAxisLines = wrapped_property(
"showAxisLines", optional(bool, True)
)
Expand Down
2 changes: 2 additions & 0 deletions src/data_panel_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export interface ViewerUIState
inputEventBindings: InputEventBindings;
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackground3D: TrackableBoolean;
}

export interface DataDisplayLayout extends RefCounted {
Expand Down Expand Up @@ -181,6 +182,7 @@ export function getCommonViewerState(viewer: ViewerUIState) {
selectedLayer: viewer.selectedLayer,
visibility: viewer.visibility,
scaleBarOptions: viewer.scaleBarOptions,
hideCrossSectionBackground3D: viewer.hideCrossSectionBackground3D,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/layer_group_viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export interface LayerGroupViewerState {
visibleLayerRoles: WatchableSet<RenderLayerRole>;
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackground3D: TrackableBoolean;
}

export interface LayerGroupViewerOptions {
Expand Down Expand Up @@ -357,6 +358,9 @@ export class LayerGroupViewer extends RefCounted {
get enableAdaptiveDownsampling() {
return this.viewerState.enableAdaptiveDownsampling;
}
get hideCrossSectionBackground3D() {
return this.viewerState.hideCrossSectionBackground3D;
}
get showScaleBar() {
return this.viewerState.showScaleBar;
}
Expand Down
1 change: 1 addition & 0 deletions src/layer_groups_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ function getCommonViewerState(viewer: Viewer) {
velocity: viewer.velocity.addRef(),
crossSectionBackgroundColor: viewer.crossSectionBackgroundColor,
perspectiveViewBackgroundColor: viewer.perspectiveViewBackgroundColor,
hideCrossSectionBackground3D: viewer.hideCrossSectionBackground3D,
};
}

Expand Down
20 changes: 16 additions & 4 deletions src/perspective_view/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface PerspectiveViewerState extends RenderedDataViewerState {
showSliceViewsCheckbox?: boolean;
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackground3D: TrackableBoolean;
rpc: RPC;
}

Expand Down Expand Up @@ -270,6 +271,7 @@ class PerspectiveViewState extends PerspectiveViewStateBase {

export class PerspectivePanel extends RenderedDataPanel {
declare viewer: PerspectiveViewerState;
sliceViewRenderHelper: SliceViewRenderHelper;

projectionParameters: Owned<DerivedProjectionParameters>;

Expand Down Expand Up @@ -321,10 +323,6 @@ export class PerspectivePanel extends RenderedDataPanel {
);

private axesLineHelper = this.registerDisposer(AxesLineHelper.get(this.gl));
sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(this.gl, perspectivePanelEmit),
);

protected offscreenFramebuffer = this.registerDisposer(
new FramebufferConfiguration(this.gl, {
colorBuffers: [
Expand Down Expand Up @@ -406,6 +404,15 @@ export class PerspectivePanel extends RenderedDataPanel {
viewer: PerspectiveViewerState,
) {
super(context, element, viewer);
this.sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(
this.gl,
perspectivePanelEmit,
this.viewer,
true /*perspectivePanel*/,
),
);

this.projectionParameters = this.registerDisposer(
new DerivedProjectionParameters({
navigationState: this.navigationState,
Expand Down Expand Up @@ -580,6 +587,11 @@ export class PerspectivePanel extends RenderedDataPanel {
this.registerDisposer(
viewer.wireFrame.changed.add(() => this.scheduleRedraw()),
);
this.registerDisposer(
viewer.hideCrossSectionBackground3D.changed.add(() =>
this.scheduleRedraw(),
),
);
this.sliceViews.changed.add(() => this.scheduleRedraw());
}

Expand Down
92 changes: 75 additions & 17 deletions src/sliceview/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
DisplayDimensionRenderInfo,
NavigationState,
} from "#src/navigation_state.js";
import type { PerspectiveViewerState } from "#src/perspective_view/panel.js";
import { updateProjectionParametersFromInverseViewAndProjection } from "#src/projection_parameters.js";
import type {
ChunkDisplayTransformParameters,
Expand Down Expand Up @@ -60,6 +61,7 @@ import {
SliceViewProjectionParameters,
} from "#src/sliceview/base.js";
import { ChunkLayout } from "#src/sliceview/chunk_layout.js";
import type { SliceViewerState } from "#src/sliceview/panel.js";
import { SliceViewRenderLayer } from "#src/sliceview/renderlayer.js";
import type { WatchableValueInterface } from "#src/trackable_value.js";
import type { Borrowed, Disposer, Owned } from "#src/util/disposable.js";
Expand All @@ -72,6 +74,8 @@ import type { ProgressOptions } from "#src/util/progress_listener.js";
import { NullarySignal } from "#src/util/signal.js";
import { withSharedVisibility } from "#src/visibility_priority/frontend.js";
import type { GL } from "#src/webgl/context.js";
import type { ParameterizedContextDependentShaderGetter } from "#src/webgl/dynamic_shader.js";
import { parameterizedContextDependentShaderGetter } from "#src/webgl/dynamic_shader.js";
import type { HistogramSpecifications } from "#src/webgl/empirical_cdf.js";
import { TextureHistogramGenerator } from "#src/webgl/empirical_cdf.js";
import type { TextureBuffer } from "#src/webgl/offscreen.js";
Expand All @@ -80,8 +84,7 @@ import {
FramebufferConfiguration,
makeTextureBuffers,
} from "#src/webgl/offscreen.js";
import type { ShaderModule, ShaderProgram } from "#src/webgl/shader.js";
import { ShaderBuilder } from "#src/webgl/shader.js";
import type { ShaderModule, ShaderBuilder } from "#src/webgl/shader.js";
import { getSquareCornersBuffer } from "#src/webgl/square_corners_buffer.js";
import type { RPC } from "#src/worker_rpc.js";
import { registerSharedObjectOwner } from "#src/worker_rpc.js";
Expand Down Expand Up @@ -721,41 +724,83 @@ export class SliceViewChunk extends Chunk {
*/
export class SliceViewRenderHelper extends RefCounted {
private copyVertexPositionsBuffer;
private shader: ShaderProgram;

private textureCoordinateAdjustment = new Float32Array(4);
private shaderGetter: ParameterizedContextDependentShaderGetter<
{ emitter: ShaderModule; isProjection: boolean },
boolean
>;

constructor(
public gl: GL,
defineShader(
builder: ShaderBuilder,
hideTransparent: boolean,
isProjection: boolean,
emitter: ShaderModule,
) {
super();
this.copyVertexPositionsBuffer = getSquareCornersBuffer(this.gl);

const builder = new ShaderBuilder(gl);
builder.addVarying("vec2", "vTexCoord");
builder.addUniform("sampler2D", "uSampler");
builder.addInitializer((shader) => {
gl.uniform1i(shader.uniform("uSampler"), 0);
this.gl.uniform1i(shader.uniform("uSampler"), 0);
});
builder.addUniform("vec4", "uColorFactor");
builder.addUniform("vec4", "uBackgroundColor");
builder.addUniform("mat4", "uProjectionMatrix");
builder.addUniform("vec4", "uTextureCoordinateAdjustment");
builder.require(emitter);
builder.setFragmentMain(`
const glsl_fragmentMainStart = `
vec4 sampledColor = texture(uSampler, vTexCoord);
if (sampledColor.a == 0.0) {
if (sampledColor.a == 0.0) {`;
let glsl_fragmentMainEnd: string;
if (hideTransparent && isProjection) {
glsl_fragmentMainEnd = `
discard;
}
else {
emit(sampledColor * uColorFactor, 0u);
}
`;
} else {
glsl_fragmentMainEnd = `
sampledColor = uBackgroundColor;
}
emit(sampledColor * uColorFactor, 0u);
`);
`;
}
builder.setFragmentMain(`${glsl_fragmentMainStart}${glsl_fragmentMainEnd}`);
builder.addAttribute("vec4", "aVertexPosition");
builder.setVertexMain(`
vTexCoord = uTextureCoordinateAdjustment.xy + 0.5 * (aVertexPosition.xy + 1.0) * uTextureCoordinateAdjustment.zw;
gl_Position = uProjectionMatrix * aVertexPosition;
`);
this.shader = this.registerDisposer(builder.build());
}

constructor(
public gl: GL,
private emitter: ShaderModule,
private viewer: SliceViewerState | PerspectiveViewerState,
private isProjection: boolean,
) {
super();

this.copyVertexPositionsBuffer = getSquareCornersBuffer(this.gl);
this.shaderGetter = parameterizedContextDependentShaderGetter(
this,
this.gl,
{
memoizeKey: "sliceview/SliceViewRenderHelper",
parameters: this.viewer.hideCrossSectionBackground3D,
getContextKey: ({ emitter, isProjection }) =>
`${getObjectId(emitter)}${isProjection}`,
defineShader: (builder, context, hideTransparent) => {
this.defineShader(
builder,
hideTransparent,
context.isProjection,
context.emitter,
);
},
},
);
}

draw(
Expand All @@ -768,11 +813,19 @@ gl_Position = uProjectionMatrix * aVertexPosition;
xEnd: number,
yEnd: number,
) {
const { gl, shader, textureCoordinateAdjustment } = this;
const { gl, textureCoordinateAdjustment } = this;
textureCoordinateAdjustment[0] = xStart;
textureCoordinateAdjustment[1] = yStart;
textureCoordinateAdjustment[2] = xEnd - xStart;
textureCoordinateAdjustment[3] = yEnd - yStart;
const shaderResult = this.shaderGetter({
emitter: this.emitter,
isProjection: this.isProjection,
});
const shader = shaderResult.shader;
if (shader === null) {
throw new Error("Shader compilation failed in SliceViewRenderHelper.");
}
shader.bind();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
Expand Down Expand Up @@ -801,10 +854,15 @@ gl_Position = uProjectionMatrix * aVertexPosition;
gl.bindTexture(gl.TEXTURE_2D, null);
}

static get(gl: GL, emitter: ShaderModule) {
static get(
gl: GL,
emitter: ShaderModule,
viewer: SliceViewerState | PerspectiveViewerState,
isProjection: boolean,
) {
return gl.memoize.get(
`sliceview/SliceViewRenderHelper:${getObjectId(emitter)}`,
() => new SliceViewRenderHelper(gl, emitter),
() => new SliceViewRenderHelper(gl, emitter, viewer, isProjection),
);
}
}
Expand Down
16 changes: 13 additions & 3 deletions src/sliceview/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface SliceViewerState extends RenderedDataViewerState {
wireFrame: TrackableBoolean;
scaleBarOptions: TrackableScaleBarOptions;
crossSectionBackgroundColor: TrackableRGB;
hideCrossSectionBackground3D: TrackableBoolean;
}

export enum OffscreenTextures {
Expand Down Expand Up @@ -101,11 +102,10 @@ const tempVec4 = vec4.create();

export class SliceViewPanel extends RenderedDataPanel {
declare viewer: SliceViewerState;
private sliceViewRenderHelper;

private axesLineHelper = this.registerDisposer(AxesLineHelper.get(this.gl));
private sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(this.gl, sliceViewPanelEmitColor),
);

private colorFactor = vec4.fromValues(1, 1, 1, 1);
private pickIDs = new PickIDManager();

Expand Down Expand Up @@ -167,6 +167,16 @@ export class SliceViewPanel extends RenderedDataPanel {
viewer: SliceViewerState,
) {
super(context, element, viewer);

this.sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(
this.gl,
sliceViewPanelEmitColor,
this.viewer,
false /*sliceViewPanel*/,
),
);

viewer.wireFrame.changed.add(() => this.scheduleRedraw());
registerActionListener(
element,
Expand Down
4 changes: 4 additions & 0 deletions src/ui/viewer_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ export class ViewerSettingsPanel extends SidePanel {
addCheckbox("Show axis lines", viewer.showAxisLines);
addCheckbox("Show scale bar", viewer.showScaleBar);
addCheckbox("Show cross sections in 3-d", viewer.showPerspectiveSliceViews);
addCheckbox(
"Hide sections background 3-d",
viewer.hideCrossSectionBackground3D,
);
addCheckbox("Show default annotations", viewer.showDefaultAnnotations);
addCheckbox(
"Show chunk statistics",
Expand Down
5 changes: 5 additions & 0 deletions src/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ class TrackableViewerState extends CompoundTrackable {
this.add("showDefaultAnnotations", viewer.showDefaultAnnotations);

this.add("showSlices", viewer.showPerspectiveSliceViews);
this.add(
"hideCrossSectionBackground3D",
viewer.hideCrossSectionBackground3D,
);
this.add(
"gpuMemoryLimit",
viewer.dataContext.chunkQueueManager.capacities.gpuMemory.sizeLimit,
Expand Down Expand Up @@ -416,6 +420,7 @@ export class Viewer extends RefCounted implements ViewerState {
enableAdaptiveDownsampling = new TrackableBoolean(true, true);
showScaleBar = new TrackableBoolean(true, true);
showPerspectiveSliceViews = new TrackableBoolean(true, true);
hideCrossSectionBackground3D = new TrackableBoolean(false, false);
visibleLayerRoles = allRenderLayerRoles();
showDefaultAnnotations = new TrackableBoolean(true, true);
crossSectionBackgroundColor = new TrackableRGB(
Expand Down

0 comments on commit f2074d7

Please sign in to comment.