Skip to content

Commit 018f297

Browse files
committed
segment color shader
1 parent e5371c4 commit 018f297

File tree

14 files changed

+273
-52
lines changed

14 files changed

+273
-52
lines changed

src/layer/annotation/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ export class AnnotationUserLayer extends Base {
770770
function makeShaderCodeWidget(layer: AnnotationUserLayer) {
771771
return new ShaderCodeWidget({
772772
shaderError: layer.annotationDisplayState.shaderError,
773-
fragmentMain: layer.annotationDisplayState.shader,
773+
fragment: layer.annotationDisplayState.shader,
774774
shaderControlState: layer.annotationDisplayState.shaderControls,
775775
});
776776
}

src/layer/image/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ export class ImageUserLayer extends Base {
461461
function makeShaderCodeWidget(layer: ImageUserLayer) {
462462
return new ShaderCodeWidget({
463463
shaderError: layer.shaderError,
464-
fragmentMain: layer.fragmentMain,
464+
fragment: layer.fragmentMain,
465465
shaderControlState: layer.shaderControlState,
466466
});
467467
}

src/layer/segmentation/index.ts

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,14 @@ import {
129129
verifyString,
130130
} from "#src/util/json.js";
131131
import { Signal } from "#src/util/signal.js";
132-
import { makeWatchableShaderError } from "#src/webgl/dynamic_shader.js";
132+
import { GLBuffer } from "#src/webgl/buffer.js";
133+
import { initializeWebGL } from "#src/webgl/context.js";
134+
import {
135+
makeTrackableFragmentMain,
136+
makeWatchableShaderError,
137+
parameterizedEmitterDependentShaderGetter,
138+
} from "#src/webgl/dynamic_shader.js";
139+
import { ShaderControlState } from "#src/webgl/shader_ui_controls.js";
133140
import type { DependentViewContext } from "#src/widget/dependent_view_widget.js";
134141
import { registerLayerShaderControlsTool } from "#src/widget/shader_controls.js";
135142

@@ -410,12 +417,22 @@ class LinkedSegmentationGroupState<
410417
}
411418
}
412419

