具有透明背景的 Chromakey glsl 着色器
Chromakey glsl shader with transparent background
这是一个带有色键着色器的 P5 js 示例(从 shadertoy 移植)
使背景图像透明的最佳方法是什么?我尝试使用 loadImage() 方法将其更改为具有透明度的 png 图像,但没有成功,草图无法加载。我什至试图启用
gl.enable (gl.BLEND) 在设置函数中。我应该对背景图像也使用 texture2d () 方法,还是只使用带 alpha 的颜色?
片段着色器:
precision mediump float;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform vec2 iResolution;
mat4 RGBtoYUV = mat4(0.257, 0.439, -0.148, 0.0,
0.504, -0.368, -0.291, 0.0,
0.098, -0.071, 0.439, 0.0,
0.0625, 0.500, 0.500, 1.0 );
vec4 chromaKey = vec4(0.05, 0.63, 0.14, 1);
vec2 maskRange = vec2(0.005, 0.26);
float colorclose(vec3 yuv, vec3 keyYuv, vec2 tol)
{
float tmp = sqrt(pow(keyYuv.g - yuv.g, 2.0) + pow(keyYuv.b - yuv.b, 2.0));
if (tmp < tol.x)
return 0.0;
else if (tmp < tol.y)
return (tmp - tol.x)/(tol.y - tol.x);
else
return 1.0;
}
void main()
{
vec2 fragPos = gl_FragCoord.xy / iResolution.xy;
vec4 texColor0 = texture2D(tex0, fragPos);
vec4 texColor1 = texture2D(tex1, fragPos);
vec4 keyYUV = RGBtoYUV * chromaKey;
vec4 yuv = RGBtoYUV * texColor0;
float mask = 1.0 - colorclose(yuv.rgb, keyYUV.rgb, maskRange);
gl_FragColor = max(texColor0 - mask * chromaKey, 0.0) + texColor1 * mask;
}
顶点着色器:
attribute vec3 aPosition;
void main() {
vec4 positionVec4 = vec4(aPosition, 1.0);
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
gl_Position = positionVec4;
}
P5 js代码片段:
function preload(){
theShader = loadShader('webcam.vert', 'webcam.frag');
img = loadImage('https://princetonlibrary.org/wp-content/uploads/2018/04/p5js.png');
//img = loadImage('https://p5js.org/assets/img/asterisk-01-01.png'); // use alpha
//gl.enable(gl.BLEND);
}
function draw(){
shaderTexture.shader(theShader);
theShader.setUniform('tex0', cam);
theShader.setUniform('tex1', img);
theShader.setUniform('iResolution', [width, height]);
}
设置动态色键透明度
将以下统一添加到碎片着色器
uniform float backgroundAlpha;
将片段着色器的最后一行更改为
gl_FragColor = max(texColor0 - mask * chromaKey, 0.0) + texColor1 * mask;
至
vec4 col = max(texColor0-mask*chromaKey,0.0) + texColor1*mask*vec4(vec3(1), backgroundAlpha);
if (col.a > 0.0) {
gl_FragColor = col;
} else {
discard;
}
然后将制服设置为您想要的背景 alpha 级别
const bgLoc = gl.getUniformLocaton(program, "backgroundAlpha");
gl.uniform1f(bgLoc, 0);
// or with p5
theShader.setUniform('backgroundAlpha', 0); // 0 is full transparent
确保已设置混合模式
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// however you do that in P5.js
重要 确保在它后面的任何对象之后绘制此对象。如果不这样做,则半透明边缘将不会显示背景对象,只会显示渲染时对象后面的内容。
注意如果你希望它总是完全透明你不需要添加第二个纹理贴图,删除tex1
统一和相关代码JS 和片段着色器。然后最后一行就是...(不需要制服backgroundAlpha
)
vec4 col = max(texColor0 - mask * chromaKey, 0.0);
if (col.a > 0.0) {
gl_FragColor = col;
} else {
discard;
}
更好的色键着色器
以下着色器移除 0 到 range.y
范围内的关键颜色 keyRGBA
,使用值 range.x
到 range.y
的(Hermite)曲线平滑过渡。
您的代码使用的颜色 space 转换看起来很奇怪,我找不到任何使用该转换的东西。我使用了颜色 space YCbCr 忽略了亮度 Y
所以只需要色度组件
uniform sampler2D tex0;
uniform vec4 keyRGBA; // key color as rgba
uniform vec2 keyCC; // the CC part of YCC color model of key color
uniform vec2 range; // the smoothstep range
uniform vec2 iResolution;
vec2 RGBToCC(vec4 rgba) {
float Y = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;
return vec2((rgba.b - Y) * 0.565, (rgba.r - Y) * 0.713);
}
void main() {
vec4 src1Color = texture2D(tex0, gl_FragCoord.xy / iResolution);
vec2 CC = RGBToCC(src1Color);
float mask = sqrt(pow(keyCC.x - CC.x, 2.0) + pow(keyCC.y - CC.y, 2.0));
mask = smoothstep(range.x, range.y, mask);
if (mask == 0.0) { discard; }
else if (mask == 1.0) { gl_FragColor = src1Color; }
else { gl_FragColor = max(src1Color - (1.0 - mask) * keyRGBA, 0.0); }
}
设置制服range
,keyRGBA
和keyCC
可以设置如下
const RGBAToCC = (r, g, b) => {
const y = 0.299 * r + 0.587 * g + 0.114 * b;
return [(b - y) * 0.565, (r - y) * 0.713];
};
const keyRGBA = [0.05, 0.63, 0.14, 1]; // the green from your code
const range = [0.11, 0.22]; // A guess at the range needed
const keyCC = RGBAToCC(...keyRGBA);
theShader.setUniform('keyCC', keyCC);
theShader.setUniform('keyRGBA', keyRGBA);
theShader.setUniform('range', range);
注意keyRGBA
的alpha值应该是1
这是一个带有色键着色器的 P5 js 示例(从 shadertoy 移植) 使背景图像透明的最佳方法是什么?我尝试使用 loadImage() 方法将其更改为具有透明度的 png 图像,但没有成功,草图无法加载。我什至试图启用 gl.enable (gl.BLEND) 在设置函数中。我应该对背景图像也使用 texture2d () 方法,还是只使用带 alpha 的颜色?
片段着色器:
precision mediump float;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform vec2 iResolution;
mat4 RGBtoYUV = mat4(0.257, 0.439, -0.148, 0.0,
0.504, -0.368, -0.291, 0.0,
0.098, -0.071, 0.439, 0.0,
0.0625, 0.500, 0.500, 1.0 );
vec4 chromaKey = vec4(0.05, 0.63, 0.14, 1);
vec2 maskRange = vec2(0.005, 0.26);
float colorclose(vec3 yuv, vec3 keyYuv, vec2 tol)
{
float tmp = sqrt(pow(keyYuv.g - yuv.g, 2.0) + pow(keyYuv.b - yuv.b, 2.0));
if (tmp < tol.x)
return 0.0;
else if (tmp < tol.y)
return (tmp - tol.x)/(tol.y - tol.x);
else
return 1.0;
}
void main()
{
vec2 fragPos = gl_FragCoord.xy / iResolution.xy;
vec4 texColor0 = texture2D(tex0, fragPos);
vec4 texColor1 = texture2D(tex1, fragPos);
vec4 keyYUV = RGBtoYUV * chromaKey;
vec4 yuv = RGBtoYUV * texColor0;
float mask = 1.0 - colorclose(yuv.rgb, keyYUV.rgb, maskRange);
gl_FragColor = max(texColor0 - mask * chromaKey, 0.0) + texColor1 * mask;
}
顶点着色器:
attribute vec3 aPosition;
void main() {
vec4 positionVec4 = vec4(aPosition, 1.0);
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
gl_Position = positionVec4;
}
P5 js代码片段:
function preload(){
theShader = loadShader('webcam.vert', 'webcam.frag');
img = loadImage('https://princetonlibrary.org/wp-content/uploads/2018/04/p5js.png');
//img = loadImage('https://p5js.org/assets/img/asterisk-01-01.png'); // use alpha
//gl.enable(gl.BLEND);
}
function draw(){
shaderTexture.shader(theShader);
theShader.setUniform('tex0', cam);
theShader.setUniform('tex1', img);
theShader.setUniform('iResolution', [width, height]);
}
设置动态色键透明度
将以下统一添加到碎片着色器
uniform float backgroundAlpha;
将片段着色器的最后一行更改为
gl_FragColor = max(texColor0 - mask * chromaKey, 0.0) + texColor1 * mask;
至
vec4 col = max(texColor0-mask*chromaKey,0.0) + texColor1*mask*vec4(vec3(1), backgroundAlpha);
if (col.a > 0.0) {
gl_FragColor = col;
} else {
discard;
}
然后将制服设置为您想要的背景 alpha 级别
const bgLoc = gl.getUniformLocaton(program, "backgroundAlpha");
gl.uniform1f(bgLoc, 0);
// or with p5
theShader.setUniform('backgroundAlpha', 0); // 0 is full transparent
确保已设置混合模式
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// however you do that in P5.js
重要 确保在它后面的任何对象之后绘制此对象。如果不这样做,则半透明边缘将不会显示背景对象,只会显示渲染时对象后面的内容。
注意如果你希望它总是完全透明你不需要添加第二个纹理贴图,删除tex1
统一和相关代码JS 和片段着色器。然后最后一行就是...(不需要制服backgroundAlpha
)
vec4 col = max(texColor0 - mask * chromaKey, 0.0);
if (col.a > 0.0) {
gl_FragColor = col;
} else {
discard;
}
更好的色键着色器
以下着色器移除 0 到 range.y
范围内的关键颜色 keyRGBA
,使用值 range.x
到 range.y
的(Hermite)曲线平滑过渡。
您的代码使用的颜色 space 转换看起来很奇怪,我找不到任何使用该转换的东西。我使用了颜色 space YCbCr 忽略了亮度 Y
所以只需要色度组件
uniform sampler2D tex0;
uniform vec4 keyRGBA; // key color as rgba
uniform vec2 keyCC; // the CC part of YCC color model of key color
uniform vec2 range; // the smoothstep range
uniform vec2 iResolution;
vec2 RGBToCC(vec4 rgba) {
float Y = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;
return vec2((rgba.b - Y) * 0.565, (rgba.r - Y) * 0.713);
}
void main() {
vec4 src1Color = texture2D(tex0, gl_FragCoord.xy / iResolution);
vec2 CC = RGBToCC(src1Color);
float mask = sqrt(pow(keyCC.x - CC.x, 2.0) + pow(keyCC.y - CC.y, 2.0));
mask = smoothstep(range.x, range.y, mask);
if (mask == 0.0) { discard; }
else if (mask == 1.0) { gl_FragColor = src1Color; }
else { gl_FragColor = max(src1Color - (1.0 - mask) * keyRGBA, 0.0); }
}
设置制服range
,keyRGBA
和keyCC
可以设置如下
const RGBAToCC = (r, g, b) => {
const y = 0.299 * r + 0.587 * g + 0.114 * b;
return [(b - y) * 0.565, (r - y) * 0.713];
};
const keyRGBA = [0.05, 0.63, 0.14, 1]; // the green from your code
const range = [0.11, 0.22]; // A guess at the range needed
const keyCC = RGBAToCC(...keyRGBA);
theShader.setUniform('keyCC', keyCC);
theShader.setUniform('keyRGBA', keyRGBA);
theShader.setUniform('range', range);
注意keyRGBA
的alpha值应该是1