@jonobr1/force-directed-graph
Version:
GPU supercharged attraction-graph visualizations for the web built on top of Three.js
140 lines (112 loc) • 4.18 kB
JavaScript
/**
* Renders points of all nodes as a
* single draw call.
*/
const points = {
vertexShader: `
uniform float sizeAttenuation;
uniform float frustumSize;
uniform float is2D;
uniform float nodeRadius;
uniform float nodeScale;
uniform float uBeginning;
uniform float uEnding;
uniform float uNodeAmount;
uniform sampler2D texturePositions;
uniform sampler2D textureTargetPositions;
varying vec3 vColor;
varying float vImageKey;
varying float vDistance;
varying float vViewZ;
varying vec3 vTargetPosition;
varying float vHasTarget;
attribute float imageKey;
attribute float pointSize;
void main() {
float nodeIndex = position.z - 1.0;
float rangeStart = uBeginning * uNodeAmount;
float rangeEnd = uEnding * uNodeAmount;
float inRange = step( rangeStart, nodeIndex ) * ( 1.0 - step( rangeEnd, nodeIndex ) );
vec4 texel = texture2D( texturePositions, position.xy );
vec3 vPosition = texel.xyz;
vPosition.z *= 1.0 - is2D;
vec4 targetTexel = texture2D( textureTargetPositions, position.xy );
vTargetPosition = targetTexel.xyz;
vHasTarget = targetTexel.w;
vec4 mvPosition = modelViewMatrix * vec4( vPosition, 1.0 );
gl_PointSize = nodeRadius * pointSize * nodeScale;
gl_PointSize *= mix( 1.0, frustumSize / - mvPosition.z, sizeAttenuation );
gl_PointSize *= inRange;
vDistance = 1.0 / - mvPosition.z;
vViewZ = mvPosition.z;
vColor = color;
vImageKey = imageKey;
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform float sizeAttenuation;
uniform float frustumSize;
uniform vec3 uColor;
uniform float opacity;
uniform float imageDimensions;
uniform sampler2D textureAtlas;
uniform float inheritColors;
varying vec3 vColor;
varying float vImageKey;
varying float vDistance;
varying float vViewZ;
void main() {
// Calculate distance from center for circular shape and depth
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
float r = length(cxy);
// Antialiased circle using fwidth for automatic edge smoothing
float delta = fwidth(r);
float t = 1.0 - smoothstep(1.0 - delta, 1.0, r);
// Calculate custom depth to fix z-fighting with transparent points
// For fragments inside the circle, offset depth proportionally
if (r <= 1.0) {
// Keep the center of the node slightly closer so coincident links
// do not leak through overlapping nodes.
float depthOffset = (1.0 - r) * 0.0001;
gl_FragDepthEXT = gl_FragCoord.z - depthOffset;
} else {
gl_FragDepthEXT = gl_FragCoord.z;
}
if (r <= 1.0) {
// Keep the center of the node slightly closer so coincident links
// do not leak through overlapping nodes.
float depthOffset = (1.0 - r) * 0.0001;
gl_FragDepth = gl_FragCoord.z - depthOffset;
} else {
gl_FragDepth = gl_FragCoord.z;
}
// Calculate texture atlas coordinates for image sprites
float col = mod( vImageKey, imageDimensions );
float row = floor( vImageKey / imageDimensions );
vec2 uv = vec2( 0.0 );
uv.x = mix( 0.0, 1.0 / imageDimensions, gl_PointCoord.x );
uv.y = mix( 0.0, 1.0 / imageDimensions, gl_PointCoord.y );
uv = vec2( gl_PointCoord ) / imageDimensions;
uv.x += col / imageDimensions;
uv.y += row / imageDimensions;
vec4 texel = texture2D( textureAtlas, uv );
float useImage = step( 0.0, vImageKey );
t = mix( t, texel.a, useImage );
vec3 layer = mix( vec3( 1.0 ), texel.rgb, useImage );
float alpha = opacity * t;
if ( alpha <= 0.0 ) {
discard;
}
gl_FragColor = vec4( layer * mix( vec3( 1.0 ), vColor, inheritColors ) * uColor, alpha );
}
`,
};
export default points;