Don*_*rdy 5 shader blending alphablending glsl regl
我正在使用 regl 渲染圆圈,并且有三个目标:
更新:演示链接现在反映了有效的、已接受的答案。下面的代码不变。
索引.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)
这看起来好一点,但角落仍然存在,当背景为白色时有问题。
你能提出改进建议吗?(也许可以为我提供有关混合的更好信息,如果这是我在这里缺少的信息)谢谢!
您应该像这样设置混合参数:
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)
参见glBlendFunc和glBlendFuncSeparate
此外,您必须确保禁用深度测试
请参阅上面的 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)
除非您特别要求,否则画布需要预先乘以 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