WebGL2 рендерится в текстуру с MSAA сглаживания и прозрачного фона - PullRequest
0 голосов
/ 04 апреля 2020

Я пытаюсь отобразить набор треугольников с включенным в 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);

Обновление:

Я создал фрагмент переполнения стека, чтобы изолировать проблему. Скрипка др aws сглаженная красным кружком. Пиксели, созданные сглаживанием, становятся зелеными, что является прозрачным цветом мультисэмплерного рендеринга буфера. Кажется, проблема связана с альфа = ложным параметром создания контекста webgl2.

(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>

background color set to transparent black background color set to transparent red expected result, created in image editor

1 Ответ

1 голос
/ 07 апреля 2020

Говорят, что проблема связана с 'alpha = ложным параметром создания контекста webgl2.' предполагает, что проблема заключается в том, как холст смешивается с самой веб-страницей?

A холст составляется (смешивается) с остальной частью HTML на странице, независимо от того, что находится за холстом. Сам элемент canvas может иметь фон CSS, фон страницы <body>. Холст может быть поверх других элементов. Независимо от того, что оно составлено со страницей.

По умолчанию это эффективно смешивается с использованием blendFunc(ONE, ONE_MINUS_SRC_ALPHA), поэтому цвета на холсте должны быть предварительно умноженными альфа-значениями.

Вы можете установить холст без альфа getContext("webgl", {alpha: false}), в этом случае альфа - это эффективность 1,0 для

Вы также можете указать браузеру, что значения пикселей не являются предварительно умноженными альфа, и в этом случае он будет эффективно использовать 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. Это на самом деле зависит от браузера, но ясно, что мой браузер на моем графическом процессоре использует MSAA по умолчанию для canvas.

enter image description here

Если это неясно я хочу сказать, что ваши результаты заключаются в том, что вы не установили {alpha: false}, поэтому, если на холсте есть какие-либо альфа-значения, отличные от 1,0, вы увидите результаты смешивания с HTML цветами за холстом. .

Даже с белым фоном мы можем видеть проблемы в зависимости от настроек

enter image description here

Также обратите внимание, что вы упомянули очистку для (1 0, 0, 0). 1, 0, 0, 0 - недопустимый цвет на холсте по умолчанию «premultipliedAlpha: true». R не может быть> A, если цвета предварительно умножены.

Что происходит в этом случае, не определено. Раньше firefox и chrome вели себя очень по-разному для таких цветов.

...