webgl中的透明帧缓冲区

时间:2015-01-27 12:26:10

标签: javascript canvas webgl fbo

我正在努力在webgl中创建颜色跟踪效果。基本上我希望能够从纹理中选择一系列颜色并让它们在纹理之后跟踪它在屏幕上的动画。我的策略是将纹理绘制到画布以及帧缓冲区,使用additionsl缓冲区选择和淡出所选颜色,从而在图像之后产生轨迹。

我已经成功将preserveAlphaBuffer设置为true并且让路径出现在帧缓冲区中,但我似乎无法让帧缓冲区与后备缓冲区混合。我得到的是具有坚实背景的帧缓冲。我开始觉得它不可能,因为我已经尝试了blendFunc和blendFuncSeparate上的每一个组合。希望有人可以帮助我实现这种效果吗?

var sketch;
function init() {

    sketch = new Sketch();
    document.body.appendChild(sketch);

    sketch.width = window.innerWidth;
    sketch.height = window.innerHeight;

    loop();
}

function loop() {
    requestAnimationFrame(loop);
    sketch.draw();
}


var Sketch = function () {

    var _canvas = document.createElement("canvas");
    var _gl;
    var _image;
    var _program, _fboProgram;
    var _loaded = false;
    var _fbo, _fboTexture,_texture;

    var pos = {
        x: 0, y: 0
    };

    var speed = {
        x: 10,
        y: 10
    }

    function init() {

        //start preloading image
        _image = new Image();
        _image.onload = function () {
            setTimeout(function () {
                render();
            }, 1);
        }
        _image.src = "img/texture.png";
    }

    function render() {
        _gl = _canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});//setupWebGL(canvas, {preserveDrawingBuffer: true});//getWebGLContext(_canvas);

        initCanvas();
        initFbo();

        _loaded = true;
    }

    function initCanvas() {
        _program = initShaders(_gl, "vertex-shader", "fragment-shader");
        _gl.useProgram(_program);
        initVertexShaderLocations(_program);

        _gl.clearColor(1, 1, 1, 0); // red
        _gl.clear(_gl.COLOR_BUFFER_BIT);

        _texture = initTexture(_gl, _image);

        _gl.enable(_gl.BLEND);
        _gl.disable(_gl.DEPTH_TEST);

        _gl.colorMask(1, 1, 1, 1);
    }

    function initFbo() {

        _fboProgram = initShaders(_gl, "vertex-shader", "fbo-fragment-shader");//"2d-fragment-shader");

        _gl.useProgram(_fboProgram);
        initVertexShaderLocations(_fboProgram);

        _texture = initTexture(_gl, _image);

        // create an empty texture
        _fboTexture = _gl.createTexture();
        _gl.bindTexture(_gl.TEXTURE_2D, _fboTexture);

        _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE);
        _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE);
        _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST);
        _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST);
        _gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA, _gl.canvas.width, _gl.canvas.height, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null);

        // Create a framebuffer and attach the texture.
        _fbo = _gl.createFramebuffer();

        _gl.bindFramebuffer(_gl.FRAMEBUFFER, _fbo);
        _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, _fboTexture, 0);

        _gl.viewport(0, 0, _gl.canvas.width, _gl.canvas.height);
        _gl.clearColor(1, 1, 1, 0.2);

        _gl.clear(_gl.COLOR_BUFFER_BIT);

    }


    function drawCanvas() {

        _gl.useProgram(_program);
        _gl.clearColor(1, 1, 1, 1); // red
        _gl.clear(_gl.COLOR_BUFFER_BIT);

        renderTexture(_program);
        renderFbo(_program);

        _loaded = true;
    }

    function drawFbo() {
        _gl.bindFramebuffer(_gl.FRAMEBUFFER, _fbo);

        _gl.useProgram(_fboProgram);
         _gl.blendFunc(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA);

        var blur = [
            0, 1, 0,
            1, 1, 1,
            0, 1, 0
        ];

        var kernelLocation = _gl.getUniformLocation(_fboProgram, "u_kernel[0]");
        _gl.uniform1fv(kernelLocation, blur);

        var textureSizeLocation = _gl.getUniformLocation(_fboProgram, "u_textureSize");
        _gl.uniform2f(textureSizeLocation, _image.width, _image.height);

        // Render to the texture (using clear because it's simple)
        _gl.clearColor(1, 1, 1, 0); // green;
        _gl.clear(_gl.COLOR_BUFFER_BIT);

        renderTexture(_fboProgram);

        _gl.bindFramebuffer(_gl.FRAMEBUFFER, null);
    }


    function renderFbo(program) {

        _gl.uniform2f(program.resolutionLocation, _gl.canvas.width, _gl.canvas.height);
        _gl.uniform1f(program.flipYLocation, 1);

        _gl.bindBuffer(_gl.ARRAY_BUFFER, program.texCoordBuffer);
        setRectangle(_gl, 0, 0, 1, 1);
        _gl.enableVertexAttribArray(program.texCoordLocation);
        _gl.vertexAttribPointer(program.texCoordLocation, 2, _gl.FLOAT, false, 0, 0);

        _gl.bindBuffer(_gl.ARRAY_BUFFER, program.positionBuffer);
        setRectangle(_gl, 0, 0, _gl.canvas.width, _gl.canvas.height);
        _gl.vertexAttribPointer(program.positionLocation, 2, _gl.FLOAT, false, 0, 0);

        _gl.bindTexture(_gl.TEXTURE_2D, _fboTexture);
        _gl.drawArrays(_gl.TRIANGLES, 0, 6);
    }

    function renderTexture(program) {

        _gl.uniform1f(program.flipYLocation, 1.0);

        _gl.bindBuffer(_gl.ARRAY_BUFFER, program.texCoordBuffer);
        setRectangle(_gl, 0, 0, 1, 1);
        _gl.enableVertexAttribArray(program.texCoordLocation);
        _gl.vertexAttribPointer(program.texCoordLocation, 2, _gl.FLOAT, false, 0, 0);

        _gl.bindBuffer(_gl.ARRAY_BUFFER, program.positionBuffer);
        setRectangle(_gl, pos.x, pos.y, _image.width, _image.height);
        _gl.vertexAttribPointer(program.positionLocation, 2, _gl.FLOAT, false, 0, 0);
        _gl.enableVertexAttribArray(program.positionLocation);

        // Tell the shader the resolution of the framebuffer.
        _gl.uniform2f(program.resolutionLocation, _gl.canvas.width, _gl.canvas.height);

        _gl.bindTexture(_gl.TEXTURE_2D, _texture);
        _gl.drawArrays(_gl.TRIANGLES, 0, 6);
    }

    function setRectangle(gl, x, y, width, height) {
        var x1 = x;
        var x2 = x + width;
        var y1 = y;
        var y2 = y + height;
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            x1, y1,
            x2, y1,
            x1, y2,
            x1, y2,
            x2, y1,
            x2, y2]), gl.STATIC_DRAW);
    }

    function initVertexShaderLocations(program){
        program.texCoordLocation =  _gl.getAttribLocation(program, "a_texCoord");
        program.positionLocation =  _gl.getAttribLocation(program, "a_position");
        program.resolutionLocation = _gl.getUniformLocation(program, "u_resolution");
        program.flipYLocation = _gl.getUniformLocation(program, "u_flipY");
        program.positionBuffer = _gl.createBuffer();
        program.texCoordBuffer = _gl.createBuffer();
    }

    function initTexture(gl, image) {

        var texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);

        // Set up texture so we can render any size image and so we are
        // working with pixels.
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

        return texture;
    }

    function initShaders(gl, vert, frag) {
        // setup a GLSL program
        var vertexShader = createShaderFromScriptElement(gl, vert);
        var fragmentShader = createShaderFromScriptElement(gl, frag);
        return createProgram(gl, [vertexShader, fragmentShader]);
    }

    _canvas.draw = function () {

        if (!_loaded)
            return;

        if (pos.x + 256 > _gl.canvas.width || pos.x < 0) speed.x *= -1;
        if (pos.y + 256 > _gl.canvas.height || pos.y < 0) speed.y *= -1;

        pos.x += speed.x;
        pos.y += speed.y;

        drawFbo();
        drawCanvas();
    }

    init();
    return _canvas;

}


