具有透明背景的Chromakey glsl着色器

具有透明背景的Chromakey glsl着色器,glsl,webgl,p5.js,Glsl,Webgl,P5.js,这是一个从shadertoy移植chromakey着色器的P5 js示例 使背景图像透明的最佳方法是什么?我尝试用loadImage方法将其更改为具有透明度的png图像,但没有成功,草图没有加载。我甚至尝试启用 在设置功能中启用gl.BLEND。我应该对背景图像也使用texture2d方法,还是只对alpha使用颜色 片段着色器: precision mediump float; uniform sampler2D tex0; uniform sampler2D tex1; uniform v

这是一个从shadertoy移植chromakey着色器的P5 js示例 使背景图像透明的最佳方法是什么?我尝试用loadImage方法将其更改为具有透明度的png图像,但没有成功,草图没有加载。我甚至尝试启用 在设置功能中启用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;
}
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]);
  }
设置动态色度键透明度 将以下统一添加到frag着色器

uniform float backgroundAlpha;
将frag着色器的最后一行更改为

gl_FragColor = max(texColor0 - mask * chromaKey, 0.0) + texColor1 * mask;

然后将制服设置为背景所需的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
重要信息:请确保在其后面的任何对象之后绘制此对象。如果不显示,则半透明边将不会显示背景对象,仅显示渲染时对象后面的内容

请注意,如果希望它始终是完全透明的,则不需要添加第二个纹理贴图,请删除JS和frag着色器中的tex1统一和关联代码。那么最后一行就是。。。不需要统一的背景

vec4 col = max(texColor0 - mask * chromaKey, 0.0);
if (col.a > 0.0) {
    gl_FragColor = col;
} else {
    discard;
}
更好的chromakey着色器 以下着色器将删除0到range.y范围内的关键颜色keyRGBA,并使用值range.x到range.y的Hermite曲线平滑过渡

你的代码使用的颜色空间转换看起来很奇怪,我找不到任何使用该转换的东西。我使用了颜色空间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); }
}
设置制服范围,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

将以下统一添加到frag着色器

uniform float backgroundAlpha;
将frag着色器的最后一行更改为

gl_FragColor = max(texColor0 - mask * chromaKey, 0.0) + texColor1 * mask;

然后将制服设置为背景所需的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
重要信息:请确保在其后面的任何对象之后绘制此对象。如果不显示,则半透明边将不会显示背景对象,仅显示渲染时对象后面的内容

请注意,如果希望它始终是完全透明的,则不需要添加第二个纹理贴图,请删除JS和frag着色器中的tex1统一和关联代码。那么最后一行就是。。。不需要统一的背景

vec4 col = max(texColor0 - mask * chromaKey, 0.0);
if (col.a > 0.0) {
    gl_FragColor = col;
} else {
    discard;
}
更好的chromakey着色器 以下着色器将删除0到range.y范围内的关键颜色keyRGBA,并使用值range.x到range.y的Hermite曲线平滑过渡

你的代码使用的颜色空间转换看起来很奇怪,我找不到任何使用该转换的东西。我使用了颜色空间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); }
}
设置制服范围,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

非常感谢!我希望绿色屏幕是完全透明的。所以我只使用tex0接收网络摄像头图像。我启用了混合模式并删除了p5代码中的img,还删除了片段着色器中的tex1。但它似乎不起作用。绿色屏幕的颜色仍然是绿色。我打算创建一个这样的场景[链接]@HenriqueBarone色度键控非常敏感,当色度键控背景时,大多数人不会注意到关键点在背景图像中的轻微出血,但当alpha掩蔽时,即使是微小的出血也会显示为非零alpha。对于注释集maskRange=vec20.11中链接的图像中的绿色,0.22,因为示例中的范围太宽。你应该使遮罩范围统一,以便可以根据需要调整,没有两个绿色是相同的,特别是从网络摄像头再次感谢你的帮助!我还更好地照亮了我的绿色屏幕,效果也更好well@Blindman67谢谢你的代码,这工作得很好,如果我想在相同的代码中引入溢出清除,请你指导我需要执行哪些步骤。非常感谢!我希望绿色屏幕是完全透明的。所以我只使用tex0接收网络摄像头图像。我启用了混合模式并删除了p5代码中的img,还删除了片段着色器中的tex1。但它似乎不起作用。绿色屏幕的颜色仍然是绿色。我打算创建一个这样的场景[链接]@HenriqueBarone色度键控非常敏感,当色度键控背景时,大多数人不会注意到关键点在背景图像中的轻微出血,但当alpha掩蔽时,即使是微小的出血也会显示为非零alpha。对于注释集maskRange=vec20.11中链接的图像中的绿色,0.22,因为示例中的范围太宽。您应该使遮罩范围统一,以便可以根据需要对其进行调整,没有两个绿色是相同的
ame,特别是网络摄像头再次感谢您的帮助!我还更好地照亮了我的绿色屏幕,效果也更好well@Blindman67谢谢你的代码,这工作得很好,如果我想在相同的代码中引入溢出清除,请你指导我需要执行哪些步骤。