WebGL 混合实例精灵渲染

WebGL mix instanced sprite rendering

我想使用 drawArraysInstancedANGLE 渲染一些实例化精灵。问题是,当我为要实例化的缓冲区设置 vertexAttribDivisorANGLE 时,它会清除整个屏幕,擦除我之前绘制的所有内容。我将此示例基于

带有 vertexAttribDivisorANGLE 问题的示例(仅绘制实例化框):

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
  alert('need ANGLE_instanced_arrays');
}


// create a simple background shader

const backgroundVs = `
  attribute vec2 position;
 
  void main() {
    gl_Position = vec4(position, 0.0, 1.0);
  }
`;

const backgroundFs = `
precision mediump float;

uniform vec2 resolution;

void main() {
  vec2 uv = gl_FragCoord.xy/resolution.xy;
  vec3 color = uv.xyx;
  gl_FragColor = vec4(color, 1.0);
  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
  gl_FragColor = vec4(color,1.0);
}
`;


const backgroundProgramInfo = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundProgram = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundPositionLoc = gl.getAttribLocation(backgroundProgram, 'position');
const backgroundResolutionLoc = gl.getUniformLocation(backgroundProgram, 'resolution');

const backgroundQuad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  1, 1,   // 1 +-----+ 2
  -1, 1,  // |       |
  1, -1,  // |       |
  1, -1,  // |       |
  -1, 1,  // |       |
  -1, -1, // 3 +-----+ 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);


//Create an instanced point renderer (based on: )

const vs = `
  attribute vec4 position;       // center point
  attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
  
  uniform vec2 resolution;
  uniform mat4 matrix;
  
  varying vec3 pointCoord;  // only if you need gl_PointCoord substitute
  
  void main() {
    // do the normal thing (can mult by matrix or whatever here
    gl_Position = matrix * position; 

    float pointSize = 20.0 / gl_Position.w;
    
    // -- point emulation
    
    gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) / 
                      resolution * gl_Position.w;
    
    // only if you need gl_PointCoord substitute
    pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
  }
`;

const fs = `
precision mediump float;
void main() {
  gl_FragColor = vec4(1, 0, 0, 1);
}
`;

const programInfo = twgl.createProgram(gl, [vs, fs]);

const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');

const bufSprites = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1.001, -1.001,
   1.001, -1.001,
  -1.001,  1.001,
   1.001,  1.001,
   0,      0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);

const bufCorners = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.bufferData(gl.ARRAY_BUFFER, 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,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);


function render(ms) {
  const secs = ms * 0.001;

  gl.useProgram(backgroundProgram);

  gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
  gl.enableVertexAttribArray(backgroundPositionLoc);
  gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.uniform2f(backgroundResolutionLoc, gl.canvas.width, gl.canvas.height);
  gl.drawArrays(gl.TRIANGLES, 0, 6);


  const mat = m4.perspective(
      60 * Math.PI / 180,
      gl.canvas.clientWidth / gl.canvas.clientHeight,
      0.1,
      100);
  m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
  

  gl.useProgram(program);

  gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
  ext.vertexAttribDivisorANGLE(positionLoc, 1);

  gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
  gl.enableVertexAttribArray(cornerPositionLoc);
  gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
  gl.uniformMatrix4fv(matrixLoc, false, mat);
  
  // 6 verts per point
  ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5);
  
  requestAnimationFrame(render);
}
render(0);
<html>

    <head>
        <style>canvas { border: 1px solid black; width: 100%; height: 100%;}</style>
        <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    </head>

    <body>
        <canvas></canvas>
    </body>
    
</html>

现在当我禁用 ext.vertexAttribDivisorANGLE(positionLoc, 1);调用,背景渲染如预期,框在背景上绘制,但显然网格不正确,因为实例化/除数设置不正确:

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
  alert('need ANGLE_instanced_arrays');
}


// create a simple background shader

const backgroundVs = `
  attribute vec2 position;
 
  void main() {
    gl_Position = vec4(position, 0.0, 1.0);
  }
`;

const backgroundFs = `
precision mediump float;

uniform vec2 resolution;

void main() {
  vec2 uv = gl_FragCoord.xy/resolution.xy;
  vec3 color = uv.xyx;
  gl_FragColor = vec4(color, 1.0);
  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
  gl_FragColor = vec4(color,1.0);
}
`;


const backgroundProgramInfo = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundProgram = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundPositionLoc = gl.getAttribLocation(backgroundProgram, 'position');
const backgroundResolutionLoc = gl.getUniformLocation(backgroundProgram, 'resolution');

const backgroundQuad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  1, 1,   // 1 +-----+ 2
  -1, 1,  // |       |
  1, -1,  // |       |
  1, -1,  // |       |
  -1, 1,  // |       |
  -1, -1, // 3 +-----+ 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);


//Create an instanced point renderer (based on: )

const vs = `
  attribute vec4 position;       // center point
  attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
  
  uniform vec2 resolution;
  uniform mat4 matrix;
  
  varying vec3 pointCoord;  // only if you need gl_PointCoord substitute
  
  void main() {
    // do the normal thing (can mult by matrix or whatever here
    gl_Position = matrix * position; 

    float pointSize = 20.0 / gl_Position.w;
    
    // -- point emulation
    
    gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) / 
                      resolution * gl_Position.w;
    
    // only if you need gl_PointCoord substitute
    pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
  }
`;

