Skip to content

Commit 966ad0e

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 966ad0e

File tree

2 files changed

+116
-44
lines changed

2 files changed

+116
-44
lines changed

src/skeleton/frontend.ts

Lines changed: 97 additions & 44 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,62 @@ 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");
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, uLineParams.xy);
198+
}
199+
float ng_lineWidth;
200+
void setLineWidth(float width) {
201+
ng_lineWidth = width;
202+
}
203+
void emitRGB(vec3 color) {
204+
vColor = vec4(color.rgb, uColor.a);
205+
}
206+
void emitRGBA(vec4 color) {
207+
vColor = color;
208+
}
209+
`);
170210
let vertexMain = `
171211
highp vec3 vertexA = readAttribute0(aVertexIndex.x);
172212
highp vec3 vertexB = readAttribute0(aVertexIndex.y);
173-
emitLine(uProjection, vertexA, vertexB, uLineWidth);
174213
highp uint lineEndpointIndex = getLineEndpointIndex();
175214
highp uint vertexIndex = aVertexIndex.x * lineEndpointIndex + aVertexIndex.y * (1u - lineEndpointIndex);
215+
highp uint vertexIndex2 = aVertexIndex.y * lineEndpointIndex + aVertexIndex.x * (1u - lineEndpointIndex);
216+
vColor = uColor;
176217
`;
177-
178-
builder.addFragmentCode(`
179-
vec4 segmentColor() {
180-
return uColor;
181-
}
182-
void emitRGB(vec3 color) {
183-
emit(vec4(color * uColor.a, uColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
184-
}
185-
void emitDefault() {
186-
emit(vec4(uColor.rgb, uColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
187-
}
188-
`);
189-
builder.addFragmentCode(glsl_COLORMAPS);
190218
const { vertexAttributes } = this;
191219
const numAttributes = vertexAttributes.length;
192220
for (let i = 1; i < numAttributes; ++i) {
193221
const info = vertexAttributes[i];
194222
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
195-
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
196-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
223+
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex2);\n`; // TODO, why is vertexIndex2 correct?
224+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
197225
}
226+
vertexMain += `
227+
userMain();
228+
emitLine(uProjection * uViewModel, vertexA, vertexB, ng_lineWidth);
229+
`;
198230
builder.setVertexMain(vertexMain);
199231
addControlsToBuilder(shaderBuilderState, builder);
200-
builder.setFragmentMainFunction(
201-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
232+
builder.addVertexCode(
233+
"\n#define main userMain\n" +
234+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
235+
"\n#undef main\n",
202236
);
237+
builder.setFragmentMain(`
238+
emit(vec4(vColor.rgb, vColor.a * getLineAlpha() * ${this.getCrossSectionFadeFactor()}), uPickID);
239+
`);
203240
},
204241
},
205242
);
@@ -230,42 +267,57 @@ void emitDefault() {
230267
builder,
231268
/*crossSectionFade=*/ this.targetIsSliceView,
232269
);
270+
defineNoOpEdgeSetters(builder);
233271
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;
272+
builder.addUniform("highp float", "uLineWidth");
273+
builder.addVarying("vec4", "vColor");
274+
builder.addVertexCode(glsl_COLORMAPS);
275+
builder.addVertexCode(glsl_nanometersToPixels);
276+
builder.addVertexCode(`
277+
float nanometersToPixels(float value) {
278+
highp uint vertexIndex = uint(gl_InstanceID);
279+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
280+
return nanometersToPixels(value, vertexPosition, uProjection, uViewModel, uCircleParams.xy);
243281
}
244-
void emitRGBA(vec4 color) {
245-
vec4 borderColor = color;
246-
emit(getCircleColor(color, borderColor), uPickID);
282+
float ng_nodeDiameter;
283+
void setNodeDiameter(float size) {
284+
ng_nodeDiameter = size;
247285
}
248286
void emitRGB(vec3 color) {
249-
emitRGBA(vec4(color, 1.0));
287+
vColor = vec4(color.rgb, uColor.a);
250288
}
251-
void emitDefault() {
252-
emitRGBA(uColor);
289+
void emitRGBA(vec4 color) {
290+
vColor = color;
253291
}
254292
`);
255-
builder.addFragmentCode(glsl_COLORMAPS);
293+
let vertexMain = `
294+
highp uint vertexIndex = uint(gl_InstanceID);
295+
highp vec3 vertexPosition = readAttribute0(vertexIndex);
296+
vColor = uColor;
297+
`;
256298
const { vertexAttributes } = this;
257299
const numAttributes = vertexAttributes.length;
258300
for (let i = 1; i < numAttributes; ++i) {
259301
const info = vertexAttributes[i];
260302
builder.addVarying(`highp ${info.glslDataType}`, `vCustom${i}`);
261303
vertexMain += `vCustom${i} = readAttribute${i}(vertexIndex);\n`;
262-
builder.addFragmentCode(`#define ${info.name} vCustom${i}\n`);
304+
builder.addVertexCode(`#define ${info.name} vCustom${i}\n`);
263305
}
306+
vertexMain += `
307+
userMain();
308+
emitCircle(uProjection * uViewModel * vec4(vertexPosition, 1.0), ng_nodeDiameter, 0.0);
309+
`;
264310
builder.setVertexMain(vertexMain);
265311
addControlsToBuilder(shaderBuilderState, builder);
266-
builder.setFragmentMainFunction(
267-
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code),
312+
builder.addVertexCode(
313+
"\n#define main userMain\n" +
314+
shaderCodeWithLineDirective(shaderBuilderState.parseResult.code) +
315+
"\n#undef main\n",
268316
);
317+
builder.setFragmentMain(`
318+
vec4 borderColor = vColor;
319+
emit(getCircleColor(vColor, borderColor), uPickID);
320+
`);
269321
},
270322
},
271323
);
@@ -312,9 +364,10 @@ void emitDefault() {
312364
renderContext: SliceViewPanelRenderContext | PerspectiveViewRenderContext,
313365
modelMatrix: mat4,
314366
) {
315-
const { viewProjectionMat } = renderContext.projectionParameters;
316-
const mat = mat4.multiply(tempMat2, viewProjectionMat, modelMatrix);
317-
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, mat);
367+
const { viewMatrix, projectionMat } = renderContext.projectionParameters;
368+
const viewModelMat = mat4.multiply(tempMat2, viewMatrix, modelMatrix);
369+
gl.uniformMatrix4fv(shader.uniform("uProjection"), false, projectionMat);
370+
gl.uniformMatrix4fv(shader.uniform("uViewModel"), false, viewModelMat);
318371
this.vertexIdHelper.enable();
319372
}
320373