init();
<body>


<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
uniform vec2 u_resolution;
uniform float u_flipY;

void main() {

   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;

   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;

   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;

   gl_Position = vec4(clipSpace * vec2(1, u_flipY), 0, 1);

    v_texCoord = a_texCoord;
}

</script>


<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   // Look up a color from the texture.
   vec4 color = texture2D(u_image, v_texCoord);

   if(color.a < 0.9)
    color.a = 0.1;

   gl_FragColor = color;
}


</script>
<script id="fbo-fragment-shader" type="x-shader/x-fragment">
    precision mediump float;

    // our texture
    uniform sampler2D u_image;
    // the texCoords passed in from the vertex shader.
    varying vec2 v_texCoord;

    void main() {

        vec4 color = texture2D(u_image, v_texCoord);

       if(color.r > 0.5)
            color.a = 0.01;

        gl_FragColor = color;
    }

</script>


</body>

1 个答案:

答案 0 :(得分:0)

所以我做了这个简短的演示:

http://jsfiddle.net/windkiller/c46Lvbzp/

// custom fn

function createShaderFromScriptElement(gl, scriptId) {
    var shaderSource = "",
        shaderType;
    var shaderScript = document.getElementById(scriptId);
    if (!shaderScript) {
        throw ("*** Error: unknown script element" + scriptId);
    }
    shaderSource = shaderScript.text;

    if (shaderScript.type == "x-shader/x-vertex") {
        shaderType = gl.VERTEX_SHADER;
    } else if (shaderScript.type == "x-shader/x-fragment") {
        shaderType = gl.FRAGMENT_SHADER;
    } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
        throw ("*** Error: unknown shader type");
        return null;
    }

    var shader = gl.createShader(shaderType);

    // Load the shader source
    gl.shaderSource(shader, shaderSource);

    // Compile the shader
    gl.compileShader(shader);

    // Check the compile status
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
        // Something went wrong during compilation; get the error
        var lastError = gl.getShaderInfoLog(shader);
        errFn("*** Error compiling shader '" + shader + "':" + lastError);
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

function createProgram(gl, shaders) {

    var shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, shaders[0]);
    gl.attachShader(shaderProgram, shaders[1]);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
    }
    // HINT dont use program here
    return shaderProgram;
}

我必须手动完成一些事情,这是摘要: *缺少createShaderFromScript和createProgram函数,所以我做到了。我相信你有它们,所以请忽略它。 *我没有纹理,所以我用绿色和红色制作了base64图像并将其用作纹理。它很长,所以我没有在这里附加代码。

此后,演示开始起作用。但正如你所报告的那样,它只是浮​​动图像。我试图调查你的代码,但我太懒了,没有那么多时间。

但我觉得你有错误,以便你如何使用帧缓冲区!所以我试图交换绘制调用:

    drawCanvas();
    drawFbo();

它做了一些事!所以我也: *增加速度 *稍微更改着色器(只是alpha计算的变量) *按钮停止/开始

现在,当您运行演示并使用停止时,您将看到两张图片,一张绿色和第二张混合绿色,其中更多红色=不太可见(绿色更大,然后是红色)。

所以我认为你非常接近完成它。问题必须在于如何绘制帧缓冲区以及生成纹理时的顺序。您是否看到我在评论中提供的链接?