Skip to content

Multicampionamento del framebuffer WebGL

Vogliamo condividere con voi le migliori informazioni che abbiamo trovato online. Speriamo che ti sia d'aiuto e se puoi contribuire con qualcosa che può aiutarci a migliorare le nostre informazioni, fallo liberamente.

Soluzione:

Mi ci è voluto un giorno per capirlo, quindi ho pensato di pubblicare un esempio da seguire. Ho preso in prestito il codice dell'animazione del cubo qui sotto da webgl2fundamentals.org. Tutto ciò che ho aggiunto è il codice che esegue l'antialiasing sulla texture 3d. Assicuratevi che il contesto sia inizializzato con canvas.getContext("webgl2", {antialias: false}); Questo metodo non funzionerà con l'antialiasing attivato.

Per antialiasare una texture generata è necessario inizializzare un elemento Renderbuffer e due oggetti Framebuffer uno per memorizzare il disegno e l'altro per elaborare successivamente la grafica antialias.

  // Create and bind the framebuffer
  const FRAMEBUFFER = 
  {
    RENDERBUFFER: 0,
    COLORBUFFER: 1
  };
  const fb = 
  [
    gl.createFramebuffer(), 
    gl.createFramebuffer()
  ];
  const colorRenderbuffer = gl.createRenderbuffer();

  gl.bindRenderbuffer(gl.RENDERBUFFER, 
                      colorRenderbuffer);

  gl.renderbufferStorageMultisample(gl.RENDERBUFFER,
                                    gl.getParameter(gl.MAX_SAMPLES),
                                    gl.RGBA8, 
                                    targetTextureWidth,
                                    targetTextureHeight);

  gl.bindFramebuffer(gl.FRAMEBUFFER, 
                     fb[FRAMEBUFFER.RENDERBUFFER]);

  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, 
                             gl.COLOR_ATTACHMENT0, 
                             gl.RENDERBUFFER, 
                             colorRenderbuffer);

  gl.bindFramebuffer(gl.FRAMEBUFFER, 
                     fb[FRAMEBUFFER.COLORBUFFER]);

  gl.framebufferTexture2D(gl.FRAMEBUFFER, 
                          gl.COLOR_ATTACHMENT0, 
                          gl.TEXTURE_2D, 
                          targetTexture, 0);

  gl.bindFramebuffer(gl.FRAMEBUFFER, null);

Poco prima di disegnare quella che diventerà la texture, impostare l'oggetto Framebuffer come primo dei due oggetti Framebuffer oggetti.

  // render to our targetTexture by binding the framebuffer
  gl.bindFramebuffer(gl.FRAMEBUFFER, 
                     fb[FRAMEBUFFER.RENDERBUFFER]);

Quindi disegnare la texture e poi eseguire l'antialiasing, che richiederà il secondo buffer.

  // ... drawing code ...
  //
  // "blit" the cube into the color buffer, which adds antialiasing
  gl.bindFramebuffer(gl.READ_FRAMEBUFFER, 
                     fb[FRAMEBUFFER.RENDERBUFFER]);

  gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, 
                     fb[FRAMEBUFFER.COLORBUFFER]);

  gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);

  gl.blitFramebuffer(0, 0, targetTextureWidth, targetTextureHeight,
                     0, 0, targetTextureWidth, targetTextureHeight,
                     gl.COLOR_BUFFER_BIT, gl.LINEAR);

  // render the top layer to the framebuffer as well
  gl.bindFramebuffer(gl.FRAMEBUFFER, 
                     fb[FRAMEBUFFER.RENDERBUFFER]);

Una volta terminato il disegno del livello superiore nel buffer, utilizzare lo stesso metodo di antialiasing di prima, questa volta impostando DRAW_FRAMEBUFFER a nullche indica di disegnare sulla tela vera e propria.

  // this time render to the default buffer, which is just canvas
  gl.bindFramebuffer(gl.READ_FRAMEBUFFER, 
                     fb[FRAMEBUFFER.RENDERBUFFER]);

  gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);

  gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
  gl.blitFramebuffer(0, 0, canvas.width, canvas.height,
                     0, 0, canvas.width, canvas.height,
                     gl.COLOR_BUFFER_BIT, gl.LINEAR);