@@ -421,7 +474,7 @@ export class SkeletonRenderingOptions implements Trackable {
421474
return this.compound.changed;
422475
}
423476

424-
shader = makeTrackableFragmentMain(DEFAULT_FRAGMENT_MAIN);
477+
shader = makeTrackableFragmentMain(DEFAULT_VERTEX_MAIN);
425478
shaderControlState = new ShaderControlState(this.shader);
426479
params2d: ViewSpecificSkeletonRenderingOptions = {
427480
mode: new TrackableSkeletonRenderMode(SkeletonRenderMode.LINES_AND_POINTS),
@@ -471,7 +524,7 @@ export class SkeletonLayer extends RefCounted {
471524
private sharedObject: SegmentationLayerSharedObject;
472525
vertexAttributes: VertexAttributeRenderInfo[];
473526
fallbackShaderParameters = new WatchableValue(
474-
getFallbackBuilderState(parseShaderUiControls(DEFAULT_FRAGMENT_MAIN)),
527+
getFallbackBuilderState(parseShaderUiControls(DEFAULT_VERTEX_MAIN)),
475528
);
476529

477530
get visibility() {

src/webgl/shader_lib.ts

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

468+
// we don't need to divide by w because it is just a scalar? and we don't care about where on the screen the x/y is
469+
470+
// what if we try distance in view space and then project, to avoid inverse?
471+
472+
export const glsl_nanometersToPixels = `
473+
float nanometersToPixels(float nanometers, highp vec3 vertex, mat4 projection, mat4 viewModel, vec2 params) {
474+
float viewDistance = 1.0;
475+
vec4 vertexViewOrigin = viewModel * vec4(vertex, 1.0);
476+
vec4 vertexViewOffset = vertexViewOrigin + vec4(viewDistance, 0.0, 0.0, 0.0);
477+
vec4 vertexClipOrigin = projection * vertexViewOrigin;
478+
vec4 vertexClipOffset = projection * vertexViewOffset;
479+
float projectionDistancePixels = abs(vertexClipOrigin.x - vertexClipOffset.x) / params.x;
480+
float viewModalScale = length(viewModel[0].xyz);
481+
float projectionScale = projectionDistancePixels / viewDistance;
482+
float perspectiveScale = 1.0 / vertexClipOrigin.w;
483+
return nanometers * viewModalScale * projectionScale * perspectiveScale;
484+
}
485+
`;
486+
468487
export function getShaderType(dataType: DataType, numComponents = 1) {
469488
switch (dataType) {
470489
case DataType.FLOAT32:

0 commit comments

Comments
 (0)