Chromakey GLSL шейдер с прозрачным фоном - PullRequest
0 голосов
/ 20 марта 2020

Это пример P5 js с шейдером кеинга (портирование с шадертой). Как лучше всего сделать фоновое изображение прозрачным? Я попытался изменить его на изображение png с прозрачностью с помощью метода loadImage (), но это не сработало, эскиз не загружается. Я даже пытался включить gl.enable (gl.BLEND) в функции настройки. Должен ли я использовать метод texture2d () для фоновых изображений или просто использовать цвет с альфа-шейдером?

:

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]);
  }

Полный код при сбое

1 Ответ

1 голос
/ 20 марта 2020

Настройка динамичности c Прозрачность цветового ключа

Добавьте следующую униформу в шейдер фрагмента

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;
}

Затем установите униформу на альфа-уровень, необходимый для фона

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;
}

Лучший шейдер кеинга

Следующий шейдер удаляет ключевой цвет keyRGBA внутри диапазон от 0 до range.y сглаживание перехода с помощью кривой (Эрмита) для значений range.x до range.y.

Преобразование цветового пространства, которое использовал ваш код, выглядело очень странно, я не смог найти ничего, что использовало бы это преобразование. Я использовал цветовое пространство Y C b C r Яркость 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 должно быть 1

...