Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ The "frontend" UI thread handles user actions and rendering, while the "backend"
- [Compressed segmentation format](src/sliceview/compressed_segmentation/README.md)
- [Data chunk management](src/chunk_manager/)
- [On-GPU hashing](src/gpu_hash/)
- [Volume rendering](src/volume_rendering/)
- [Screen-space ambient occlusion (SSAO) rendering](src/ssao/)

# Building

Expand Down
5 changes: 5 additions & 0 deletions python/neuroglancer/viewer_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1916,6 +1916,11 @@ class ViewerState(JsonObjectWrapper):
"showAxisLines", optional(bool, True)
)
wire_frame = wireFrame = wrapped_property("wireFrame", optional(bool, False))
ssao = wrapped_property("ssao", optional(bool, False))
ssao_intensity = ssaoIntensity = wrapped_property(
"ssaoIntensity", optional(float, 1.8)
)
ssao_radius = ssaoRadius = wrapped_property("ssaoRadius", optional(float, 0.05))
enable_adaptive_downsampling = enableAdaptiveDownsampling = wrapped_property(
"enableAdaptiveDownsampling", optional(bool, True)
)
Expand Down
6 changes: 6 additions & 0 deletions src/data_panel_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ export interface ViewerUIState
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackground3D: TrackableBoolean;
ssao: WatchableValueInterface<boolean>;
ssaoIntensity: WatchableValueInterface<number>;
ssaoRadius: WatchableValueInterface<number>;
pickRadius: TrackableValue<number>;
}

Expand Down Expand Up @@ -184,6 +187,9 @@ export function getCommonViewerState(viewer: ViewerUIState) {
visibility: viewer.visibility,
scaleBarOptions: viewer.scaleBarOptions,
hideCrossSectionBackground3D: viewer.hideCrossSectionBackground3D,
ssao: viewer.ssao,
ssaoIntensity: viewer.ssaoIntensity,
ssaoRadius: viewer.ssaoRadius,
pickRadius: viewer.pickRadius,
};
}
Expand Down
12 changes: 12 additions & 0 deletions src/layer_group_viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ export interface LayerGroupViewerState {
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackground3D: TrackableBoolean;
ssao: WatchableValueInterface<boolean>;
ssaoIntensity: WatchableValueInterface<number>;
ssaoRadius: WatchableValueInterface<number>;
pickRadius: TrackableValue<number>;
}

