将抗锯齿圆圈与 regl 混合

Don*_*rdy 5 shader blending alphablending glsl regl

我正在使用 regl 渲染圆圈,并且有三个目标:

  1. 画布应该是透明的,在其后面显示 HTML 内容。
  2. 圆应该平滑地抗锯齿。
  3. 重叠的圆圈应该看起来合理(混合颜色,没有角落显示)

到目前为止,我有这个:故障代码演示

更新:演示链接现在反映了有效的、已接受的答案。下面的代码不变。

索引.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();
Run Code Online (Sandbox Code Playgroud)

顶点文件

// 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.;
}
Run Code Online (Sandbox Code Playgroud)

片段.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);
}
Run Code Online (Sandbox Code Playgroud)

然而,结果看起来并不那么好。角是可见的,并且圆圈没有正确混合。

具有默认混合的圆圈

我还尝试添加以下混合术语:

func: {
  srcRGB: 'src alpha',
  srcAlpha: 'one minus src alpha',
  dstRGB: 'one minus src alpha',
  dstAlpha: 'src alpha'
}
Run Code Online (Sandbox Code Playgroud)

带有自定义混合的圆圈

这看起来好一点,但角落仍然存在,当背景为白色时有问题。

你能提出改进建议吗?(也许可以为我提供有关混合的更好信息,如果这是我在这里缺少的信息)谢谢!

Rab*_*d76 5

您应该像这样设置混合参数:

func: {
    srcRGB:   'src alpha',
    srcAlpha: 'src alpha',
    dstRGB:   'one minus src alpha',
    dstAlpha: 'one minus src alpha'
}
Run Code Online (Sandbox Code Playgroud)

这意味着您的目标颜色和源颜色会像这样混合:

红、绿、蓝 ( srcRGB: 'src alpha', dstRGB: 'one minus src alpha'):

R_dest = R_dest * (1 - Alpha_src) + R_src * Alpha_src
G_dest = G_dest * (1 - Alpha_src) + G_src * Alpha_src
B_dest = R_dest * (1 - Alpha_src) + R_src * Alpha_src
Run Code Online (Sandbox Code Playgroud)

Alpha 通道 ( srcAlpha: 'src alpha', dstAlpha: 'one minus src alpha'):

Alpha_dest = Alpha_dest * (1 - Alpha_src) + Alpha_src * Alpha_src
Run Code Online (Sandbox Code Playgroud)

参见glBlendFuncglBlendFuncSeparate

此外,您必须确保禁用深度测试

请参阅上面的 WebGL 示例(使用 Firefox、Chrome、Edge、Opera 进行测试):

func: {
    srcRGB:   'src alpha',
    srcAlpha: 'src alpha',
    dstRGB:   'one minus src alpha',
    dstAlpha: 'one minus src alpha'
}
Run Code Online (Sandbox Code Playgroud)


gma*_*man 5

除非您特别要求,否则画布需要预先乘以 alpha,所以问题 #1 是您的混合函数应该是

  blend: {
    enable: true,
    func: {
      srcRGB: 'one',
      srcAlpha: 'one',
      dstRGB: 'one minus src alpha',
      dstAlpha: 'one minus src alpha',
    },
  },
Run Code Online (Sandbox Code Playgroud)

您还需要从着色器返回预乘值

  gl_FragColor = vec4(vColor, alpha);
  gl_FragColor.rgb *= gl_FragColor.a;   // premultiply by alpha
}
Run Code Online (Sandbox Code Playgroud)

另一个问题是您进行了深度测试。默认深度函数是LESS。这意味着不会绘制新像素,除非它们的 Z 值小于现有的 Z 值。由于您的所有圆圈都以相同的深度绘制,因此不会在绘制前一个圆圈的任何位置绘制新圆圈。

最简单的解决方法是关闭深度测试

  depth: {enable: false},
Run Code Online (Sandbox Code Playgroud)

结果:

在此处输入图片说明

至于为什么 premultiplied alpha 看到

https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre