Published
Edited
Feb 2, 2018
1 fork
18 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
createMesh = () => {
const subdivisions = 64;
const geometry = new THREE.PlaneGeometry(2, 2, subdivisions, 1);
const shader = new THREE.ShaderMaterial({
vertexShader: vertex(),
fragmentShader: fragment(),
uniforms: uniforms(subdivisions),
side: THREE.BackSide,
extensions: { derivatives: true }
});
const mesh = new THREE.Mesh(geometry, shader);
return mesh;
}
Insert cell
Insert cell
uniforms = (subdivisions) => ({
subdivisions: { value: subdivisions },
start: { value: new THREE.Vector2(-0.75, -0.75) },
end: { value: new THREE.Vector2(0.75, 0.75) },
control: { value: new THREE.Vector2() },
thickness: { value: 0.025 }
})
Insert cell
Insert cell
vertex = () => `
uniform float subdivisions;
uniform float thickness;
uniform vec2 start;
uniform vec2 end;
uniform vec2 control;
varying vec2 vCoord;

#define PI 3.14

vec3 sample (float t) {
// We can also adjust the per-vertex curve thickness by modifying this 0..1 number
float volume = 1.0;

// Try replacing the above with:
// float volume = 1.0 * sin(t * PI);

// Solve the quadratic curve with the start, control and end points:
float dt = (1.0 - t);
float dtSq = dt * dt;
float tSq = t * t;
float x = dtSq * start.x + 2.0 * dt * t * control.x + tSq * end.x;
float y = dtSq * start.y + 2.0 * dt * t * control.y + tSq * end.y;
return vec3(x, y, volume);

// Alternatively, you can replace the above with a linear mix() operation
// This will produce a straight line between the start and end points
// return vec3(mix(start, end, t), volume);
}

void main () {
// Get the "arc length" in 0..1 space
float arclen = (position.x * 0.5 + 0.5);

// How far to offset the line thickness for this vertex in -1..1 space
float extrusion = position.y;

// Find next sample along curve
float nextArclen = arclen + (1.0 / subdivisions);

// Sample the curve in two places
// XY is the 2D position, and the Z component is the thickness at that vertex
vec3 current = sample(arclen);
vec3 next = sample(nextArclen);

// Now find the 2D perpendicular to form our line segment
vec2 direction = normalize(next.xy - current.xy);
vec2 perpendicular = vec2(-direction.y, direction.x);

// Extrude
float computedExtrusion = extrusion * (thickness / 2.0) * current.z;
vec3 offset = current.xyz + vec3(perpendicular.xy, 0.0) * computedExtrusion;

// Compute final position
gl_Position = projectionMatrix * modelViewMatrix * vec4(offset.xyz, 1.0);

// Pass along the coordinates for texturing/effects
vCoord = position.xy;
}
`
Insert cell
Insert cell
fragment = () => `
varying vec2 vCoord;

float aastep (float threshold, float value) {
float afwidth = fwidth(value) * 0.5;
return smoothstep(threshold - afwidth, threshold + afwidth, value);
}

void main () {
// How many dashes to show
float repeat = 10.0;

// How big is the gap between each dash
float gapSize = 0.25;

// Create a dashed line
float dash = abs(fract(vCoord.x * repeat) - 0.5);

// Smooth the dashed line to sharp/crisp anti-aliasing
dash = 1.0 - aastep(gapSize, dash);

gl_FragColor = vec4(vec3(dash), 1.0);
}
`
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more