Skip to content

Commit

Permalink
Implement outline maxpixels
Browse files Browse the repository at this point in the history
Can cap the width of the outline using screen space pixels.
Also fixes a bug with outline and stick imposters.
  • Loading branch information
dkoes committed Sep 3, 2024
1 parent eb030b7 commit ec8251f
Show file tree
Hide file tree
Showing 16 changed files with 171 additions and 12 deletions.
7 changes: 3 additions & 4 deletions src/GLViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1361,10 +1361,7 @@ export class GLViewer {
parameters.style = parameters.style || "";

if (parameters.style.includes("outline")) {
var params: any = {};
if (parameters.color) params.color = CC.color(parameters.color);
if (parameters.width) params.width = parameters.width;
this.renderer.enableOutline(params);
this.renderer.enableOutline(parameters);
} else {
this.renderer.disableOutline();
}
Expand Down Expand Up @@ -5018,6 +5015,8 @@ export interface OutlineStyle {
width?: number;
/** Color of the outline */
color?: ColorSpec;
/** Maximum width in screen pixels of outline. */
maxpixels?: number;
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/WebGL/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,8 @@ export class Renderer {
"modelViewMatrix",
"projectionMatrix",
"normalMatrix",
"vWidth",
"vHeight"
];

// custom uniform vars
Expand Down Expand Up @@ -1664,6 +1666,13 @@ export class Renderer {
camera.matrixWorldInverse.elements
);
}

if (p_uniforms.vWidth) {
this._gl.uniform1f(p_uniforms.vWidth, this._viewportWidth);
}
if (p_uniforms.vHeight) {
this._gl.uniform1f(p_uniforms.vHeight, this._viewportHeight);
}
// Send projection matrix to uniform variable in shader
if (refreshMaterial) {
// Load projection, model-view matrices for perspective
Expand Down Expand Up @@ -1695,6 +1704,7 @@ export class Renderer {
m_uniforms.outlineColor.value = material.outlineColor;
m_uniforms.outlineWidth.value = material.outlineWidth;
m_uniforms.outlinePushback.value = material.outlinePushback;
m_uniforms.outlineMaxPixels.value = material.outlineMaxPixels*this.devicePixelRatio;
} else if (material.shaderID === "volumetric") {
//need a matrix that maps back from model coordinates to texture coordinates
// textureMat*modelInv*position
Expand Down
11 changes: 9 additions & 2 deletions src/WebGL/materials/MeshOutlineMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ export class MeshOutlineMaterial extends Material {
shaderID: string;
wireframe: boolean;
outlineColor: any;
outlineWidth: any;
outlinePushback: any;
outlineWidth: number;
outlinePushback: number;
outlineMaxPixels: number;

constructor(parameters?: any) {
super();
parameters = parameters || {};
Expand All @@ -18,12 +20,17 @@ export class MeshOutlineMaterial extends Material {
this.outlineColor = parameters.color || new Color(0.0, 0.0, 0.0);
this.outlineWidth = parameters.width || 0.1;
this.outlinePushback = parameters.pushback || 1.0;
this.outlineMaxPixels = parameters.maxpixels || 0.0;
}
clone<T extends this>(material: T = new MeshOutlineMaterial() as T): T {
super.clone.call(this, material);
material.fog = this.fog;
material.shaderID = this.shaderID;
material.wireframe = this.wireframe;
material.outlineColor = this.outlineColor;
material.outlineWidth = this.outlineWidth;
material.outlinePushback = this.outlinePushback;
material.outlineMaxPixels = this.outlineMaxPixels;
return material;
}
}
4 changes: 3 additions & 1 deletion src/WebGL/materials/SphereImposterOutlineMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class SphereImposterOutlineMaterial extends ImposterMaterial {
outlineColor: Color;
outlineWidth: number;
outlinePushback: number;
outlineMaxPixels: number;

constructor(parameters?: any) {
super(parameters);
Expand All @@ -14,7 +15,7 @@ export class SphereImposterOutlineMaterial extends ImposterMaterial {
this.outlineColor = parameters.color || new Color(0.0, 0.0, 0.0);
this.outlineWidth = parameters.width || 0.1;
this.outlinePushback = parameters.pushback || 1.0;

this.outlineMaxPixels = parameters.maxpixels || 0.0;
this.setValues(parameters);
}

Expand All @@ -23,6 +24,7 @@ export class SphereImposterOutlineMaterial extends ImposterMaterial {
material.outlineColor = this.outlineColor;
material.outlineWidth = this.outlineWidth;
material.outlinePushback = this.outlinePushback;
material.outlineMaxPixels = this.outlineMaxPixels;
return material;
}
}
6 changes: 6 additions & 0 deletions src/WebGL/materials/StickImposterOutlineMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ export class StickImposterOutlineMaterial extends ImposterMaterial {
outlineColor = new Color(0.0, 0.0, 0.0);
outlineWidth = 0.1;
outlinePushback = 1.0;
outlineMaxPixels = 0.0;

constructor(
parameters: Record<keyof StickImposterOutlineMaterial, unknown> & {
width?: number;
pushback?: number;
maxpixels?: number;
} = {} as any
) {
super(parameters);
if (parameters.color) this.outlineColor = parameters.color as Color;
if (parameters.width) this.outlineWidth = parameters.width as number;
if (parameters.pushback)
this.outlinePushback = parameters.pushback as number;
if (parameters.maxpixels)
this.outlineMaxPixels = parameters.maxpixels;

this.setValues(parameters);
}
Expand All @@ -25,6 +30,7 @@ export class StickImposterOutlineMaterial extends ImposterMaterial {
material.outlineColor = this.outlineColor;
material.outlineWidth = this.outlineWidth;
material.outlinePushback = this.outlinePushback;
material.outlineMaxPixels = this.outlineMaxPixels;
return material;
}
}
4 changes: 4 additions & 0 deletions src/WebGL/shaders/lib/outline/outline.frag
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ uniform float fogFar;

void main() {
gl_FragColor = vec4( outlineColor, 1 );

float depth = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = smoothstep( fogNear, fogFar, depth );
gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );
}


26 changes: 25 additions & 1 deletion src/WebGL/shaders/lib/outline/outline.vert
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float outlineWidth;
uniform float outlinePushback;
uniform float vWidth;
uniform float vHeight;
uniform float outlineMaxPixels;

attribute vec3 position;
attribute vec3 normal;
Expand All @@ -14,7 +17,28 @@ void main() {
vec4 norm = modelViewMatrix*vec4(normalize(normal),0.0);
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
mvPosition.xy += norm.xy*outlineWidth;
gl_Position = projectionMatrix * mvPosition;
vec4 outpos = projectionMatrix * mvPosition;

if(outlineMaxPixels > 0.0) {
vec4 unadjusted = projectionMatrix*modelViewMatrix * vec4( position, 1.0 );
float w = outpos.w;
//normalize homogeneous coords
unadjusted /= unadjusted.w;
outpos /= outpos.w;
vec2 diff = outpos.xy-unadjusted.xy;
//put into pixels
diff.x *= vWidth;
diff.y *= vHeight;
if ( length(diff) > outlineMaxPixels) {
vec2 ndiff = normalize(diff)*outlineMaxPixels;
ndiff.x /= vWidth;
ndiff.y /= vHeight;
outpos.xy = unadjusted.xy;
outpos.xy += ndiff;
}
outpos *= w; //if I don't do this things blow up
}
gl_Position = outpos;
mvPosition.z -= outlinePushback; //go backwards in model space
vec4 pushpos = projectionMatrix*mvPosition; //project to get z in projection space, I'm probably missing some simple math to do the same thing..
gl_Position.z = gl_Position.w*pushpos.z/pushpos.w;
Expand Down
1 change: 1 addition & 0 deletions src/WebGL/shaders/lib/outline/uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const uniforms = {
fogFar: { type: "f", value: 2000 },
outlineWidth: { type: "f", value: 0.1 },
outlinePushback: { type: "f", value: 1.0 },
outlineMaxPixels: {type: "f", value: 0.0 }
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float outlineWidth;
uniform float outlinePushback;
uniform float outlineMaxPixels;
uniform float vWidth;
uniform float vHeight;

attribute vec3 position;
attribute vec3 normal;
Expand All @@ -19,9 +22,30 @@ void main() {
center = mvPosition.xyz;
vec4 projPosition = projectionMatrix * mvPosition;
vec2 norm = normal.xy + vec2(sign(normal.x)*outlineWidth,sign(normal.y)*outlineWidth);
vec4 adjust = projectionMatrix* vec4(norm,normal.z,0.0); adjust.z = 0.0; adjust.w = 0.0;

vec4 adjust = projectionMatrix* vec4(norm,normal.z,1.0);
mapping = norm.xy;
rval = abs(norm.x);
gl_Position = projPosition+adjust;
gl_Position = projPosition+vec4(adjust.xy,0.0,0.0);

if(outlineMaxPixels > 0.0) {
vec4 unadjusted = projectionMatrix*vec4(center.x+normal.x, center.y,center.z,1.0);
vec4 ccoord = projectionMatrix*vec4(center.xyz,1.0);
adjust = projectionMatrix* vec4(center.x+norm.x,center.y,center.z,1.0);
//subtract center
unadjusted.xyz -= ccoord.xyz;
adjust.xyz -= ccoord.xyz;
unadjusted /= unadjusted.w;
adjust /= adjust.w;
float diff = abs(adjust.x-unadjusted.x);
diff *= vWidth;
if(diff > outlineMaxPixels) {

float fixlen = abs(unadjusted.x) + outlineMaxPixels/vWidth;
//adjsut reval by ratio of lengths
rval *= fixlen/abs(adjust.x);
}

}
}

1 change: 1 addition & 0 deletions src/WebGL/shaders/lib/sphereimposteroutline/uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const uniforms = {
fogFar: { type: 'f', value: 2000},
outlineWidth: { type: 'f', value: 0.1 },
outlinePushback: { type: 'f', value: 1.0 },
outlineMaxPixels: { type: 'f', value: 0.0 }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ uniform vec3 directionalLightDirection[ 1 ];
uniform vec3 outlineColor;
uniform float outlineWidth;
uniform float outlinePushback;
uniform float outlineMaxPixels;
uniform float vWidth;
uniform mat4 projinv;


attribute vec3 position;
Expand All @@ -28,19 +31,49 @@ void main() {
vColor = outlineColor;
float rad = radius+sign(radius)*outlineWidth;
r = abs(rad);

vec4 to = modelViewMatrix*vec4(normal, 1.0); //normal is other point of cylinder
vec4 pt = modelViewMatrix*vec4(position, 1.0);
//pushback
to.xyz += normalize(to.xyz)*outlinePushback;
pt.xyz += normalize(pt.xyz)*outlinePushback;
float scale = 1.0;
if(projectionMatrix[3][3] != 0.0) { //orthographic
to.z -= outlinePushback;
pt.z -= outlinePushback;
} else { //perspective
vec4 midbefore = pt;
if(length(to.xyz) < length(pt)) {
midbefore = to;
}
vec4 midafter = midbefore;
midafter.xyz += normalize(midbefore.xyz)*outlinePushback;

to.xyz += normalize(to.xyz)*outlinePushback;
pt.xyz += normalize(pt.xyz)*outlinePushback;

//figure out a scaling factor for radius to account for perspective setback
vec4 midbeforer = vec4(midbefore.x+rad,midbefore.y, midbefore.z, midbefore.w);
vec4 midafterr = vec4(midafter.x+rad,midafter.y, midafter.z, midafter.w);

vec4 mb = projectionMatrix*midbefore;
vec4 mbr = projectionMatrix*midbeforer;
vec4 ma = projectionMatrix*midafter;
vec4 mar = projectionMatrix*midafterr;
mb /= mb.w;
mbr /= mbr.w;
ma /= ma.w;
mar /= mar.w;
scale = abs((mbr.x-mb.x)/(mar.x-ma.x));
rad *= scale;
r = abs(rad);
}
vec4 mvPosition = pt;
p1 = pt.xyz; p2 = to.xyz;
vec3 norm = to.xyz-pt.xyz;
float mult = 1.1; //slop to account for perspective of sphere
if(length(p1) > length(p2)) { //billboard at level of closest point
mvPosition = to;
}

vec3 n = normalize(mvPosition.xyz);
//intersect with the plane defined by the camera looking at the billboard point
if(color.z >= 0.0) { //p1
Expand All @@ -53,6 +86,27 @@ void main() {
mvPosition.xyz = p2+t*pnorm;
mult *= -1.0;
}

if(outlineMaxPixels > 0.0) {
vec4 cpos = mvPosition;
vec4 unadjusted = projectionMatrix*vec4(cpos.x+abs(scale*radius), cpos.y,cpos.z,cpos.w);
vec4 ccoord = projectionMatrix*cpos;
vec4 adjust = projectionMatrix*vec4(cpos.x+r,cpos.y,cpos.z,cpos.w);
unadjusted /= unadjusted.w;
adjust /= adjust.w;
unadjusted.xyz -= ccoord.xyz/ccoord.w;
adjust.xyz -= ccoord.xyz/ccoord.w;
float diff = abs(adjust.x-unadjusted.x);
diff *= vWidth; //this should now be in pixels
if(diff > outlineMaxPixels) {
float fixlen = abs(unadjusted.x) + outlineMaxPixels/vWidth;
vec4 pcoord = ccoord;
pcoord.x += fixlen*pcoord.w;
vec4 altc = projinv*pcoord;
r= abs(altc.x-cpos.x);
}
}

vec3 cr = normalize(cross(mvPosition.xyz,norm))*rad;
vec3 doublecr = normalize(cross(mvPosition.xyz,cr))*rad;
mvPosition.xy += mult*(cr + doublecr).xy;
Expand Down
3 changes: 3 additions & 0 deletions src/WebGL/shaders/lib/stickimposteroutline/uniforms.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Matrix4 } from "WebGL/math";
import { Color } from "../../../../colors";

export const uniforms = {
Expand All @@ -8,4 +9,6 @@ export const uniforms = {
outlineColor: { type: 'c', value: new Color(0.0, 0.0, 0.0) },
outlineWidth: { type: 'f', value: 0.1 },
outlinePushback: { type: 'f', value: 1.0 },
outlineMaxPixels: { type: 'f', value: 0.0 },
projinv: { type: 'mat4', value: [] as Matrix4[] }
}
12 changes: 12 additions & 0 deletions tests/auto/tests/orthooutline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@


viewer.setViewStyle({style:"outline"});
viewer.setCameraParameters({orthographic: true});
$.get('data/1fas.pqr', function(data){
viewer.addModel(data, "pqr");
viewer.setStyle({'cartoon':{},'stick':{},'sphere':{radius:0.5}});
viewer.zoomTo({resi:1});
viewer.render();
});


12 changes: 12 additions & 0 deletions tests/auto/tests/outlinemaxpixels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@


viewer.setViewStyle({style:"outline","maxpixels":5});

$.get('data/1fas.pqr', function(data){
viewer.addModel(data, "pqr");
viewer.setStyle({'cartoon':{},'stick':{},'sphere':{radius:0.5}});
viewer.zoomTo({resi:1});
viewer.render();
});


Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ec8251f

Please sign in to comment.