Ecco il prodotto finito:

"use strict";

var vertexShaderSource = `#version 300 es

// an attribute is an input (in) to a vertex shader.
// It will receive data from a buffer
in vec4 a_position;
in vec2 a_texcoord;

// A matrix to transform the positions by
uniform mat4 u_matrix;

// a varying to pass the texture coordinates to the fragment shader
out vec2 v_texcoord;

// all shaders have a main function
void main() {
  // Multiply the position by the matrix.
  gl_Position = u_matrix * a_position;

  // Pass the texcoord to the fragment shader.
  v_texcoord = a_texcoord;
}
`;

var fragmentShaderSource = `#version 300 es

precision mediump float;

// Passed in from the vertex shader.
in vec2 v_texcoord;

// The texture.
uniform sampler2D u_texture;

// we need to declare an output for the fragment shader
out vec4 outColor;

void main() {
  outColor = texture(u_texture, v_texcoord);
}
`;

function main() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  var canvas = document.getElementById("canvas");
  var gl = canvas.getContext("webgl2", {
    antialias: false
  });
  if (!gl) {
    return;
  }

  // Use our boilerplate utils to compile the shaders and link into a program
  var program = webglUtils.createProgramFromSources(gl, [vertexShaderSource, fragmentShaderSource]);

  // look up where the vertex data needs to go.
  var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
  var texcoordAttributeLocation = gl.getAttribLocation(program, "a_texcoord");

  // look up uniform locations
  var matrixLocation = gl.getUniformLocation(program, "u_matrix");
  var textureLocation = gl.getUniformLocation(program, "u_texture");

  // Create a buffer
  var positionBuffer = gl.createBuffer();

  // Create a vertex array object (attribute state)
  var vao = gl.createVertexArray();

  // and make it the one we're currently working with
  gl.bindVertexArray(vao);

  // Turn on the attribute
  gl.enableVertexAttribArray(positionAttributeLocation);

  // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  // Set Geometry.
  setGeometry(gl);

  // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
  var size = 3; // 3 components per iteration
  var type = gl.FLOAT; // the data is 32bit floats
  var normalize = false; // don't normalize the data
  var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
  var offset = 0; // start at the beginning of the buffer
  gl.vertexAttribPointer(
    positionAttributeLocation, size, type, normalize, stride, offset);

  // create the texcoord buffer, make it the current ARRAY_BUFFER
  // and copy in the texcoord values
  var texcoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  setTexcoords(gl);

  // Turn on the attribute
  gl.enableVertexAttribArray(texcoordAttributeLocation);

  // Tell the attribute how to get data out of colorBuffer (ARRAY_BUFFER)
  var size = 2; // 2 components per iteration
  var type = gl.FLOAT; // the data is 32bit floating point values
  var normalize = true; // convert from 0-255 to 0.0-1.0
  var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next color
  var offset = 0; // start at the beginning of the buffer
  gl.vertexAttribPointer(
    texcoordAttributeLocation, size, type, normalize, stride, offset);

  // Create a texture.
  var texture = gl.createTexture();

  // use texture unit 0
  gl.activeTexture(gl.TEXTURE0 + 0);

  // bind to the TEXTURE_2D bind point of texture unit 0
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // fill texture with 3x2 pixels
  {
    const level = 0;
    const internalFormat = gl.R8;
    const width = 3;
    const height = 2;
    const border = 0;
    const format = gl.RED;
    const type = gl.UNSIGNED_BYTE;
    const data = new Uint8Array([
      128, 64, 128,
      0, 192, 0,
    ]);
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
    gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
      format, type, data);
  }

  // set the filtering so we don't need mips
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  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);

  // Create a texture to render to
  const targetTextureWidth = 512;
  const targetTextureHeight = 512;
  const targetTexture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, targetTexture);

  {
    // define size and format of level 0
    const level = 0;
    const internalFormat = gl.RGBA;
    const border = 0;
    const format = gl.RGBA;
    const type = gl.UNSIGNED_BYTE;
    const data = null;
    gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                  targetTextureWidth, targetTextureHeight, border,
                  format, type, data);

    // set the filtering so we don't need mips
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    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);
  }

  // Create and bind the framebuffer
  const FRAMEBUFFER = {
    RENDERBUFFER: 0,
    COLORBUFFER: 1
  };
  const fb = [gl.createFramebuffer(), gl.createFramebuffer()];
  const colorRenderbuffer = gl.createRenderbuffer();
  gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
  gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, targetTextureWidth, targetTextureHeight);
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb[FRAMEBUFFER.RENDERBUFFER]);
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb[FRAMEBUFFER.COLORBUFFER]);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);

  function degToRad(d) {
    return d * Math.PI / 180;
  }

  var fieldOfViewRadians = degToRad(60);
  var modelXRotationRadians = degToRad(0);
  var modelYRotationRadians = degToRad(0);

  // Get the starting time.
  var then = 0;

  requestAnimationFrame(drawScene);

  function drawCube(aspect) {
    // Tell it to use our program (pair of shaders)
    gl.useProgram(program);

    // Bind the attribute/buffer set we want.
    gl.bindVertexArray(vao);

    // Compute the projection matrix
    var projectionMatrix =
      m4.perspective(fieldOfViewRadians, aspect, 1, 2000);

    var cameraPosition = [0, 0, 2];
    var up = [0, 1, 0];
    var target = [0, 0, 0];

    // Compute the camera's matrix using look at.
    var cameraMatrix = m4.lookAt(cameraPosition, target, up);

    // Make a view matrix from the camera matrix.
    var viewMatrix = m4.inverse(cameraMatrix);

    var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

    var matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
    matrix = m4.yRotate(matrix, modelYRotationRadians);

    // Set the matrix.
    gl.uniformMatrix4fv(matrixLocation, false, matrix);

    // Tell the shader to use texture unit 0 for u_texture
    gl.uniform1i(textureLocation, 0);

    // Draw the geometry.
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 6 * 6;
    gl.drawArrays(primitiveType, offset, count);
  }

  // Draw the scene.
  function drawScene(time) {
    // convert to seconds
    time *= 0.001;
    // Subtract the previous time from the current time
    var deltaTime = time - then;
    // Remember the current time for the next frame.
    then = time;

    // Animate the rotation
    modelYRotationRadians += -0.7 * deltaTime;
    modelXRotationRadians += -0.4 * deltaTime;

    //webglUtils.resizeCanvasToDisplaySize(gl.canvas);

    gl.enable(gl.CULL_FACE);
    gl.enable(gl.DEPTH_TEST);

    {
      // render to our targetTexture by binding the framebuffer
      gl.bindFramebuffer(gl.FRAMEBUFFER, fb[FRAMEBUFFER.RENDERBUFFER]);

      // render cube with our 3x2 texture
      gl.bindTexture(gl.TEXTURE_2D, texture);

      // Tell WebGL how to convert from clip space to pixels
      gl.viewport(0, 0, targetTextureWidth, targetTextureHeight);

      // Clear the canvas AND the depth buffer.
      gl.clearColor(0, 0, 1, 1); // clear to blue
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      const aspect = targetTextureWidth / targetTextureHeight;
      drawCube(aspect);

      // "blit" the cube into the color buffer, which adds antialiasing
      gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb[FRAMEBUFFER.RENDERBUFFER]);
      gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb[FRAMEBUFFER.COLORBUFFER]);
      gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
      gl.blitFramebuffer(0, 0, targetTextureWidth, targetTextureHeight,
                         0, 0, targetTextureWidth, targetTextureHeight,
                         gl.COLOR_BUFFER_BIT, gl.LINEAR);
    }

    {
      // render the top layer to the frame buffer as well
      gl.bindFramebuffer(gl.FRAMEBUFFER, fb[FRAMEBUFFER.RENDERBUFFER]);

      // render the cube with the texture we just rendered to
      gl.bindTexture(gl.TEXTURE_2D, targetTexture);

      // Tell WebGL how to convert from clip space to pixels
      gl.viewport(0, 0, targetTextureWidth, targetTextureHeight);

      // Clear the canvas AND the depth buffer.
      gl.clearColor(0.105, 0.105, 0.105, 1); // clear to black
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      const aspect = 1;
      drawCube(aspect);

      // this time render to the default buffer, which is just canvas
      gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb[FRAMEBUFFER.RENDERBUFFER]);
      gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
      gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
      gl.blitFramebuffer(0, 0, canvas.width, canvas.height,
                         0, 0, canvas.width, canvas.height,
                         gl.COLOR_BUFFER_BIT, gl.LINEAR);
    }

    requestAnimationFrame(drawScene);
  }
}

