Skip to content

Commit 4d0f4d8

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 4d0f4d8

File tree

2 files changed

+116
-42
lines changed

2 files changed

+116
-42
lines changed

src/skeleton/frontend.ts

Lines changed: 101 additions & 42 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,8 +95,22 @@ import { defineVertexId, VertexIdHelper } from "#src/webgl/vertex_id.js";
9495

9596
const tempMat2 = mat4.create();
9697

97-
const DEFAULT_FRAGMENT_MAIN = `void main() {
98-
emitDefault();
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() {
112+
setLineWidth(uLineWidth);
113+
setNodeDiameter(uNodeDiameter);
99114
}
100115
`;
101116

@@ -126,6 +141,7 @@ class RenderHelper extends RefCounted {
126141
defineVertexId(builder);
127142
builder.addUniform("highp vec4", "uColor");
128143
builder.addUniform("highp mat4", "uProjection");
144+
builder.addUniform("highp mat4", "uViewModel");
129145
builder.addUniform("highp uint", "uPickID");
130146
}
131147

@@ -165,41 +181,65 @@ class RenderHelper extends RefCounted {
165181
this.defineCommonShader(builder);
166182
this.defineAttributeAccess(builder);
167183
defineLineShader(builder);
184+
defineNoOpNodeSetters(builder);
168185
builder.addAttribute("highp uvec2", "aVertexIndex");
169186
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;
187+
builder.addUniform("highp float", "uNodeDiameter");
188+
builder.addVarying("vec4", "vColor");
189+
builder.addVertexCode(glsl_COLORMAPS);
190+
builder.addVertexCode(glsl_nanometersToPixels);
191+
builder.addVertexCode(`
192+
float nanometersToPixels(float value) {
193+
vec2 lineOffset = getLineOffset();
194+
highp vec3 vertexA = readAttribute0(aVertexIndex.x);
195+
highp vec3 vertexB = readAttribute0(aVertexIndex.y);
196+
highp vec3 vertex = mix(vertexA, vertexB, lineOffset.x);
197+
return nanometersToPixels(value, vertex, uProjection, uViewModel, 1.0 / uLineParams.x);
198+
}
199+
float ng_lineWidth;
200+
void setLineWidth(float width) {
201+
ng_lineWidth = width;
181202
}
182203
void emitRGB(vec3 color) {
183-
emit(vec4(color * uColor.a, uColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
204+
vColor = vec4(color.rgb, uColor.a);
205+
}
206+
void emitRGBA(vec4 color) {
207+
vColor = color;
184208
}
185209
void emitDefault() {
186-
emit(vec4(uColor.rgb, uColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
210+
emitRGBA(uColor);
187211
}
188212
`);
189-
builder.addFragmentCode(glsl_COLORMAPS);
213+
let vertexMain = `
214+
highp vec3 vertexA = readAttribute0(aVertexIndex.x);
215+
highp vec3 vertexB = readAttribute0(aVertexIndex.y);
216+
highp uint lineEndpointIndex = getLineEndpointIndex();
217+
highp uint vertexIndex = aVertexIndex.x * lineEndpointIndex + aVertexIndex.y * (1u - lineEndpointIndex);
218+
highp uint vertexIndex2 = aVertexIndex.y * lineEndpointIndex + aVertexIndex.x * (1u - lineEndpointIndex);
219+
setLineWidth(uLineWidth);
220+
`;
190221
const { vertexAttributes } = this;
191222
const numAttributes = vertexAttributes.length;
192223
for (let i = 1; i < numAttributes; ++i) {
193224
const info = vertexAttributes[i];
194225
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
195-
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
196-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
226+
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex2);\n`; // TODO, why is vertexIndex2 correct?
227+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
197228
}
229+
vertexMain += `
230+
userMain();
231+
emitLine(uProjection * uViewModel, vertexA, vertexB, ng_lineWidth);
232+
`;
198233
builder.setVertexMain(vertexMain);
199234
addControlsToBuilder(shaderBuilderState, builder);
200-
builder.setFragmentMainFunction(
201-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
235+
builder.addVertexCode(
236+
"\n#define main userMain\n" +
237+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
238+
"\n#undef main\n",
202239
);
240+
builder.setFragmentMain(`
241+
emit(vec4(vColor.rgb, vColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
242+
`);
203243
},
204244
},
205245
);
@@ -230,42 +270,60 @@ void emitDefault() {
230270
builder,
231271
/*crossSectionFade=*/ this.targetIsSliceView,
232272
);
273+
defineNoOpEdgeSetters(builder);
233274
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;
275+
builder.addUniform("highp float", "uLineWidth");
276+
builder.addVarying("vec4", "vColor");
277+
builder.addVertexCode(glsl_COLORMAPS);
278+
builder.addVertexCode(glsl_nanometersToPixels);
279+
builder.addVertexCode(`
280+
float nanometersToPixels(float value) {
281+
highp uint vertexIndex = uint(gl_InstanceID);
282+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
283+
return nanometersToPixels(value, vertexPosition, uProjection, uViewModel, 1.0 / uCircleParams.x);
243284
}
244-
void emitRGBA(vec4 color) {
245-
vec4 borderColor = color;
246-
emit(getCircleColor(color, borderColor), uPickID);
285+
float ng_nodeDiameter;
286+
void setNodeDiameter(float size) {
287+
ng_nodeDiameter = size;
247288
}
248289
void emitRGB(vec3 color) {
249-
emitRGBA(vec4(color, 1.0));
290+
vColor = vec4(color.rgb, uColor.a);
291+
}
292+
void emitRGBA(vec4 color) {
293+
vColor = color;
250294
}
251295
void emitDefault() {
252296
emitRGBA(uColor);
253297
}
254298
`);
255-
builder.addFragmentCode(glsl_COLORMAPS);
299+
let vertexMain = `
300+
highp uint vertexIndex = uint(gl_InstanceID);
301+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
302+
setNodeDiameter(uNodeDiameter);
303+
`;
256304
const { vertexAttributes } = this;
257305
const numAttributes = vertexAttributes.length;
258306
for (let i = 1; i < numAttributes; ++i) {
259307
const info = vertexAttributes[i];
260308
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
261309
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
262-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
310+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
263311
}
312+
vertexMain += `
313+
userMain();
314+
emitCircle(uProjection * uViewModel * vec4(vertexPosition, 1.0), ng_nodeDiameter, 0.0);
315+
`;
264316
builder.setVertexMain(vertexMain);
265317
addControlsToBuilder(shaderBuilderState, builder);
266-
builder.setFragmentMainFunction(
267-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
318+
builder.addVertexCode(
319+
"\n#define main userMain\n" +
320+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
321+
"\n#undef main\n",
268322
);
323+
builder.setFragmentMain(`
324+
vec4 borderColor = vColor;
325+
emit(getCircleColor(vColor, borderColor), uPickID);
326+
`);
269327
},
270328
},
271329
);
@@ -312,9 +370,10 @@ void emitDefault() {
312370
renderContext: SliceViewPanelRenderContext | PerspectiveViewRenderContext,
313371
modelMatrix: mat4,
314372
) {
315-
const { viewProjectionMat } = renderContext.projectionParameters;
316-
const mat = mat4.multiply(tempMat2, viewProjectionMat, modelMatrix);
317-
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, mat);
373+
const { viewMatrix, projectionMat } = renderContext.projectionParameters;
374+
const viewModelMat = mat4.multiply(tempMat2, viewMatrix, modelMatrix);
375+
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, projectionMat);
376+
gl.uniformMatrix4fv(shader.uniform("uViewModel"), false, viewModelMat);
318377
this.vertexIdHelper.enable();
319378
}
320379

@@ -421,7 +480,7 @@ export class SkeletonRenderingOptions implements Trackable {
421480
return this.compound.changed;
422481
}
423482

424-
shader = makeTrackableFragmentMain(DEFAULT_FRAGMENT_MAIN);
483+
shader = makeTrackableFragmentMain(DEFAULT_VERTEX_MAIN);
425484
shaderControlState = new ShaderControlState(this.shader);
426485
params2d: ViewSpecificSkeletonRenderingOptions = {
427486
mode: new TrackableSkeletonRenderMode(SkeletonRenderMode.LINES_AND_POINTS),
@@ -471,7 +530,7 @@ export class SkeletonLayer extends RefCounted {
471530
private sharedObject: SegmentationLayerSharedObject;
472531
vertexAttributes: VertexAttributeRenderInfo[];
473532
fallbackShaderParameters = new WatchableValue(
474-
getFallbackBuilderState(parseShaderUiControls(DEFAULT_FRAGMENT_MAIN)),
533+
getFallbackBuilderState(parseShaderUiControls(DEFAULT_VERTEX_MAIN)),
475534
);
476535

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