Blending anti-aliased circles with regl

Viewed 1203

I'm rendering circles using regl, and have three goals:

  1. The canvas should be transparent, showing HTML content behind it.
  2. Circles should be antialiased smoothly.
  3. Overlapping circles should look reasonable (blend colors, no corners showing)

So far, I have this: Glitch code and demo.

UPDATE: The demo links now reflect the working, accepted answer. Code below is unchanged.

index.js

const regl = require('regl');
const glsl = require('glslify');
const vertexShader = glsl.file('../shaders/vertex.glsl');
const fragmentShader = glsl.file('../shaders/fragment.glsl');

// Create webgl context and clear.
const canvasEl = document.querySelector('canvas');
const app = regl({
  canvas: canvasEl,
  extensions: ['OES_standard_derivatives']
});
app.clear({color: [0, 0, 0, 0], depth: 1});

// Generate random points and colors.
const attributes = {position: [], color: []};
for (let i = 0; i < 100; i++) {
  attributes.position.push(Math.random() * 2 - 1, Math.random() * 2 - 1);
  attributes.color.push(Math.random(), Math.random(), Math.random());
}

// Define draw instructions.
const draw = app({
  vert: vertexShader,
  frag: fragmentShader,
  attributes: attributes,
  count: 100,
  primitive: 'points',
  depth: {enable: true},
  blend: {
    enable: true
  }
});

// Draw the points.
draw();

vertex.glsl

// vertex.glsl
precision mediump float;

attribute vec2 position;
attribute vec3 color;

varying vec3 vColor;

void main() {
  vColor = color;
  gl_Position = vec4(position, 0, 1);
  gl_PointSize = 40.;
}

fragment.glsl

// fragment.glsl
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif

precision mediump float;

varying vec3 vColor;

void main() {

  float r = 0.0, delta = 0.0, alpha = 1.0;
  vec2 cxy = 2.0 * gl_PointCoord - 1.0;
  r = dot(cxy, cxy);

#ifdef GL_OES_standard_derivatives
  delta = fwidth(r);
  alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);
#endif

  gl_FragColor = vec4(vColor, alpha);
}

However, the result looks not-so-great. Corners are visible, and circles aren't blending properly.

circles with default blending

I've also tried adding the following blend terms:

func: {
  srcRGB: 'src alpha',
  srcAlpha: 'one minus src alpha',
  dstRGB: 'one minus src alpha',
  dstAlpha: 'src alpha'
}

circles with custom blending

This looks a bit better, but the corners are still there and something is wrong when the background is white.

Could you suggest improvements to this? (And maybe point me to better information about blending, if that's what I'm missing here) Thanks!

2 Answers
Related