const fs = `
precision mediump float;
void main() {
  gl_FragColor = vec4(1, 0, 0, 1);
}
`;

const programInfo = twgl.createProgram(gl, [vs, fs]);

const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');

const bufSprites = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1.001, -1.001,
   1.001, -1.001,
  -1.001,  1.001,
   1.001,  1.001,
   0,      0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);

const bufCorners = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.bufferData(gl.ARRAY_BUFFER, 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,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);


function render(ms) {
  const secs = ms * 0.001;

  gl.useProgram(backgroundProgram);

  gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
  gl.enableVertexAttribArray(backgroundPositionLoc);
  gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.uniform2f(backgroundResolutionLoc, gl.canvas.width, gl.canvas.height);
  gl.drawArrays(gl.TRIANGLES, 0, 6);


  const mat = m4.perspective(
      60 * Math.PI / 180,
      gl.canvas.clientWidth / gl.canvas.clientHeight,
      0.1,
      100);
  m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
  

  gl.useProgram(program);

  gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
  //ext.vertexAttribDivisorANGLE(positionLoc, 1);

  gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
  gl.enableVertexAttribArray(cornerPositionLoc);
  gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
  gl.uniformMatrix4fv(matrixLoc, false, mat);
  
  // 6 verts per point
  ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5);
  
  requestAnimationFrame(render);
}
render(0);
<html>

    <head>
        <style>canvas { border: 1px solid black; width: 100%; height: 100%;}</style>
        <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    </head>

    <body>
        <canvas></canvas>
    </body>
    
</html>

如何在不擦除背景的情况下在背景上正确渲染实例化框?

编辑:删除了一些不必要的注释行

编辑 2:正如 user253751 所指出的,我需要使用 ext.vertexAttribDivisorANGLE(positionLoc, 0) 关闭除数;在调用 drawArraysInstancedANGLE 之后

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
  alert('need ANGLE_instanced_arrays');
}


// create a simple background shader

const backgroundVs = `
  attribute vec2 position;
 
  void main() {
    gl_Position = vec4(position, 0.0, 1.0);
  }
`;

const backgroundFs = `
precision mediump float;

uniform vec2 resolution;

void main() {
  vec2 uv = gl_FragCoord.xy/resolution.xy;
  vec3 color = uv.xyx;
  gl_FragColor = vec4(color, 1.0);
  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
  gl_FragColor = vec4(color,1.0);
}
`;


const backgroundProgramInfo = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundProgram = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundPositionLoc = gl.getAttribLocation(backgroundProgram, 'position');
const backgroundResolutionLoc = gl.getUniformLocation(backgroundProgram, 'resolution');

const backgroundQuad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  1, 1,   // 1 +-----+ 2
  -1, 1,  // |       |
  1, -1,  // |       |
  1, -1,  // |       |
  -1, 1,  // |       |
  -1, -1, // 3 +-----+ 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);


//Create an instanced point renderer (based on: )

const vs = `
  attribute vec4 position;       // center point
  attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
  
  uniform vec2 resolution;
  uniform mat4 matrix;
  
  varying vec3 pointCoord;  // only if you need gl_PointCoord substitute
  
  void main() {
    // do the normal thing (can mult by matrix or whatever here
    gl_Position = matrix * position; 

    float pointSize = 20.0 / gl_Position.w;
    
    // -- point emulation
    
    gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) / 
                      resolution * gl_Position.w;
    
    // only if you need gl_PointCoord substitute
    pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
  }
`;

const fs = `
precision mediump float;
void main() {
  gl_FragColor = vec4(1, 0, 0, 1);
}
`;

const programInfo = twgl.createProgram(gl, [vs, fs]);

const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');

const bufSprites = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1.001, -1.001,
   1.001, -1.001,
  -1.001,  1.001,
   1.001,  1.001,
   0,      0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);

const bufCorners = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.bufferData(gl.ARRAY_BUFFER, 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,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);


function render(ms) {
  const secs = ms * 0.001;

  gl.useProgram(backgroundProgram);

  gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
  gl.enableVertexAttribArray(backgroundPositionLoc);
  gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.uniform2f(backgroundResolutionLoc, gl.canvas.width, gl.canvas.height);
  gl.drawArrays(gl.TRIANGLES, 0, 6);


  const mat = m4.perspective(
      60 * Math.PI / 180,
      gl.canvas.clientWidth / gl.canvas.clientHeight,
      0.1,
      100);
  m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
  

  gl.useProgram(program);

  gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
  ext.vertexAttribDivisorANGLE(positionLoc, 1);

  gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
  gl.enableVertexAttribArray(cornerPositionLoc);
  gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);

  gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
  gl.uniformMatrix4fv(matrixLoc, false, mat);
  
  // 6 verts per point
  ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5);
  ext.vertexAttribDivisorANGLE(positionLoc, 0);
 
  requestAnimationFrame(render);
}
render(0);
<html>

    <head>
        <style>canvas { border: 1px solid black; width: 100%; height: 100%;}</style>
        <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    </head>

    <body>
        <canvas></canvas>
    </body>
    
</html>

您需要在绘制背景之前将除数设置为 0 以关闭除数。不然背景也用了,背景画错了。