0

我正在尝试在 WebGL2 中启用多样本抗锯齿 MSAA 来渲染一组三角形。因此,我正在使用多样本渲染缓冲区设置渲染管道以渲染到目标纹理。抗锯齿似乎有效,但是如果我尝试将场景渲染到透明渲染缓冲区,尽管它是完全透明的,但抗锯齿似乎逐渐融合到不透明的背景颜色中。

在下面的示例图像中,绘制了一组绿色 rgb(0,1,0,1) 三角形:首先将背景透明色设置为 gl.clearColor(0, 0, 0, 0) - 第二个将透明色设置为gl.clearColor(1, 0, 0, 0) - (生成的纹理混合在白色背景上以显示结果)。

如何将场景渲染为透明纹理,抗锯齿从 rgba(0,0,255,1) 逐渐变为 rgba(0,0,0,0)?

//initialization code
gl.frameBufferAA = gl.createFramebuffer();
//render code
let renderBufferAA = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderBufferAA);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, texDst.width, texDst.height);

//attach renderBufferAA to frameBufferRenderBuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.frameBufferAA);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderBufferAA);
gl.clearColor(0, 0, 0, 0);  //<--- transparent color affects anti-aliasing 
gl.colorMask(true, true, true, true);
gl.clear(gl.COLOR_BUFFER_BIT);

twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);

//blit renderBuffe
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, gl.frameBufferAA);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, gl.frameBuffer1);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texDst, 0);
gl.blitFramebuffer(
  0, 0, texDst.width, texDst.height,
  0, 0, texDst.width, texDst.height,
  gl.COLOR_BUFFER_BIT, gl.NEAREST
);

gl.deleteRenderbuffer(renderBufferAA);

更新:

我创建了一个堆栈溢出片段来隔离问题。小提琴画了一个抗锯齿的红色圆圈。抗锯齿创建的像素逐渐变为绿色,这是多重采样渲染缓冲区的清晰颜色。该问题似乎与 webgl2 上下文的 alpha=false 创建参数有关。

