я могу использовать несколько текстур с drawArraysInstancedANGLE? - PullRequest
0 голосов
/ 09 января 2020

Я пытаюсь нарисовать несколько значков на экране и использую метод drawArraysInstancedANGLE. Я пытаюсь использовать несколько текстур, как это, но некоторые иконки рисуют различные геометрии, я не могу найти то, что рисует, как это.

Я использую одну большую текстуру карты иконок и заполняю массив координат вершин иконок этим забавным c:

  FillIconTextCoordBuffer(data, mapW, mapH, i) {
    const ULiconW = data.x / mapW
    const ULiconH = data.y / mapH
    const LRiconW = (data.x + data.width) / mapW
    const LRiconH = (data.y + data.height) / mapH
    const { gl } = this.FGlobe

    this.IconMapTexCoordArr[i] = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, this.IconMapTexCoordArr[i])
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      ULiconW, ULiconH,
      LRiconW, LRiconH,
      LRiconW, ULiconH,
      LRiconW, LRiconH,
      ULiconW, ULiconH,
      ULiconW, LRiconH]), gl.STATIC_DRAW)
  }

, а затем моя забавная ничья c вот так:

  gl.uniform1f(P2DRotationForLayer, icon.rotDeg)
  gl.uniform2fv(P2DScaleLocForLayer, icon.__size)
  gl.uniform4fv(P2DOpacityLocForLayer, __iconColor)

  ext.vertexAttribDivisorANGLE(P2DoffsetForLayer, 1) // This makes it instanced!

  gl.bindBuffer(gl.ARRAY_BUFFER, this.IconMapVertCoordArr)
  gl.enableVertexAttribArray(P2DvertexPosForLayer)
  gl.vertexAttribPointer(P2DvertexPosForLayer, 3, gl.FLOAT, false, 0, 0)

  gl.bindBuffer(gl.ARRAY_BUFFER, this.IconCoordBuff)
  gl.enableVertexAttribArray(P2DoffsetForLayer)
  gl.vertexAttribPointer(P2DoffsetForLayer, 2, gl.FLOAT, false, 0, 0)

  gl.bindTexture(gl.TEXTURE_2D, IconMap[icon.mapIndex].texture)
  gl.disable(gl.BLEND)
  for (var j = this.StartCountArr.length; j--;) {
     this.DrawInstances(this.StartCountArr[j].start, this.StartCountArr[j].count, j)
  }

  ext.vertexAttribDivisorANGLE(P2DoffsetForLayer, 0)

и моя забавная ничья c вот так:

DrawInstances(start, count, j) {
    const {
      gl, ext,
      P2DtextCoordLocForLayer,
    } = this.FGlobe

    gl.bindBuffer(gl.ARRAY_BUFFER, this.IconMapTexCoordArr[j])
    gl.enableVertexAttribArray(P2DtextCoordLocForLayer)
    gl.vertexAttribPointer(P2DtextCoordLocForLayer, 2, gl.FLOAT, false, 0, 0)
    gl.bindBuffer(gl.ARRAY_BUFFER, null)
    ext.drawArraysInstancedANGLE(gl.TRIANGLES, start, 6, count)
  }

На самом деле некоторые Значки нарисованы справа. Я вижу 2 разных значка, но есть еще один тип, похожий на этот:

|\
| \
|  \
|  /
| /
|/

у моих значков только два треугольника, как показано ниже, я не устанавливаю никакую форму, как указано выше,

______
|\   |
| \  |
|  \ |
|   \|
------

1 Ответ

1 голос
/ 09 января 2020

Вот пример рисования нескольких спрайтов из листа спрайтов с использованием инстансированного рисования.

Обратите внимание, если бы это был я, я бы использовал матрицу для каждого экземпляра , как в этом примере , но я подумал, что код был бы проще с использованием здесь смещения и масштаба.

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

const vs = `
attribute vec2 position;
attribute vec2 uv;
attribute vec2 offset;    // instanced
attribute vec2 scale;     // instanced
attribute vec2 uvOffset;  // instanced
attribute vec2 uvScale;   // instanced

uniform mat4 matrix;

varying vec2 v_uv;

void main() {
  gl_Position = matrix * vec4(position * scale + offset, 0, 1);
  
  v_uv = uv * uvScale + uvOffset;
}
`;