Expand Down Expand Up @@ -391,6 +394,15 @@ export class LayerGroupViewer extends RefCounted {
get scaleBarOptions() {
return this.viewerState.scaleBarOptions;
}
get ssao() {
return this.viewerState.ssao;
}
get ssaoIntensity() {
return this.viewerState.ssaoIntensity;
}
get ssaoRadius() {
return this.viewerState.ssaoRadius;
}
layerPanel: LayerBar | undefined;
layout: DataPanelLayoutContainer;
toolBinder: LocalToolBinder<this>;
Expand Down
3 changes: 3 additions & 0 deletions src/layer_groups_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ function getCommonViewerState(viewer: Viewer) {
crossSectionBackgroundColor: viewer.crossSectionBackgroundColor,
perspectiveViewBackgroundColor: viewer.perspectiveViewBackgroundColor,
hideCrossSectionBackground3D: viewer.hideCrossSectionBackground3D,
ssao: viewer.ssao,
ssaoIntensity: viewer.ssaoIntensity,
ssaoRadius: viewer.ssaoRadius,
pickRadius: viewer.uiConfiguration.pickRadius,
};
}
Expand Down
49 changes: 47 additions & 2 deletions src/mesh/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
validateOctree,
} from "#src/mesh/multiscale.js";
import type { PerspectivePanel } from "#src/perspective_view/panel.js";
import { perspectivePanelEmitWithNormals } from "#src/perspective_view/panel.js";
import type {
PerspectiveViewReadyRenderContext,
PerspectiveViewRenderContext,
Expand Down Expand Up @@ -294,6 +295,16 @@ export class MeshShaderManager {
mat3.invert(tempMat3, tempMat3);
mat3.transpose(tempMat3, tempMat3);
gl.uniformMatrix3fv(shader.uniform("uNormalMatrix"), false, tempMat3);
if (renderContext.emitNormals) {
// Combined model→view normal transform for SSAO. Uses `modelMat`
// directly, bypassing `uNormalMatrix`'s `canonicalVoxelFactors` scaling,
// which gives wrong oblique normals on anisotropic data.
mat4.multiply(tempMat4, projectionParameters.viewMatrix, modelMat);
mat4.invert(tempMat4, tempMat4);
mat3FromMat4(tempMat3, tempMat4);
mat3.transpose(tempMat3, tempMat3);
gl.uniformMatrix3fv(shader.uniform("uViewNormalMatrix"), false, tempMat3);
}
}

drawFragmentHelper(
Expand Down Expand Up @@ -358,7 +369,8 @@ export class MeshShaderManager {
return parameterizedEmitterDependentShaderGetter(layer, layer.gl, {
memoizeKey: `mesh/MeshShaderManager/${this.fragmentRelativeVertices}/${this.vertexPositionFormat}`,
parameters: silhouetteRenderingEnabled,
defineShader: (builder, silhouetteRenderingEnabled) => {
defineShader: (builder, silhouetteRenderingEnabled, _, emitter) => {
const emitNormals = emitter === perspectivePanelEmitWithNormals;
this.vertexPositionHandler.defineShader(builder);
builder.addAttribute("highp vec2", "aVertexNormal");
builder.addVarying("highp vec4", "vColor");
Expand All @@ -367,6 +379,10 @@ export class MeshShaderManager {
builder.addUniform("highp mat3", "uNormalMatrix");
builder.addUniform("highp mat4", "uModelViewProjection");
builder.addUniform("highp uint", "uPickID");
if (emitNormals) {
builder.addVarying("highp vec3", "vViewNormal");
builder.addUniform("highp mat3", "uViewNormalMatrix");
}
if (silhouetteRenderingEnabled) {
builder.addUniform("highp float", "uSilhouettePower");
}
Expand Down Expand Up @@ -395,13 +411,24 @@ float absCosAngle = abs(dot(normal, uLightDirection.xyz));
float lightingFactor = absCosAngle + uLightDirection.w;
vColor = vec4(lightingFactor * uColor.rgb, uColor.a);
`;
if (emitNormals) {
// Bypass `normal` (post-uNormalMatrix); `uViewNormalMatrix`
// already encodes the full model→view transform.
vertexMain += `
vViewNormal = normalize(uViewNormalMatrix * (normalMultiplier * origNormal));
`;
}
if (silhouetteRenderingEnabled) {
vertexMain += `
vColor *= pow(1.0 - absCosAngle, uSilhouettePower);
`;
}
builder.setVertexMain(vertexMain);
builder.setFragmentMain("emit(vColor, uPickID);");
builder.setFragmentMain(
emitNormals
? "emit(vColor, uPickID, vViewNormal);"
: "emit(vColor, uPickID);",
);
},
});
}
Expand Down Expand Up @@ -516,6 +543,15 @@ export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRender
++presentChunks;
if (renderContext.emitColor) {
meshShaderManager.setColor(gl, shader, color!);
if (renderContext.emitNormals) {
const isHighlighted =
displayState.hoverHighlight.value &&
displayState.segmentSelectionState.isSelected(objectId);
gl.uniform1f(
shader.uniform("uHighlighted"),
isHighlighted ? 1.0 : 0.0,
);
}
}
if (renderContext.emitPickID) {
meshShaderManager.setPickID(gl, shader, pickIndex!);
Expand Down Expand Up @@ -896,6 +932,15 @@ export class MultiscaleMeshLayer extends PerspectiveViewRenderLayer<ThreeDimensi
}
if (renderContext.emitColor) {
meshShaderManager.setColor(gl, shader, color!);
if (renderContext.emitNormals) {
const isHighlighted =
displayState.hoverHighlight.value &&
displayState.segmentSelectionState.isSelected(objectId);
gl.uniform1f(
shader.uniform("uHighlighted"),
isHighlighted ? 1.0 : 0.0,
);
}
}
if (renderContext.emitPickID) {
meshShaderManager.setPickID(gl, shader, pickIndex!);
Expand Down
Loading
Loading