Skip to content

Commit f596890

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 a11f9a9 commit f596890

File tree

2 files changed

+114
-41
lines changed

2 files changed

+114
-41
lines changed

src/skeleton/frontend.ts

Lines changed: 99 additions & 41 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,65 @@ 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 * lineEndpointIndex + aVertexIndex.y * (1u - 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 * lineEndpointIndex + aVertexIndex.y * (1u - lineEndpointIndex);
217+
highp uint vertexIndex2 = aVertexIndex.y * lineEndpointIndex + aVertexIndex.x * (1u - lineEndpointIndex);
218+
setLineWidth(uLineWidth);
219+
`;
190220
const { vertexAttributes } = this;
191221
const numAttributes = vertexAttributes.length;
192222
for (let i = 1; i < numAttributes; ++i) {
193223
const info = vertexAttributes[i];
194224
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
195-
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
196-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
225+
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex2);\n`; // TODO, why is vertexIndex2 correct?
226+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
197227
}
228+
vertexMain += `
229+
userMain();
230+
emitLine(uProjection * uViewModel, vertexA, vertexB, ng_lineWidth);
231+
`;
198232
builder.setVertexMain(vertexMain);
199233
addControlsToBuilder(shaderBuilderState, builder);
200-
builder.setFragmentMainFunction(
201-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
234+
builder.addVertexCode(
235+
"\n#define main userMain\n" +
236+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
237+
"\n#undef main\n",
202238
);
239+
builder.setFragmentMain(`
240+
emit(vec4(vColor.rgb, vColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
241+
`);
203242
},
204243
},
205244
);
@@ -230,42 +269,60 @@ void emitDefault() {
230269
builder,
231270
/*crossSectionFade=*/ this.targetIsSliceView,
232271
);
272+
defineNoOpEdgeSetters(builder);
233273
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;
274+
builder.addUniform("highp float", "uLineWidth");
275+
builder.addVarying("vec4", "vColor");
276+
builder.addVertexCode(glsl_COLORMAPS);
277+
builder.addVertexCode(glsl_nanometersToPixels);
278+
builder.addVertexCode(`
279+
float nanometersToPixels(float value) {
280+
highp uint vertexIndex = uint(gl_InstanceID);
281+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
282+
return nanometersToPixels(value, vertexPosition, uProjection, uViewModel, 1.0 / uCircleParams.x);
243283
}
244-
void emitRGBA(vec4 color) {
245-
vec4 borderColor = color;
246-
emit(getCircleColor(color, borderColor), uPickID);
284+
float ng_nodeDiameter;
285+
void setNodeDiameter(float size) {
286+
ng_nodeDiameter = size;
247287
}
248288
void emitRGB(vec3 color) {
249-
emitRGBA(vec4(color, 1.0));
289+
vColor = vec4(color.rgb, uColor.a);
290+
}
291+
void emitRGBA(vec4 color) {
292+
vColor = color;
250293
}
251294
void emitDefault() {
252295
emitRGBA(uColor);
253296
}
254297
`);
255-
builder.addFragmentCode(glsl_COLORMAPS);
298+
let vertexMain = `
299+
highp uint vertexIndex = uint(gl_InstanceID);
300+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
301+
setNodeDiameter(uNodeDiameter);
302+
`;
256303
const { vertexAttributes } = this;
257304
const numAttributes = vertexAttributes.length;
258305
for (let i = 1; i < numAttributes; ++i) {
259306
const info = vertexAttributes[i];
260307
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
261308
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
262-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
309+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
263310
}
311+
vertexMain += `
312+
userMain();
313+
emitCircle(uProjection * uViewModel * vec4(vertexPosition, 1.0), ng_nodeDiameter, 0.0);
314+
`;
264315
builder.setVertexMain(vertexMain);
265316
addControlsToBuilder(shaderBuilderState, builder);
266-
builder.setFragmentMainFunction(
267-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
317+
builder.addVertexCode(
318+
"\n#define main userMain\n" +
319+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
320+
"\n#undef main\n",
268321
);
322+
builder.setFragmentMain(`
323+
vec4 borderColor = vColor;
324+
emit(getCircleColor(vColor, borderColor), uPickID);
325+
`);
269326
},
270327
},
271328
);
@@ -312,9 +369,10 @@ void emitDefault() {
312369
renderContext: SliceViewPanelRenderContext | PerspectiveViewRenderContext,
313370
modelMatrix: mat4,
314371
) {
315-
const { viewProjectionMat } = renderContext.projectionParameters;
316-
const mat = mat4.multiply(tempMat2, viewProjectionMat, modelMatrix);
317-
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, mat);
372+
const { viewMatrix, projectionMat } = renderContext.projectionParameters;
373+
const viewModelMat = mat4.multiply(tempMat2, viewMatrix, modelMatrix);
374+
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, projectionMat);
375+
gl.uniformMatrix4fv(shader.uniform("uViewModel"), false, viewModelMat);
318376
this.vertexIdHelper.enable();
319377
}
320378

@@ -421,7 +479,7 @@ export class SkeletonRenderingOptions implements Trackable {
421479
return this.compound.changed;
422480
}
423481

424-
shader = makeTrackableFragmentMain(DEFAULT_FRAGMENT_MAIN);
482+
shader = makeTrackableFragmentMain(DEFAULT_VERTEX_MAIN);
425483
shaderControlState = new ShaderControlState(this.shader);
426484
params2d: ViewSpecificSkeletonRenderingOptions = {
427485
mode: new TrackableSkeletonRenderMode(SkeletonRenderMode.LINES_AND_POINTS),
@@ -471,7 +529,7 @@ export class SkeletonLayer extends RefCounted {
471529
private sharedObject: SegmentationLayerSharedObject;
472530
vertexAttributes: VertexAttributeRenderInfo[];
473531
fallbackShaderParameters = new WatchableValue(
474-
getFallbackBuilderState(parseShaderUiControls(DEFAULT_FRAGMENT_MAIN)),
532+
getFallbackBuilderState(parseShaderUiControls(DEFAULT_VERTEX_MAIN)),
475533
);
476534

477535
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)