const fs = `
precision highp float;
varying vec2 v_uv;
uniform sampler2D spriteAtlas;

void main() {
  gl_FragColor = texture2D(spriteAtlas, v_uv);
}
`;

const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const uvLoc = gl.getAttribLocation(program, 'uv');
const offsetLoc = gl.getAttribLocation(program, 'offset');
const scaleLoc = gl.getAttribLocation(program, 'scale');
const uvOffsetLoc = gl.getAttribLocation(program, 'uvOffset');
const uvScaleLoc = gl.getAttribLocation(program, 'uvScale');
const matrixLoc = gl.getUniformLocation(program, 'matrix');

// setup quad positions and uv
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  0, 0,
  1, 0,
  0, 1,
  0, 1,
  1, 0,
  1, 1,
]), gl.STATIC_DRAW);

const uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  0, 0,
  1, 0,
  0, 1,
  0, 1,
  1, 0,
  1, 1,
]), gl.STATIC_DRAW);


// create typed array for instanced data
const maxSprites = 1000;
const offsets = new Float32Array(maxSprites * 2);
const scales = new Float32Array(maxSprites * 2);
const uvOffsets = new Float32Array(maxSprites * 2);
const uvScales = new Float32Array(maxSprites * 2);

// create buffers fo instanced data
const offsetBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer);
gl.bufferData(gl.ARRAY_BUFFER, offsets.byteLength, gl.DYNAMIC_DRAW);
const scaleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, scaleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, scales.byteLength, gl.DYNAMIC_DRAW);
const uvOffsetBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvOffsetBuffer);
gl.bufferData(gl.ARRAY_BUFFER, uvOffsets.byteLength, gl.DYNAMIC_DRAW);
const uvScaleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvScaleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, uvScales.byteLength, gl.DYNAMIC_DRAW);

let spriteNdx = 0;
function addSprite(
    spriteAtlasWidth, spriteAtlasHeight,
    srcX, srcY, srcWidth, srcHeight,
    dstX, dstY, dstWidth, dstHeight) {
  const off0 = spriteNdx * 2;
  const off1 = off0 + 1;
  offsets[off0] = dstX;
  offsets[off1] = dstY;
  scales[off0] = dstWidth;
  scales[off1] = dstHeight;
  uvOffsets[off0] = srcX / spriteAtlasWidth;
  uvOffsets[off1] = srcY / spriteAtlasHeight;
  uvScales[off0] = srcWidth / spriteAtlasWidth;
  uvScales[off1] = srcHeight / spriteAtlasHeight;
  ++spriteNdx;
}

const sprites = [ 
   {msg: 'A', x: 0,  y:  0, w: 64, h: 32, bg: 'red',   fg: 'yellow'},
   {msg: 'B', x: 64, y:  0, w: 64, h: 32, bg: 'blue',  fg: 'white'},
   {msg: 'C', x: 0,  y: 32, w: 40, h: 32, bg: 'green', fg: 'pink'},
   {msg: 'D', x: 40, y: 32, w: 48, h: 32, bg: 'purple', fg: 'cyan'},
   {msg: 'F', x: 88, y: 32, w: 40, h: 32, bg: 'black', fg: 'red'},
];

// make 5 sprites in an atlas
const spriteAtlasWidth = 128;
const spriteAtlasHeight = 64;
const ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = spriteAtlasWidth;
ctx.canvas.height = spriteAtlasHeight;
for (const spr of sprites) {
  ctx.fillStyle = spr.bg;
  ctx.fillRect(spr.x, spr.y, spr.w, spr.h);
  ctx.strokeStyle = spr.fg;
  ctx.strokeRect(spr.x + .5, spr.y + .5, spr.w - 1, spr.h - 1);
  ctx.fillStyle = spr.fg;
  ctx.font = 'bold 26px sans-serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText(spr.msg, spr.x + spr.w / 2, spr.y + spr.h / 2);
}
// show the atlas
document.body.appendChild(ctx.canvas);

// copy the atlas to a texture
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ctx.canvas);
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);


