Skip to content

Commit 5fb851d

Browse files
committed
feat(skeleton): added setLineWidth, setNodeDiameter, nanometersToPixels. Migrated skeleton shader editor to control vertex shader instead of fragment shader (match behavior of annotation layer).
1 parent ce31ec9 commit 5fb851d

File tree

2 files changed

+112
-40
lines changed

2 files changed

+112
-40
lines changed

src/skeleton/frontend.ts

Lines changed: 97 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import type {
7575
ShaderProgram,
7676
ShaderSamplerType,
7777
} from "#src/webgl/shader.js";
78+
import { glsl_nanometersToPixels } from "#src/webgl/shader_lib.js";
7879
import type { ShaderControlsBuilderState } from "#src/webgl/shader_ui_controls.js";
7980
import {
8081
addControlsToBuilder,
@@ -94,7 +95,20 @@ import { defineVertexId, VertexIdHelper } from "#src/webgl/vertex_id.js";
9495

9596
const tempMat2 = mat4.create();
9697

97-
const DEFAULT_FRAGMENT_MAIN = `void main() {
98+
function defineNoOpEdgeSetters(builder: ShaderBuilder) {
99+
builder.addVertexCode(`
100+
void setLineWidth(float width) {}
101+
`);
102+
}
103+
104+
function defineNoOpNodeSetters(builder: ShaderBuilder) {
105+
builder.addVertexCode(`
106+
void setNodeDiameter(float size) {}
107+
`);
108+
}
109+
110+
const DEFAULT_VERTEX_MAIN = `
111+
void main() {
98112
emitDefault();
99113
}
100114
`;
@@ -126,6 +140,7 @@ class RenderHelper extends RefCounted {
126140
defineVertexId(builder);
127141
builder.addUniform("highp vec4", "uColor");
128142
builder.addUniform("highp mat4", "uProjection");
143+
builder.addUniform("highp mat4", "uViewModel");
129144
builder.addUniform("highp uint", "uPickID");
130145
}
131146

@@ -165,41 +180,64 @@ class RenderHelper extends RefCounted {
165180
this.defineCommonShader(builder);
166181
this.defineAttributeAccess(builder);
167182
defineLineShader(builder);
183+
defineNoOpNodeSetters(builder);
168184
builder.addAttribute("highp uvec2", "aVertexIndex");
169185
builder.addUniform("highp float", "uLineWidth");
170-
let vertexMain = `
171-
highp vec3 vertexA = readAttribute0(aVertexIndex.x);
172-
highp vec3 vertexB = readAttribute0(aVertexIndex.y);
173-
emitLine(uProjection, vertexA, vertexB, uLineWidth);
174-
highp uint lineEndpointIndex = getLineEndpointIndex();
175-
highp uint vertexIndex = aVertexIndex.x * (1u - lineEndpointIndex) + aVertexIndex.y * lineEndpointIndex;
176-
`;
177-
178-
builder.addFragmentCode(`
179-
vec4 segmentColor() {
180-
return uColor;
186+
builder.addUniform("highp float", "uNodeDiameter");
187+
builder.addVarying("vec4", "vColor");
188+
builder.addVertexCode(glsl_COLORMAPS);
189+
builder.addVertexCode(glsl_nanometersToPixels);
190+
builder.addVertexCode(`
191+
float nanometersToPixels(float value) {
192+
vec2 lineOffset = getLineOffset();
193+
highp vec3 vertexA = readAttribute0(aVertexIndex.x);
194+
highp vec3 vertexB = readAttribute0(aVertexIndex.y);
195+
highp vec3 vertex = mix(vertexA, vertexB, lineOffset.x);
196+
return nanometersToPixels(value, vertex, uProjection, uViewModel, 1.0 / uLineParams.x);
197+
}
198+
float ng_lineWidth;
199+
void setLineWidth(float width) {
200+
ng_lineWidth = width;
181201
}
182202
void emitRGB(vec3 color) {
183-
emit(vec4(color * uColor.a, uColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
203+
vColor = vec4(color.rgb, uColor.a);
204+
}
205+
void emitRGBA(vec4 color) {
206+
vColor = color;
184207
}
185208
void emitDefault() {
186-
emit(vec4(uColor.rgb, uColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
209+
emitRGBA(uColor);
187210
}
188211
`);
189-
builder.addFragmentCode(glsl_COLORMAPS);
212+
let vertexMain = `
213+
highp vec3 vertexA = readAttribute0(aVertexIndex.x);
214+
highp vec3 vertexB = readAttribute0(aVertexIndex.y);
215+
highp uint lineEndpointIndex = getLineEndpointIndex();
216+
highp uint vertexIndex = aVertexIndex.x * (1u - lineEndpointIndex) + aVertexIndex.y * lineEndpointIndex;
217+
setLineWidth(uLineWidth);
218+
`;
190219
const { vertexAttributes } = this;
191220
const numAttributes = vertexAttributes.length;
192221
for (let i = 1; i < numAttributes; ++i) {
193222
const info = vertexAttributes[i];
194223
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
195224
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
196-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
225+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
197226
}
227+
vertexMain += `
228+
userMain();
229+
emitLine(uProjection * uViewModel, vertexA, vertexB, ng_lineWidth);
230+
`;
198231
builder.setVertexMain(vertexMain);
199232
addControlsToBuilder(shaderBuilderState, builder);
200-
builder.setFragmentMainFunction(
201-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
233+
builder.addVertexCode(
234+
"\n#define main userMain\n" +
235+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
236+
"\n#undef main\n",
202237
);
238+
builder.setFragmentMain(`
239+
emit(vec4(vColor.rgb, vColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
240+
`);
203241
},
204242
},
205243
);
@@ -230,42 +268,60 @@ void emitDefault() {
230268
builder,
231269
/*crossSectionFade=*/ this.targetIsSliceView,
232270
);
271+
defineNoOpEdgeSetters(builder);
233272
builder.addUniform("highp float", "uNodeDiameter");
234-
let vertexMain = `
235-
highp uint vertexIndex = uint(gl_InstanceID);
236-
highp vec3 vertexPosition = readAttribute0(vertexIndex);
237-
emitCircle(uProjection * vec4(vertexPosition, 1.0), uNodeDiameter, 0.0);
238-
`;
239-
240-
builder.addFragmentCode(`
241-
vec4 segmentColor() {
242-
return uColor;
273+
builder.addUniform("highp float", "uLineWidth");
274+
builder.addVarying("vec4", "vColor");
275+
builder.addVertexCode(glsl_COLORMAPS);
276+
builder.addVertexCode(glsl_nanometersToPixels);
277+
builder.addVertexCode(`
278+
float nanometersToPixels(float value) {
279+
highp uint vertexIndex = uint(gl_InstanceID);
280+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
281+
return nanometersToPixels(value, vertexPosition, uProjection, uViewModel, 1.0 / uCircleParams.x);
243282
}
244-
void emitRGBA(vec4 color) {
245-
vec4 borderColor = color;
246-
emit(getCircleColor(color, borderColor), uPickID);
283+
float ng_nodeDiameter;
284+
void setNodeDiameter(float size) {
285+
ng_nodeDiameter = size;
247286
}
248287
void emitRGB(vec3 color) {
249-
emitRGBA(vec4(color, 1.0));
288+
vColor = vec4(color.rgb, uColor.a);
289+
}
290+
void emitRGBA(vec4 color) {
291+
vColor = color;
250292
}
251293
void emitDefault() {
252294
emitRGBA(uColor);
253295
}
254296
`);
255-
builder.addFragmentCode(glsl_COLORMAPS);
297+
let vertexMain = `
298+
highp uint vertexIndex = uint(gl_InstanceID);
299+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
300+
setNodeDiameter(uNodeDiameter);
301+
`;
256302
const { vertexAttributes } = this;
257303
const numAttributes = vertexAttributes.length;
258304
for (let i = 1; i < numAttributes; ++i) {
259305
const info = vertexAttributes[i];
260306
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
261307
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
262-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
308+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
263309
}
310+
vertexMain += `
311+
userMain();
312+
emitCircle(uProjection * uViewModel * vec4(vertexPosition, 1.0), ng_nodeDiameter, 0.0);
313+
`;
264314
builder.setVertexMain(vertexMain);
265315
addControlsToBuilder(shaderBuilderState, builder);
266-
builder.setFragmentMainFunction(
267-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
316+
builder.addVertexCode(
317+
"\n#define main userMain\n" +
318+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
319+
"\n#undef main\n",
268320
);
321+
builder.setFragmentMain(`
322+
vec4 borderColor = vColor;
323+
emit(getCircleColor(vColor, borderColor), uPickID);
324+
`);
269325
},
270326
},
271327
);
@@ -312,9 +368,10 @@ void emitDefault() {
312368
renderContext: SliceViewPanelRenderContext | PerspectiveViewRenderContext,
313369
modelMatrix: mat4,
314370
) {
315-
const { viewProjectionMat } = renderContext.projectionParameters;
316-
const mat = mat4.multiply(tempMat2, viewProjectionMat, modelMatrix);
317-
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, mat);
371+
const { viewMatrix, projectionMat } = renderContext.projectionParameters;
372+
const viewModelMat = mat4.multiply(tempMat2, viewMatrix, modelMatrix);
373+
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, projectionMat);
374+
gl.uniformMatrix4fv(shader.uniform("uViewModel"), false, viewModelMat);
318375
this.vertexIdHelper.enable();
319376
}
320377

@@ -421,7 +478,7 @@ export class SkeletonRenderingOptions implements Trackable {
421478
return this.compound.changed;
422479
}
423480

424-
shader = makeTrackableFragmentMain(DEFAULT_FRAGMENT_MAIN);
481+
shader = makeTrackableFragmentMain(DEFAULT_VERTEX_MAIN);
425482
shaderControlState = new ShaderControlState(this.shader);
426483
params2d: ViewSpecificSkeletonRenderingOptions = {
427484
mode: new TrackableSkeletonRenderMode(SkeletonRenderMode.LINES_AND_POINTS),
@@ -471,7 +528,7 @@ export class SkeletonLayer extends RefCounted {
471528
private sharedObject: SegmentationLayerSharedObject;
472529
vertexAttributes: VertexAttributeRenderInfo[];
473530
fallbackShaderParameters = new WatchableValue(
474-
getFallbackBuilderState(parseShaderUiControls(DEFAULT_FRAGMENT_MAIN)),
531+
getFallbackBuilderState(parseShaderUiControls(DEFAULT_VERTEX_MAIN)),
475532
);
476533

477534
get visibility() {

src/webgl/shader_lib.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,21 @@ highp int subtractSaturate(highp int x, highp uint y) {
465465
`,
466466
];
467467

468+
export const glsl_nanometersToPixels = `
469+
float nanometersToPixels(float nanometers, highp vec3 vertex, mat4 projection, mat4 viewModel, float projectionWidthPixels) {
470+
float viewDistance = 1.0;
471+
vec4 vertexViewOrigin = viewModel * vec4(vertex, 1.0);
472+
vec4 vertexViewOffset = vertexViewOrigin + vec4(viewDistance, 0.0, 0.0, 0.0);
473+
vec4 vertexClipOrigin = projection * vertexViewOrigin;
474+
vec4 vertexClipOffset = projection * vertexViewOffset;
475+
float projectionDistancePixels = abs(vertexClipOrigin.x - vertexClipOffset.x) * projectionWidthPixels;
476+
float viewModalScale = length(viewModel[0].xyz);
477+
float projectionScale = projectionDistancePixels / viewDistance;
478+
float perspectiveScale = 1.0 / vertexClipOrigin.w;
479+
return nanometers * viewModalScale * projectionScale * perspectiveScale;
480+
}
481+
`;
482+
468483
export function getShaderType(dataType: DataType, numComponents = 1) {
469484
switch (dataType) {
470485
case DataType.FLOAT32:

0 commit comments

Comments
 (0)