(function () {
        'use strict';

        var canvas = document.createElement('canvas');
        canvas.width = Math.min(window.innerWidth, window.innerHeight);
        canvas.height = canvas.width;
        document.body.appendChild(canvas);

        var gl = canvas.getContext( 'webgl2', { antialias: false, alpha: false } );
        var isWebGL2 = !!gl;
        if(!isWebGL2) {
            document.getElementById('info').innerHTML = 'WebGL 2 is not available.  See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
            return;
        }

        // -- Init program
        var PROGRAM = {
            TEXTURE: 0,
            SPLASH: 1,
            MAX: 2
        };

        var programs = [
            createProgram(gl, getShaderSource('vs-render'), getShaderSource('fs-render')),
            createProgram(gl, getShaderSource('vs-splash'), getShaderSource('fs-splash'))
        ];
        var mvpLocationTexture = gl.getUniformLocation(programs[PROGRAM.TEXTURE], 'MVP');
        var mvpLocation = gl.getUniformLocation(programs[PROGRAM.SPLASH], 'MVP');
        var diffuseLocation = gl.getUniformLocation(programs[PROGRAM.SPLASH], 'diffuse');

        // -- Init primitive data
        var vertexCount = 18;
        var data = new Float32Array(vertexCount * 2);
        var angle;
        var radius = 0.1;
        for(var i = 0; i < vertexCount; i++ )
        {
            angle = Math.PI * 2 * i / vertexCount;
            data[2 * i] = radius * Math.sin(angle);
            data[2 * i + 1] = radius * Math.cos(angle);
        }

        // -- Init buffers
        var vertexDataBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var positions = new Float32Array([
            -1.0, -1.0,
             1.0, -1.0,
             1.0,  1.0,
             1.0,  1.0,
            -1.0,  1.0,
            -1.0, -1.0
        ]);
        var vertexPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var texCoords = new Float32Array([
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,
            1.0, 0.0,
            0.0, 0.0,
            0.0, 1.0
        ]);
        var vertexTexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        // -- Init Texture
        // used for draw framebuffer storage
        var FRAMEBUFFER_SIZE = {
            x: canvas.width,
            y: canvas.height
        };
        var texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        gl.bindTexture(gl.TEXTURE_2D, null);

        // -- Init Frame Buffers
        var FRAMEBUFFER = {
            RENDERBUFFER: 0,
            COLORBUFFER: 1
        };
        var framebuffers = [
            gl.createFramebuffer(),
            gl.createFramebuffer()
        ];
        var colorRenderbuffer = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
        gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y);

        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);

        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.COLORBUFFER]);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);

        // -- Init VertexArray
        var vertexArrays = [
            gl.createVertexArray(),
            gl.createVertexArray()
        ];

        var vertexPosLocation = 0; // set with GLSL layout qualifier

        gl.bindVertexArray(vertexArrays[PROGRAM.TEXTURE]);
        gl.enableVertexAttribArray(vertexPosLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
        gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        gl.bindVertexArray(null);

        gl.bindVertexArray(vertexArrays[PROGRAM.SPLASH]);

        gl.enableVertexAttribArray(vertexPosLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var vertexTexLocation = 1; // set with GLSL layout qualifier
        gl.enableVertexAttribArray(vertexTexLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.vertexAttribPointer(vertexTexLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.bindVertexArray(null);

        // -- Render

        // Pass 1
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
        gl.clearBufferfv(gl.COLOR, 0, [0.0, 1.0, 0.0, 0.0]);
        gl.useProgram(programs[PROGRAM.TEXTURE]);
        gl.bindVertexArray(vertexArrays[PROGRAM.TEXTURE]);

        var IDENTITY = mat4.create();
        gl.uniformMatrix4fv(mvpLocationTexture, false, IDENTITY);

        gl.enable(gl.blend);
        gl.blendFunc(gl.SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA);
        gl.drawArrays(gl.LINE_LOOP, 0, vertexCount);

        // Blit framebuffers, no Multisample texture 2d in WebGL 2
        gl.bindFramebuffer(gl.READ_FRAMEBUFFER, framebuffers[FRAMEBUFFER.RENDERBUFFER]);
        gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, framebuffers[FRAMEBUFFER.COLORBUFFER]);
        gl.blitFramebuffer(
            0, 0, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y,
            0, 0, FRAMEBUFFER_SIZE.x, FRAMEBUFFER_SIZE.y,
            gl.COLOR_BUFFER_BIT, gl.NEAREST
        );

        // Pass 2
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.useProgram(programs[PROGRAM.SPLASH]);
        gl.uniform1i(diffuseLocation, 0);
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.bindVertexArray(vertexArrays[PROGRAM.SPLASH]);

        gl.clearColor(1.0, 1.0, 1.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        var scaleVector3 = vec3.create();
        vec3.set(scaleVector3, 8.0, 8.0, 8.0);
        var mvp = mat4.create();
        mat4.scale(mvp, IDENTITY, scaleVector3);

        gl.uniformMatrix4fv(mvpLocation, false, mvp);

        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        gl.drawArrays(gl.TRIANGLES, 0, 6);

        // -- Delete WebGL resources
        gl.deleteBuffer(vertexPosBuffer);
        gl.deleteBuffer(vertexTexBuffer);
        gl.deleteTexture(texture);
        gl.deleteRenderbuffer(colorRenderbuffer);
        gl.deleteFramebuffer(framebuffers[FRAMEBUFFER.RENDERBUFFER]);
        gl.deleteFramebuffer(framebuffers[FRAMEBUFFER.COLORBUFFER]);
        gl.deleteVertexArray(vertexArrays[PROGRAM.TEXTURE]);
        gl.deleteVertexArray(vertexArrays[PROGRAM.SPLASH]);
        gl.deleteProgram(programs[PROGRAM.TEXTURE]);
        gl.deleteProgram(programs[PROGRAM.SPLASH]);

    })();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block }
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js" integrity="sha256-+09xst+d1zIS41eAvRDCXOf0MH993E4cS40hKBIJj8Q=" crossorigin="anonymous"></script>
<script>
(function () {
    'use strict';

    window.getShaderSource = function(id) {
        return document.getElementById(id).textContent.replace(/^\s+|\s+$/g, '');
    };

    function createShader(gl, source, type) {
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        return shader;
    }

    window.createProgram = function(gl, vertexShaderSource, fragmentShaderSource) {
        var program = gl.createProgram();
        var vshader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
        var fshader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
        gl.attachShader(program, vshader);
        gl.deleteShader(vshader);
        gl.attachShader(program, fshader);
        gl.deleteShader(fshader);
        gl.linkProgram(program);

        var log = gl.getProgramInfoLog(program);
        if (log) {
            console.log(log);
        }

        log = gl.getShaderInfoLog(vshader);
        if (log) {
            console.log(log);
        }

        log = gl.getShaderInfoLog(fshader);
        if (log) {
            console.log(log);
        }

        return program;
    };        
})();
</script>
<!-- vertex shader -->
<!-- WebGL 2 shaders -->
<script id="vs-render" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 0

        precision highp float;
        precision highp int;

        uniform mat4 MVP;

        layout(location = POSITION_LOCATION) in vec2 position;

        void main()
        {
            gl_Position = MVP * vec4(position, 0.0, 1.0);
        }
</script>

<script id="fs-render" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;

        out vec4 color;

        void main()
        {
            color = vec4(1.0, 0.0, 0.0, 1.0);
        }
</script>

<script id="vs-splash" type="x-shader/x-vertex">
        #version 300 es
        precision highp float;
        precision highp int;

        uniform mat4 MVP;

        layout(location = 0) in vec2 position;
        layout(location = 1) in vec2 texcoord;

        out vec2 uv;

        void main()
        {
            uv = texcoord;
            gl_Position = MVP * vec4(position, 0.0, 1.0);
        }
</script>

<script id="fs-splash" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;

        uniform sampler2D diffuse;

        in vec2 uv;

        out vec4 color;

        void main()
        {
            color = texture(diffuse, uv);
        }
</script>

<script>
    
    </script>

背景颜色设置为透明黑色 背景颜色设置为透明红色 预期结果,在图像编辑器中创建

4

1 回答 1

2

说这个问题似乎与“webgl2 上下文的 alpha=false 创建参数”有关。暗示问题是画布如何与网页本身融合?

画布与页面中的其余 HTML 组合(混合),无论画布后面是什么。画布元素本身可以有 CSS 背景,页面<body>也可以有背景。画布可以在其他元素之上。不管它是与页面合成的。

默认值是使用有效混合的,blendFunc(ONE, ONE_MINUS_SRC_ALPHA)因此画布中的颜色需要预乘 alpha 值。

您可以将画布设置为没有 alpha getContext("webgl", {alpha: false}),在这种情况下,alpha 为有效 1.0

您还可以告诉浏览器您的像素值是未预乘的 alpha,在这种情况下,它将有效地用于blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA)在页面中合成画布。你用getContext("webgl, {premultipliedAlpha: false}.

我建议您将画布的背景颜色设置为可以清楚地说明正在发生的事情。例如。

canvas {
  background-color: #FF0;
  background-image: 
      linear-gradient(45deg, #F0F 25%, transparent 25%), 
      linear-gradient(-45deg, #F0F 25%, transparent 25%), 
      linear-gradient(45deg, transparent 75%, #F0F 75%), 
      linear-gradient(-45deg, transparent 75%, #F0F 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
<canvas></canvas>

然后画东西

function main(attribs = {}) {
  const canvas = document.createElement('canvas');
  canvas.width = 150;
  canvas.height = 50;
  document.body.appendChild(canvas);
  const gl = canvas.getContext('webgl2', attribs);
  if (!gl) {
    return alert('need webgl2');
  }
  log('attribs:', JSON.stringify(gl.getContextAttributes()));
  
  const vs = `
  attribute vec4 position;
  void main() {
    gl_Position = position;
  }
  `;
  const fs = `
  precision highp float;
  uniform vec4 color;
  void main() {
    gl_FragColor = color;
  }
  `;
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    position: [
      -0.8, 0.5, 0,
       0.8, 0.4, 0,
       0.0,-0.5, 0,
    ],
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.setUniforms(programInfo, {color: [0, 1, 0, 1]});
  twgl.drawBufferInfo(gl, bufferInfo);
}

function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}

main({});
main({premultipliedAlpha: false});
main({antialias: false});
canvas {
  image-rendering: pixelated;
  background-color: #FF0;
  background-image: 
      linear-gradient(45deg, #F0F 25%, transparent 25%), 
      linear-gradient(-45deg, #F0F 25%, transparent 25%), 
      linear-gradient(45deg, transparent 75%, #F0F 75%), 
      linear-gradient(-45deg, transparent 75%, #F0F 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

如果我把它们炸毁,你会看到区别。您还可以看到它正在与 HTML 的背景混合。

注意:我没有设置 MSAA 渲染缓冲区,因为通常画布本身默认是 MSAA 渲染缓冲区。这实际上取决于浏览器,但很明显我的 GPU 上的浏览​​器默认使用 MSAA 作为画布。

在此处输入图像描述

如果不清楚,我的建议是你的结果是你没有设置的{alpha: false},所以如果你的画布中有任何非 1.0 的 alpha 值,那么你会看到与后面的 HTML 颜色混合的结果帆布。

即使使用白色背景,我们也可以根据设置看到问题

在此处输入图像描述

另请注意,您提到清除 (1, 0, 0, 0)。1, 0, 0, 0 是默认“premultipliedAlpha: true”画布上的无效颜色。如果颜色被预乘,R 不能 > A。

在那种情况下会发生什么是未定义的。过去,Firefox 和 chrome 对这种颜色的表现非常不同。

于 2020-04-07T04:15:30.180 回答