420+
const DEFAULT_FRAGMENT_SEGMENT_COLOR = `
421+
vec4 segmentColor(vec4 color) {
422+
return color;
423+
}
424+
`;
425+
413426
class SegmentationUserLayerDisplayState implements SegmentationDisplayState {
427+
private getSegmentColorShader;
428+
414429
constructor(public layer: SegmentationUserLayer) {
415430
// Even though `SegmentationUserLayer` assigns this to its `displayState` property, redundantly
416431
// assign it here first in order to allow it to be accessed by `segmentationGroupState`.
417432
layer.displayState = this;
418433

434+
this.getSegmentColorShader = this.makeSegmentColorShaderGetter();
435+
419436
this.linkedSegmentationGroup = layer.registerDisposer(
420437
new LinkedLayerGroup(
421438
layer.manager.rootLayers,
@@ -536,6 +553,8 @@ class SegmentationUserLayerDisplayState implements SegmentationDisplayState {
536553
ignoreNullVisibleSet = new TrackableBoolean(true, true);
537554
skeletonRenderingOptions = new SkeletonRenderingOptions();
538555
shaderError = makeWatchableShaderError();
556+
fragmentSegmentColor = makeTrackableFragmentMain(DEFAULT_FRAGMENT_SEGMENT_COLOR);
557+
segmentColorShaderControlState = new ShaderControlState(this.fragmentSegmentColor);
539558
renderScaleHistogram = new RenderScaleHistogram();
540559
renderScaleTarget = trackableRenderScaleTarget(1);
541560
selectSegment: (id: bigint, pin: boolean | "toggle") => void;
@@ -551,6 +570,65 @@ class SegmentationUserLayerDisplayState implements SegmentationDisplayState {
551570
this.layer.moveToSegment(id);
552571
};
553572

573+
makeSegmentColorShaderGetter = () => {
574+
const gl = initializeWebGL(new OffscreenCanvas(1, 1));
575+
const parameters = this.segmentColorShaderControlState.builderState;
576+
return parameterizedEmitterDependentShaderGetter(this.layer, gl, {
577+
memoizeKey: `segmentColorShaderTODO`,
578+
parameters,
579+
encodeParameters: (p) => {
580+
return `${p.parseResult.code}`;
581+
},
582+
shaderError: this.layer.displayState.shaderError, // TODO can I reuse this?
583+
defineShader: (builder, shaderBuilderState) => {
584+
builder.addAttribute("highp vec4", "aVertexPosition");
585+
builder.addUniform("highp vec4", "uColor");
586+
builder.addUniform("highp uvec2", "uID");
587+
builder.addVarying("highp vec4", "vColor");
588+
builder.addVertexCode(shaderBuilderState.parseResult.code);
589+
builder.addVertexMain(`
590+
gl_Position = aVertexPosition;
591+
vColor = segmentColor(uColor);
592+
`);
593+
builder.addOutputBuffer("vec4", "out_fragColor", 0);
594+
builder.setFragmentMain("out_fragColor = vColor;");
595+
},
596+
});
597+
};
598+
599+
context = () => {}; // TEMP how to get rid of this?
600+
601+
getShaderSegmentColor = (id: bigint, color: Float32Array) => {
602+
id; // TODO
603+
const { shader, parameters } = this.getSegmentColorShader(this.context);
604+
if (shader === null) return color;
605+
parameters;
606+
shader.bind();
607+
const { gl } = shader;
608+
const positionBuffer = GLBuffer.fromData(
609+
gl,
610+
new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]),
611+
);
612+
positionBuffer.bindToVertexAttrib(shader.attribute("aVertexPosition"), 2);
613+
gl.uniform4fv(shader.uniform("uColor"), color);
614+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
615+
const data = new Uint8Array(4);
616+
// TODO can I read straight to float?
617+
gl.readPixels(
618+
0,
619+
0,
620+
1,
621+
1,
622+
WebGL2RenderingContext.RGBA,
623+
WebGL2RenderingContext.UNSIGNED_BYTE,
624+
data,
625+
);
626+
for (let i = 0; i < data.length; i++) {
627+
color[i] = data[i] / 255.0;
628+
}
629+
return color;
630+
};
631+
554632
linkedSegmentationGroup: LinkedLayerGroup;
555633
linkedSegmentationColorGroup: LinkedLayerGroup;
556634
originalSegmentationGroupState: SegmentationUserLayerGroupState;
@@ -689,6 +767,9 @@ export class SegmentationUserLayer extends Base {
689767
this.displayState.linkedSegmentationGroup.changed.add(() =>
690768
this.updateDataSubsourceActivations(),
691769
);
770+
this.displayState.fragmentSegmentColor.changed.add(
771+
this.specificationChanged.dispatch,
772+
);
692773
this.tabs.add("rendering", {
693774
label: "Render",
694775
order: -100,
@@ -996,6 +1077,9 @@ export class SegmentationUserLayer extends Base {
9961077
this.displayState.ignoreNullVisibleSet.restoreState(
9971078
specification[json_keys.IGNORE_NULL_VISIBLE_SET_JSON_KEY],
9981079
);
1080+
this.displayState.fragmentSegmentColor.restoreState(
1081+
specification[json_keys.SHADER_JSON_KEY],
1082+
);
9991083

10001084
const { skeletonRenderingOptions } = this.displayState;
10011085
skeletonRenderingOptions.restoreState(
@@ -1066,6 +1150,7 @@ export class SegmentationUserLayer extends Base {
10661150
this.displayState.renderScaleTarget.toJSON();
10671151
x[json_keys.CROSS_SECTION_RENDER_SCALE_JSON_KEY] =
10681152
this.sliceViewRenderScaleTarget.toJSON();
1153+
x[json_keys.SHADER_JSON_KEY] = this.displayState.fragmentSegmentColor.toJSON();
10691154

10701155
const { linkedSegmentationGroup, linkedSegmentationColorGroup } =
10711156
this.displayState;
@@ -1365,6 +1450,7 @@ export class SegmentationUserLayer extends Base {
13651450

13661451
const visibleSegments = [...visibleSegmentsSet];
13671452
const colors = visibleSegments.map((id) => {
1453+
// here we can do a batch get of colors using the segment color shader instead of one at a time
13681454
const color = getCssColor(getBaseObjectColor(displayState, id));
13691455
return { color, id };
13701456
});
@@ -1401,6 +1487,10 @@ registerLayerTypeDetector((subsource) => {
14011487
return undefined;
14021488
});
14031489

1490+
registerLayerShaderControlsTool(SegmentationUserLayer, (layer) => ({
1491+
shaderControlState: layer.displayState.segmentColorShaderControlState,
1492+
}));
1493+
14041494
registerLayerShaderControlsTool(
14051495
SegmentationUserLayer,
14061496
(layer) => ({

src/layer/segmentation/json_keys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const COLOR_SEED_JSON_KEY = "colorSeed";
1414
export const SEGMENT_STATED_COLORS_JSON_KEY = "segmentColors";
1515
export const MESH_RENDER_SCALE_JSON_KEY = "meshRenderScale";
1616
export const CROSS_SECTION_RENDER_SCALE_JSON_KEY = "crossSectionRenderScale";
17+
export const SHADER_JSON_KEY = "shader";
1718
export const SKELETON_RENDERING_JSON_KEY = "skeletonRendering";
1819
export const SKELETON_SHADER_JSON_KEY = "skeletonShader";
1920
export const SKELETON_CODE_VISIBLE_KEY = "codeVisible";

src/layer/segmentation/style.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
.neuroglancer-segmentation-rendering-tab .neuroglancer-shader-code-widget {
2424
height: 6em;
25+
resize: vertical;
2526
}
2627

2728
.neuroglancer-segmentation-dropdown-skeleton-shader-header {

src/layer/single_mesh/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
} from "#src/widget/shader_controls.js";
4848
import { Tab } from "#src/widget/tab_view.js";
4949

50-
const SHADER_JSON_KEY = "shader";
50+
const SEGMENT_COLOR_SHADER_JSON_KEY = "segmentColorShader";
5151
const SHADER_CONTROLS_JSON_KEY = "shaderControls";
5252
const CODE_VISIBLE_KEY = "codeVisible";
5353

@@ -82,7 +82,7 @@ export class SingleMeshUserLayer extends UserLayer {
8282
restoreState(specification: any) {
8383
super.restoreState(specification);
8484
this.codeVisible.restoreState(specification[CODE_VISIBLE_KEY]);
85-
this.displayState.fragmentMain.restoreState(specification[SHADER_JSON_KEY]);
85+
this.displayState.fragmentMain.restoreState(specification[SEGMENT_COLOR_SHADER_JSON_KEY]);
8686
this.displayState.shaderControlState.restoreState(
8787
specification[SHADER_CONTROLS_JSON_KEY],
8888
);
@@ -121,7 +121,7 @@ export class SingleMeshUserLayer extends UserLayer {
121121

122122
toJSON() {
123123
const x = super.toJSON();
124-
x[SHADER_JSON_KEY] = this.displayState.fragmentMain.toJSON();
124+
x[SEGMENT_COLOR_SHADER_JSON_KEY] = this.displayState.fragmentMain.toJSON();
125125
x[SHADER_CONTROLS_JSON_KEY] = this.displayState.shaderControlState.toJSON();
126126
x[CODE_VISIBLE_KEY] = this.codeVisible.toJSON();
127127
return x;
@@ -133,7 +133,7 @@ export class SingleMeshUserLayer extends UserLayer {
133133

134134
function makeShaderCodeWidget(layer: SingleMeshUserLayer) {
135135
return new ShaderCodeWidget({
136-
fragmentMain: layer.displayState.fragmentMain,
136+
fragment: layer.displayState.fragmentMain,
137137
shaderError: layer.displayState.shaderError,
138138
shaderControlState: layer.displayState.shaderControlState,
139139
});

src/mesh/frontend.ts

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import type {
4343
import { PerspectiveViewRenderLayer } from "#src/perspective_view/render_layer.js";
4444
import type { ThreeDimensionalRenderLayerAttachmentState } from "#src/renderlayer.js";
4545
import { update3dRenderLayerAttachment } from "#src/renderlayer.js";
46+
import { SegmentColorShaderManager } from "#src/segment_color.js";
4647
import {
4748
forEachVisibleSegment,
4849
getObjectKey,
@@ -68,8 +69,17 @@ import {
6869
import * as matrix from "#src/util/matrix.js";
6970
import { GLBuffer } from "#src/webgl/buffer.js";
7071
import type { GL } from "#src/webgl/context.js";
71-
import { parameterizedEmitterDependentShaderGetter } from "#src/webgl/dynamic_shader.js";
72+
import type { WatchableShaderError } from "#src/webgl/dynamic_shader.js";
73+
import {
74+
parameterizedEmitterDependentShaderGetter,
75+
shaderCodeWithLineDirective,
76+
} from "#src/webgl/dynamic_shader.js";
7277
import type { ShaderBuilder, ShaderProgram } from "#src/webgl/shader.js";
78+
import type { ShaderControlState } from "#src/webgl/shader_ui_controls.js";
79+
import {
80+
addControlsToBuilder,
81+
setControlsInShader,
82+
} from "#src/webgl/shader_ui_controls.js";
7383
import type { RPC } from "#src/worker_rpc.js";
7484
import { registerSharedObjectOwner } from "#src/worker_rpc.js";
7585

@@ -349,16 +359,33 @@ export class MeshShaderManager {
349359
}
350360

351361
makeGetter(layer: RefCounted & { gl: GL; displayState: MeshDisplayState }) {
352-
const silhouetteRenderingEnabled = layer.registerDisposer(
362+
const parameters = layer.registerDisposer(
353363
makeCachedDerivedWatchableValue(
354-
(x) => x > 0,
355-
[layer.displayState.silhouetteRendering],
364+
(silhouetteRendering, shaderBuilderState) => {
365+
return {
366+
silhouetteRenderingEnabled: silhouetteRendering > 0,
367+
shaderBuilderState,
368+
};
369+
},
370+
[
371+
layer.displayState.silhouetteRendering,
372+
layer.displayState.segmentColorShaderControlState.builderState,
373+
],
356374
),
357375
);
376+
358377
return parameterizedEmitterDependentShaderGetter(layer, layer.gl, {
359378
memoizeKey: `mesh/MeshShaderManager/${this.fragmentRelativeVertices}/${this.vertexPositionFormat}`,
360-
parameters: silhouetteRenderingEnabled,
361-
defineShader: (builder, silhouetteRenderingEnabled) => {
379+
parameters,
380+
encodeParameters: (p) => {
381+
return `${p.silhouetteRenderingEnabled}/${p.shaderBuilderState.parseResult.code}`;
382+
},
383+
shaderError: layer.displayState.shaderError,
384+
defineShader: (
385+
builder,
386+
{ silhouetteRenderingEnabled, shaderBuilderState },
387+
) => {
388+
addControlsToBuilder(shaderBuilderState, builder);
362389
this.vertexPositionHandler.defineShader(builder);
363390
builder.addAttribute("highp vec2", "aVertexNormal");
364391
builder.addVarying("highp vec4", "vColor");
@@ -367,6 +394,7 @@ export class MeshShaderManager {
367394
builder.addUniform("highp mat3", "uNormalMatrix");
368395
builder.addUniform("highp mat4", "uModelViewProjection");
369396
builder.addUniform("highp uint", "uPickID");
397+
370398
if (silhouetteRenderingEnabled) {
371399
builder.addUniform("highp float", "uSilhouettePower");
372400
}
@@ -393,22 +421,32 @@ vec3 origNormal = decodeNormalOctahedronSnorm8(aVertexNormal);
393421
vec3 normal = normalize(uNormalMatrix * (normalMultiplier * origNormal));
394422
float absCosAngle = abs(dot(normal, uLightDirection.xyz));
395423
float lightingFactor = absCosAngle + uLightDirection.w;
396-
vColor = vec4(lightingFactor * uColor.rgb, uColor.a);
424+
vColor = uColor;
425+
vColor = segmentColor(vColor);
426+
vColor = vec4(lightingFactor * vColor.rgb, vColor.a);
397427
`;
398428
if (silhouetteRenderingEnabled) {
399429
vertexMain += `
400430
vColor *= pow(1.0 - absCosAngle, uSilhouettePower);
401431
`;
402432
}
403433
builder.setVertexMain(vertexMain);
434+
const shaderManager = new SegmentColorShaderManager("getColor");
435+
shaderManager.defineShader(builder);
404436
builder.setFragmentMain("emit(vColor, uPickID);");
437+
const segmentColor = shaderCodeWithLineDirective(
438+
shaderBuilderState.parseResult.code,
439+
);
440+
builder.addVertexCode(segmentColor + "\n");
405441
},
406442
});
407443
}
408444
}
409445

410446
export interface MeshDisplayState extends SegmentationDisplayState3D {
411447
silhouetteRendering: WatchableValueInterface<number>;
448+
segmentColorShaderControlState: ShaderControlState;
449+
shaderError: WatchableShaderError;
412450
}
413451

414452
export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRenderLayerAttachmentState> {
@@ -432,6 +470,14 @@ export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRender
432470
this.registerDisposer(
433471
displayState.silhouetteRendering.changed.add(this.redrawNeeded.dispatch),
434472
);
473+
this.registerDisposer(
474+
displayState.segmentColorShaderControlState.changed.add(this.redrawNeeded.dispatch),
475+
);
476+
this.registerDisposer(
477+
displayState.segmentColorShaderControlState.parseResult.changed.add(
478+
this.redrawNeeded.dispatch,
479+
),
480+
);
435481

436482
const sharedObject = (this.backend = this.registerDisposer(
437483
new SegmentationLayerSharedObject(
@@ -490,11 +536,17 @@ export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRender
490536
if (modelMatrix === undefined) {
491537
return;
492538
}
493-
const { shader } = this.getShader(renderContext.emitter);
539+
const { shader, parameters } = this.getShader(renderContext.emitter);
494540
if (shader === null) return;
495541
shader.bind();
496542
meshShaderManager.beginLayer(gl, shader, renderContext, this.displayState);
497543
meshShaderManager.beginModel(gl, shader, renderContext, modelMatrix);
544+
setControlsInShader(
545+
gl,
546+
shader,
547+
this.displayState.segmentColorShaderControlState,
548+
parameters.shaderBuilderState.parseResult.controls,
549+
);
498550

499551
const manifestChunks = this.source.chunks;
500552

0 commit comments

Comments
 (0)