// Fill the buffer with the values that define a cube.
function setGeometry(gl) {
  var positions = new Float32Array(
    [-0.5, -0.5, -0.5, -0.5, 0.5, -0.5,
      0.5, -0.5, -0.5, -0.5, 0.5, -0.5,
      0.5, 0.5, -0.5,
      0.5, -0.5, -0.5,

      -0.5, -0.5, 0.5,
      0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
      0.5, -0.5, 0.5,
      0.5, 0.5, 0.5,

      -0.5, 0.5, -0.5, -0.5, 0.5, 0.5,
      0.5, 0.5, -0.5, -0.5, 0.5, 0.5,
      0.5, 0.5, 0.5,
      0.5, 0.5, -0.5,

      -0.5, -0.5, -0.5,
      0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
      0.5, -0.5, -0.5,
      0.5, -0.5, 0.5,

      -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,

      0.5, -0.5, -0.5,
      0.5, 0.5, -0.5,
      0.5, -0.5, 0.5,
      0.5, -0.5, 0.5,
      0.5, 0.5, -0.5,
      0.5, 0.5, 0.5,

    ]);
  gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
}

// Fill the buffer with texture coordinates the cube.
function setTexcoords(gl) {
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array(
      [
        0, 0,
        0, 1,
        1, 0,
        0, 1,
        1, 1,
        1, 0,

        0, 0,
        0, 1,
        1, 0,
        1, 0,
        0, 1,
        1, 1,

        0, 0,
        0, 1,
        1, 0,
        0, 1,
        1, 1,
        1, 0,

        0, 0,
        0, 1,
        1, 0,
        1, 0,
        0, 1,
        1, 1,

        0, 0,
        0, 1,
        1, 0,
        0, 1,
        1, 1,
        1, 0,

        0, 0,
        0, 1,
        1, 0,
        1, 0,
        0, 1,
        1, 1,

      ]),
    gl.STATIC_DRAW);
}

main();
html 
{
  background-color: #1b1b1b;
}




WebGL1 non supporta il multicampionamento per i framebuffer, quindi in questo caso le opzioni sono cose come il rendering a una risoluzione più alta e il down sampling quando si esegue il rendering sulla tela e/o l'esecuzione di qualche effetto di post-elaborazione per eseguire l'anti-aliasing.

WebGL2 supporta il multicampionamento per i framebuffer. È possibile chiamare renderbufferStorageMultisample per creare un renderbufffer multicampionato e si può richiamare il comando blitFramebuffer per risolverlo nel canvas

Sezione recensioni e valutazioni

Se ti piace l'argomento, hai il potere di lasciare una divisione su ciò che aggiungeresti a questo saggio.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.