Skip to content

Commit f07b96e

Browse files
committed
segment color shader
1 parent e5371c4 commit f07b96e

File tree

13 files changed

+305
-44
lines changed

13 files changed

+305
-44
lines changed

src/layer/segmentation/index.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,16 @@ 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 {
133+
makeTrackableFragmentMain,
134+
makeWatchableShaderError,
135+
parameterizedEmitterDependentShaderGetter,
136+
} from "#src/webgl/dynamic_shader.js";
133137
import type { DependentViewContext } from "#src/widget/dependent_view_widget.js";
134138
import { registerLayerShaderControlsTool } from "#src/widget/shader_controls.js";
139+
import { ShaderControlState } from "#src/webgl/shader_ui_controls.js";
140+
import { initializeWebGL } from "#src/webgl/context.js";
141+
import { GLBuffer } from "#src/webgl/buffer.js";
135142

136143
const MAX_LAYER_BAR_UI_INDICATOR_COLORS = 6;
137144

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

420+
const DEFAULT_FRAGMENT_MAIN = `
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+
fragmentMain = makeTrackableFragmentMain(DEFAULT_FRAGMENT_MAIN);
557+
shaderControlState = new ShaderControlState(this.fragmentMain);
539558
renderScaleHistogram = new RenderScaleHistogram();
540559
renderScaleTarget = trackableRenderScaleTarget(1);
541560
selectSegment: (id: bigint, pin: boolean | "toggle") => void;
@@ -551,6 +570,67 @@ 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.shaderControlState.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(
589+
shaderBuilderState.parseResult.code
590+
);
591+
builder.addVertexMain(`
592+
gl_Position = aVertexPosition;
593+
vColor = segmentColor(uColor);
594+
`);
595+
builder.addOutputBuffer("vec4", "out_fragColor", 0);
596+
builder.setFragmentMain("out_fragColor = vColor;");
597+
},
598+
});
599+
};
600+
601+
context = () => {}; // TEMP how to get rid of this?
602+
603+
getShaderSegmentColor = (id: bigint, color: Float32Array) => {
604+
id; // TODO
605+
const { shader, parameters } = this.getSegmentColorShader(this.context);
606+
if (shader === null) return color;
607+
parameters;
608+
shader.bind();
609+
const { gl } = shader;
610+
const positionBuffer = GLBuffer.fromData(
611+
gl,
612+
new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]),
613+
);
614+
positionBuffer.bindToVertexAttrib(shader.attribute("aVertexPosition"), 2);
615+
gl.uniform4fv(shader.uniform("uColor"), color);
616+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
617+
const data = new Uint8Array(4);
618+
// TODO can I read straight to float?
619+
gl.readPixels(
620+
0,
621+
0,
622+
1,
623+
1,
624+
WebGL2RenderingContext.RGBA,
625+
WebGL2RenderingContext.UNSIGNED_BYTE,
626+
data,
627+
);
628+
for (let i = 0; i < data.length; i++) {
629+
color[i] = data[i] / 255.0;
630+
}
631+
return color;
632+
};
633+
554634
linkedSegmentationGroup: LinkedLayerGroup;
555635
linkedSegmentationColorGroup: LinkedLayerGroup;
556636
originalSegmentationGroupState: SegmentationUserLayerGroupState;
@@ -689,6 +769,9 @@ export class SegmentationUserLayer extends Base {
689769
this.displayState.linkedSegmentationGroup.changed.add(() =>
690770
this.updateDataSubsourceActivations(),
691771
);
772+
this.displayState.fragmentMain.changed.add(
773+
this.specificationChanged.dispatch,
774+
);
692775
this.tabs.add("rendering", {
693776
label: "Render",
694777
order: -100,
@@ -996,6 +1079,9 @@ export class SegmentationUserLayer extends Base {
9961079
this.displayState.ignoreNullVisibleSet.restoreState(
9971080
specification[json_keys.IGNORE_NULL_VISIBLE_SET_JSON_KEY],
9981081
);
1082+
this.displayState.fragmentMain.restoreState(
1083+
specification[json_keys.SHADER_JSON_KEY],
1084+
);
9991085

10001086
const { skeletonRenderingOptions } = this.displayState;
10011087
skeletonRenderingOptions.restoreState(
@@ -1066,6 +1152,7 @@ export class SegmentationUserLayer extends Base {
10661152
this.displayState.renderScaleTarget.toJSON();
10671153
x[json_keys.CROSS_SECTION_RENDER_SCALE_JSON_KEY] =
10681154
this.sliceViewRenderScaleTarget.toJSON();
1155+
x[json_keys.SHADER_JSON_KEY] = this.displayState.fragmentMain.toJSON();
10691156

10701157
const { linkedSegmentationGroup, linkedSegmentationColorGroup } =
10711158
this.displayState;
@@ -1365,6 +1452,7 @@ export class SegmentationUserLayer extends Base {
13651452

13661453
const visibleSegments = [...visibleSegmentsSet];
13671454
const colors = visibleSegments.map((id) => {
1455+
// here we can do a batch get of colors using the segment color shader instead of one at a time
13681456
const color = getCssColor(getBaseObjectColor(displayState, id));
13691457
return { color, id };
13701458
});
@@ -1401,6 +1489,10 @@ registerLayerTypeDetector((subsource) => {
14011489
return undefined;
14021490
});
14031491

1492+
registerLayerShaderControlsTool(SegmentationUserLayer, (layer) => ({
1493+
shaderControlState: layer.displayState.shaderControlState,
1494+
}));
1495+
14041496
registerLayerShaderControlsTool(
14051497
SegmentationUserLayer,
14061498
(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/mesh/frontend.ts

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ 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 {
47+
SegmentColorShaderManager,
48+
} from "#src/segment_color.js";
4649
import {
4750
forEachVisibleSegment,
4851
getObjectKey,
@@ -56,7 +59,7 @@ import {
5659
import type { WatchableValueInterface } from "#src/trackable_value.js";
5760
import { makeCachedDerivedWatchableValue } from "#src/trackable_value.js";
5861
import type { Borrowed, RefCounted } from "#src/util/disposable.js";
59-
import type { vec4 } from "#src/util/geom.js";
62+
import { vec4 } from "#src/util/geom.js";
6063
import {
6164
getFrustrumPlanes,
6265
mat3,
@@ -68,8 +71,17 @@ import {
6871
import * as matrix from "#src/util/matrix.js";
6972
import { GLBuffer } from "#src/webgl/buffer.js";
7073
import type { GL } from "#src/webgl/context.js";
71-
import { parameterizedEmitterDependentShaderGetter } from "#src/webgl/dynamic_shader.js";
74+
import {
75+
parameterizedEmitterDependentShaderGetter,
76+
shaderCodeWithLineDirective,
77+
WatchableShaderError,
78+
} from "#src/webgl/dynamic_shader.js";
7279
import type { ShaderBuilder, ShaderProgram } from "#src/webgl/shader.js";
80+
import {
81+
addControlsToBuilder,
82+
setControlsInShader,
83+
ShaderControlState,
84+
} from "#src/webgl/shader_ui_controls.js";
7385
import type { RPC } from "#src/worker_rpc.js";
7486
import { registerSharedObjectOwner } from "#src/worker_rpc.js";
7587

@@ -349,24 +361,44 @@ export class MeshShaderManager {
349361
}
350362

351363
makeGetter(layer: RefCounted & { gl: GL; displayState: MeshDisplayState }) {
352-
const silhouetteRenderingEnabled = layer.registerDisposer(
364+
const parameters = layer.registerDisposer(
353365
makeCachedDerivedWatchableValue(
354-
(x) => x > 0,
355-
[layer.displayState.silhouetteRendering],
366+
(silhouetteRendering, shaderBuilderState) => {
367+
return {
368+
silhouetteRenderingEnabled: silhouetteRendering > 0,
369+
shaderBuilderState,
370+
};
371+
},
372+
[
373+
layer.displayState.silhouetteRendering,
374+
layer.displayState.shaderControlState.builderState,
375+
],
356376
),
357377
);
378+
358379
return parameterizedEmitterDependentShaderGetter(layer, layer.gl, {
359380
memoizeKey: `mesh/MeshShaderManager/${this.fragmentRelativeVertices}/${this.vertexPositionFormat}`,
360-
parameters: silhouetteRenderingEnabled,
361-
defineShader: (builder, silhouetteRenderingEnabled) => {
381+
parameters,
382+
encodeParameters: (p) => {
383+
return `${p.silhouetteRenderingEnabled}/${p.shaderBuilderState.parseResult.code}`;
384+
},
385+
shaderError: layer.displayState.shaderError,
386+
defineShader: (
387+
builder,
388+
{ silhouetteRenderingEnabled, shaderBuilderState },
389+
) => {
390+
addControlsToBuilder(shaderBuilderState, builder);
362391
this.vertexPositionHandler.defineShader(builder);
363392
builder.addAttribute("highp vec2", "aVertexNormal");
364393
builder.addVarying("highp vec4", "vColor");
394+
builder.addVarying("highp float", "vLightingFactor"); // TODO do I keep this here?
365395
builder.addUniform("highp vec4", "uLightDirection");
366396
builder.addUniform("highp vec4", "uColor");
367397
builder.addUniform("highp mat3", "uNormalMatrix");
368398
builder.addUniform("highp mat4", "uModelViewProjection");
369399
builder.addUniform("highp uint", "uPickID");
400+
builder.addUniform("highp uvec2", "uID");
401+
370402
if (silhouetteRenderingEnabled) {
371403
builder.addUniform("highp float", "uSilhouettePower");
372404
}
@@ -375,6 +407,12 @@ export class MeshShaderManager {
375407
builder.addUniform("highp vec3", "uFragmentShape");
376408
}
377409
builder.addVertexCode(glsl_decodeNormalOctahedronSnorm8);
410+
411+
builder.addVertexCode(`
412+
vec4 defaultColor() { return uColor; }
413+
void setColor(vec4 color) {
414+
vColor = color;
415+
}`);
378416
let vertexMain = "";
379417
if (this.fragmentRelativeVertices) {
380418
vertexMain += `
@@ -393,22 +431,33 @@ vec3 origNormal = decodeNormalOctahedronSnorm8(aVertexNormal);
393431
vec3 normal = normalize(uNormalMatrix * (normalMultiplier * origNormal));
394432
float absCosAngle = abs(dot(normal, uLightDirection.xyz));
395433
float lightingFactor = absCosAngle + uLightDirection.w;
396-
vColor = vec4(lightingFactor * uColor.rgb, uColor.a);
434+
vColor = uColor;
435+
vColor = segmentColor(vColor);
436+
vColor = vec4(lightingFactor * vColor.rgb, vColor.a);
437+
vLightingFactor = lightingFactor;
397438
`;
398439
if (silhouetteRenderingEnabled) {
399440
vertexMain += `
400441
vColor *= pow(1.0 - absCosAngle, uSilhouettePower);
401442
`;
402443
}
403444
builder.setVertexMain(vertexMain);
445+
const shaderManager = new SegmentColorShaderManager("getColor");
446+
shaderManager.defineShader(builder);
404447
builder.setFragmentMain("emit(vColor, uPickID);");
448+
const segmentColor = shaderCodeWithLineDirective(
449+
shaderBuilderState.parseResult.code,
450+
);
451+
builder.addVertexCode(segmentColor + "\n");
405452
},
406453
});
407454
}
408455
}
409456

410457
export interface MeshDisplayState extends SegmentationDisplayState3D {
411458
silhouetteRendering: WatchableValueInterface<number>;
459+
shaderControlState: ShaderControlState;
460+
shaderError: WatchableShaderError;
412461
}
413462

414463
export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRenderLayerAttachmentState> {
@@ -432,6 +481,14 @@ export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRender
432481
this.registerDisposer(
433482
displayState.silhouetteRendering.changed.add(this.redrawNeeded.dispatch),
434483
);
484+
this.registerDisposer(
485+
displayState.shaderControlState.changed.add(this.redrawNeeded.dispatch),
486+
);
487+
this.registerDisposer(
488+
displayState.shaderControlState.parseResult.changed.add(
489+
this.redrawNeeded.dispatch,
490+
),
491+
);
435492

436493
const sharedObject = (this.backend = this.registerDisposer(
437494
new SegmentationLayerSharedObject(
@@ -490,11 +547,19 @@ export class MeshLayer extends PerspectiveViewRenderLayer<ThreeDimensionalRender
490547
if (modelMatrix === undefined) {
491548
return;
492549
}
493-
const { shader } = this.getShader(renderContext.emitter);
550+
const { shader, parameters } = this.getShader(renderContext.emitter);
494551
if (shader === null) return;
495552
shader.bind();
496553
meshShaderManager.beginLayer(gl, shader, renderContext, this.displayState);
497554
meshShaderManager.beginModel(gl, shader, renderContext, modelMatrix);
555+
setControlsInShader;
556+
this.displayState.silhouetteRendering;
557+
setControlsInShader(
558+
gl,
559+
shader,
560+
this.displayState.shaderControlState,
561+
parameters.shaderBuilderState.parseResult.controls,
562+
);
498563

499564
const manifestChunks = this.source.chunks;
500565

@@ -774,6 +839,8 @@ export class MultiscaleMeshLayer extends PerspectiveViewRenderLayer<ThreeDimensi
774839
displayState.silhouetteRendering.changed.add(this.redrawNeeded.dispatch),
775840
);
776841

842+
console.log("are we multiscale?");
843+
777844
const sharedObject = (this.backend = this.registerDisposer(
778845
new SegmentationLayerSharedObject(
779846
chunkManager,
@@ -829,7 +896,9 @@ export class MultiscaleMeshLayer extends PerspectiveViewRenderLayer<ThreeDimensi
829896
attachment,
830897
);
831898
if (modelMatrix === undefined) return;
832-
const { shader } = this.getShader(renderContext.emitter);
899+
console.log("get shader multiscale!");
900+
const { shader, parameters } = this.getShader(renderContext.emitter);
901+
parameters;
833902
if (shader === null) return;
834903
shader.bind();
835904
meshShaderManager.beginLayer(gl, shader, renderContext, this.displayState);
@@ -900,6 +969,7 @@ export class MultiscaleMeshLayer extends PerspectiveViewRenderLayer<ThreeDimensi
900969
if (renderContext.emitPickID) {
901970
meshShaderManager.setPickID(gl, shader, pickIndex!);
902971
}
972+
903973
getMultiscaleChunksToDraw(
904974
manifest,
905975
modelViewProjection,

0 commit comments

Comments
 (0)