function render(time) {
   time *= 0.001;  // convert to seconds

   spriteNdx = 0;
   const numSprites = 10;
   for (let i = 0; i < numSprites; ++i) {
     const sp = sprites[i % sprites.length];
     addSprite(
       spriteAtlasWidth, spriteAtlasHeight,
       sp.x, sp.y, sp.w, sp.h,
       Math.sin(time + i * 15) * gl.canvas.width / 2 + gl.canvas.width / 2,
       Math.cos(time + i * 17) * gl.canvas.height / 2 + gl.canvas.height / 2,
       sp.w, sp.h,
     );
  }
  
  // copy the latest sprite instance data
  // to their respective buffers and setup
  // the attributes.

  // NOTE: for the attributes it would be better
  // to use a vertex array
  
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
  gl.enableVertexAttribArray(uvLoc);
  gl.vertexAttribPointer(uvLoc, 2, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, offsets);
  gl.enableVertexAttribArray(offsetLoc);
  gl.vertexAttribPointer(offsetLoc, 2, gl.FLOAT, false, 0, 0);
  ext.vertexAttribDivisorANGLE(offsetLoc, 1);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, scaleBuffer);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, scales);
  gl.enableVertexAttribArray(scaleLoc);
  gl.vertexAttribPointer(scaleLoc, 2, gl.FLOAT, false, 0, 0);
  ext.vertexAttribDivisorANGLE(scaleLoc, 1);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, uvOffsetBuffer);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, uvOffsets);
  gl.enableVertexAttribArray(uvOffsetLoc);
  gl.vertexAttribPointer(uvOffsetLoc, 2, gl.FLOAT, false, 0, 0);
  ext.vertexAttribDivisorANGLE(uvOffsetLoc, 1);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, uvScaleBuffer);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, uvScales);
  gl.enableVertexAttribArray(uvScaleLoc);
  gl.vertexAttribPointer(uvScaleLoc, 2, gl.FLOAT, false, 0, 0);
  ext.vertexAttribDivisorANGLE(uvScaleLoc, 1);
  
  gl.useProgram(program);
  
  // pass in a projection matrix that 
  // converts to pixel space so the top
  // left corner is 0,0 and the bottom right corner
  // is canvas.width, canvas.height
  // 
  // if you had a 3d math library this would be something like
  // m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
  gl.uniformMatrix4fv(matrixLoc, false, [
     2 / gl.canvas.width, 0, 0, 0,
     0, -2 / gl.canvas.height, 0, 0,
     0, 0, 1, 0,
     -1, 1, 0, 1,
  ]);
  
  // note as there as only 1 texture and 
  // uniforms default to 0 we don't need to
  // bind the texture to setup a uniform
  // as the defaults happen to work.
  
  ext.drawArraysInstancedANGLE(
      gl.TRIANGLES,
      0,
      6,          // verts per instance
      spriteNdx,  // num instances
  );
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; margin: 5px; }
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

примечание Я не пропускаю экземпляры, но если вы хотите пропустить экземпляры, вам нужно установить смещение, переданное в gl.vertexAttribPointer для каждого экземпляра атрибут. Например, в приведенном выше коде, если вы хотите нарисовать экземпляры с 7 по 29, это будет

 const numInstancesToSkip = 7;
 const numInstancesToDraw = 29 - 7 + 1;
 const size = 2;  // vec2
 const sizeOfFloat = 4;
 const offset = numInstancesToSkip * sizeOfFloat * size;

 gl.bindBuffer(offsetBuffer);
 gl.vertexAttribPointer(offsetLoc, size, gl.FLOAT, false, 0, offset);
 gl.bindBuffer(scaleBuffer);
 gl.vertexAttribPointer(scaleLoc, size, gl.FLOAT, false, 0, offset);
 gl.bindBuffer(uvOffsetBuffer);
 gl.vertexAttribPointer(uvOffsetLoc, size, gl.FLOAT, false, 0, offset);
 gl.bindBuffer(uvScaleBuffer);
 gl.vertexAttribPointer(uvScaleLoc, size, gl.FLOAT, false, 0, offset);

, а для рисования будет

 ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, mumInstancesToDraw);

, обратите внимание, что offset выше это то же самое для каждого атрибута, потому что все атрибуты имеют одинаковый размер (2) и один и тот же тип (gl.FLOAT), и все они находятся в отдельных буферах, поэтому их базовые смещения равны 0. Если они были разных размеров или разных типов или смешаны в один и тот же буфер, они все требуют разной математики.

...