如何通过使用统一的缓冲区对象和实例渲染来绘制多个对象? [英] How to draw multiple objects by using uniform buffer objects and instanced rendering?

查看:82
本文介绍了如何通过使用统一的缓冲区对象和实例渲染来绘制多个对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过使用统一缓冲区对象和实例化渲染来绘制带有动画的多个对象.

I want to draw multiple objects with animations by using Uniform buffer objets and Instanced rendering..

我已经使用for循环实现了此功能,但我想一次渲染它们.

I've implemented this with a for loop but I want to render them at one time.

这是我的代码,当单击鼠标时,该代码使用for循环呈现多个对象.

Here is my code which using for loop to render multiple objects when mouse is clicked..

我正在使用四个外部库,分别是webgl-utils.js,webgl-debug.js,cuon-utils.js,cuon-matrix.js.可以在此处找到.

I am using four external libraries which are webgl-utils.js, webgl-debug.js, cuon-utils.js, cuon-matrix.js. These can be found here.

"use strict";
const loc_aPosition = 1;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
uniform mat4 uRotMatrix;
uniform mat4 uScaleMatrix;
uniform vec2 uOffSet;
void main() {
    gl_Position = aPosition * uScaleMatrix * uRotMatrix + vec4(uOffSet, 0, 0); 
}`;

const FSHADER_SOURCE =
`#version 300 es
precision mediump float;
out vec4 fColor;
uniform vec4 uColor;
void main() {
    fColor = uColor;
}`;


function main() {
  // Retrieve <canvas> element
  let canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  let gl = canvas.getContext("webgl2");
  if (!gl) 
  {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) 
  {
    console.log('Failed to intialize shaders.');
    return;
  }
  
  const loc_uOffSet = gl.getUniformLocation(gl.program, 'uOffSet');
  const loc_uColor = gl.getUniformLocation(gl.program, 'uColor');
  const loc_uRotMatrix = gl.getUniformLocation(gl.program, 'uRotMatrix');
  const loc_uScaleMatrix = gl.getUniformLocation(gl.program, 'uScaleMatrix');

  if(!loc_uOffSet)
  {
      console.log("Failed to load uOffSet uniform variable.");
      return;
  }

  if(!loc_uColor)
  {
      console.log("Failed to load uColor uniform variable.");
      return;
  }

  if(!loc_uRotMatrix)
  {
      console.log("Failed to load uModelMatrix uniform variable.");
      return;
  }

  if(!loc_uScaleMatrix)
  {
      console.log("Falied to load uScaleMatrix uniform variable.");
      return;
  }




  let n = initVertexBuffers(gl);

  if(n < 0)
  {
    console.log('Failed to set the positions of the vertices');
    return;
  }


  // Register function (event handler) to be called on a mouse press

  canvas.onmousedown = function(ev){ click(ev, gl, canvas) };

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);


  let tick = function() 
  {
    animate();  // Update the rotation angle
    draw(gl, loc_uRotMatrix, loc_uOffSet, loc_uColor, loc_uScaleMatrix);   // Draw
    requestAnimationFrame(tick, canvas); // Request that the browser calls tick
  };
  tick();
}


//These are the arrays for the attributes of the stars
let g_vertices = []; 
let g_angles = [];
let g_colors = [];
let g_ages = [];
let g_scale = [];

const ANGLE_STEP = -60;


let g_last = Date.now();


function click(ev, gl, canvas) 
{
  let x = ev.clientX; // x coordinate of a mouse pointer
  let y = ev.clientY; // y coordinate of a mouse pointer
  let rect = ev.target.getBoundingClientRect();

  x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
  y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);


  // Store the coordinates and color
  g_vertices.push([x,y]);
  g_angles.push(0);
  g_ages.push(Date.now());
  g_scale.push(1);
  
  let randomPos = Math.floor(Math.random() * Math.floor(3));
  let rgba = [4];
  let randomColor = Math.random();

  for(let i = 0; i<4; i++)
  {
      rgba[i] = randomColor;
  }
  
  rgba[3] = 1.0;
  rgba[randomPos] = Math.random();
    
  g_colors.push(rgba);
}


//Make the BO for making stars
function initVertexBuffers(gl)
{

  let vertices = new Float32Array([    
    0, -0.2,
    -0.3, -0.4,
    0.0, 0.5,
    0.3, -0.4,
    0.0, 0.5,
    0.0, 0.3,
    -0.4, 0.3,
    0.4, 0.3,
    0.0, 0.3,   
  ]);
  let n = 9;

  //Create a buffer Object
  let posBuffer = gl.createBuffer();
  
  if(!posBuffer)
  {
    console.log('Failed to create the buffer object');
    return;
  }

  //Bind the buffer object to target
  gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
  //Write date into the buffer object
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  //Connect the assignment to a_Position variable
  gl.vertexAttribPointer(loc_aPosition, 2, gl.FLOAT, false, 0, 0);

  //Enable the assignment to a_Position variable
  gl.enableVertexAttribArray(loc_aPosition);

  return n;
}

function animate() 
{
    // Calculate the elapsed time
    let now = Date.now();
    let elapsed = now - g_last;
    g_last = now;
    // Update the current rotation angle (adjusted by the elapsed time)
    for(let i = 0; i<g_angles.length; i++)
    {
        g_angles[i] = g_angles[i] + (ANGLE_STEP * elapsed) / 1000.0;
        g_angles[i] %= 360;
        g_scale[i] *= 0.99;
    }
  }


  function draw(gl, loc_uModelMatrix, loc_uOffSet, loc_uColor, loc_uScaleMatrix) 
  {
    // Clear <canvas>
    gl.clear(gl.COLOR_BUFFER_BIT);
    let rotMatrix = new Matrix4();
    let scaleMatrix = new Matrix4();
    
    // Draw the stars
    let len = g_vertices.length;
    for(let i = 0; i < len; i++) 
    {
        if((Date.now() - g_ages[i]) / 1000 > 3.5 ) // dissapear stars about 3.5 seconds after
          continue;


        let rgba = g_colors[i]; 
        rotMatrix.setRotate(g_angles[i], 0, 0, 1);
        scaleMatrix.setScale(g_scale[i], g_scale[i], 1);


        //Set the uniform variables
        gl.uniformMatrix4fv(loc_uModelMatrix, false, rotMatrix.elements);
        gl.uniformMatrix4fv(loc_uScaleMatrix, false, scaleMatrix.elements);
        gl.uniform2f(loc_uOffSet, g_vertices[i][0], g_vertices[i][1]);
        gl.uniform4f(loc_uColor, rgba[0], rgba[1], rgba[2], rgba[3]);


        gl.drawArrays(gl.TRIANGLE_FAN, 0, 9);


        //Reset matrices for the next star
        rotMatrix.setIdentity();
        scaleMatrix.setIdentity();
    }
  }

这是示例图片的外观和工作方式:

and here is the sample image how it looks and works:

我这样修改了上面的代码

I modified above code like this

"use strict";
const loc_aPosition = 1;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
uniform matrices
{
    mat4 uScaleMat;
    mat4 uRotMat;
    vec2 uOffSetXY;
};
void main() {
    gl_Position = aPosition * uScaleMat * uRotMat + vec4(uOffSetXY, 0, 0); 
}`;

// Fragment shader program
const FSHADER_SOURCE =
`#version 300 es
uniform colors
{
  vec4 uColorVec;
}
precision mediump float;
out vec4 fColor;
void main() {
    fColor = uColorVec;
}`;


function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');
  
  // Get the rendering context for WebGL
  var gl = canvas.getContext("webgl2");
  if (!gl) 
  {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) 
  {
    console.log('Failed to intialize shaders.');
    return;
  }
  
  const loc_uOffSet = gl.getUniformLocation(gl.program, 'uOffSet');
  const loc_uColor = gl.getUniformLocation(gl.program, 'uColor');
  const loc_uModelMatrix = gl.getUniformLocation(gl.program, 'uModelMatrix');
  const loc_uScaleMatrix = gl.getUniformLocation(gl.program, 'uScaleMatrix');

  if(!loc_uOffSet)
  {
      console.log("Failed to load uOffSet uniform variable.");
      return;
  }

  if(!loc_uColor)
  {
      console.log("Failed to load uColor uniform variable.");
      return;
  }

  if(!loc_uModelMatrix)
  {
      console.log("Failed to load uModelMatrix uniform variable.");
      return;
  }

  if(!loc_uScaleMatrix)
  {
      console.log("Falied to load uScaleMatrix uniform variable.");
      return;
  }



  let matR = new Matrix4();
  let matS = new Matrix4();
  let offSetXY = [];
  let colors = [];
  let prog = gl.program
  let {vao, n} = initVertexBuffers(gl);
  let {vubo, cubo, matBuffer, colorBuffer} = initUBO(gl, prog, matR, matS, offSetXY, colors);

  if(n < 0)
  {
    console.log('Failed to set the positions of the vertices');
    return;
  }


  // Register function (event handler) to be called on a mouse press
  
  canvas.onmousedown = function(ev){ click(ev, gl, canvas) };

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);


  let tick = function() 
  {
    animate();
    render(gl, prog, vao, n, g_stars, matBuffer, colorBuffer, matR, matS, offSetXY, colors, vubo, cubo)
    requestAnimationFrame(tick, canvas); // Request that the browser calls tick
  };
  tick();
}


let g_vertices = [];  // The array for the position of Triangle with mouse click
let g_angles = [];
let g_colors = [];
let g_ages = [];
let g_scale = [];
const ANGLE_STEP = -60;
const MAX_TRIANGLES = 30;
let g_last = Date.now();
let g_stars = 0;

function click(ev, gl, canvas) 
{
  let x = ev.clientX; // x coordinate of a mouse pointer
  let y = ev.clientY; // y coordinate of a mouse pointer
  let rect = ev.target.getBoundingClientRect();

  x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
  y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);


  // Store the coordinates and color
  g_vertices.push([x,y]);
  g_angles.push(0);
  g_ages.push(Date.now());
  g_scale.push(1);
  g_stars++;
  
  let randomPos = Math.floor(Math.random() * Math.floor(3));
  let rgba = [4];
  let randomColor = Math.random();

  for(let i = 0; i<4; i++)
  {
      rgba[i] = randomColor;
  }
  
  rgba[3] = 1.0;
  rgba[randomPos] = Math.random();
    
  g_colors.push(rgba);
}


function initVertexBuffers(gl)
{

  let vertices = new Float32Array([    
    0, -0.2,
    -0.3, -0.4,
    0.0, 0.5,
    0.3, -0.4,
    0.0, 0.5,
    0.0, 0.3,
    -0.4, 0.3,
    0.4, 0.3,
    0.0, 0.3,   
  ]);
  let n = 9;

  //Create a buffer Object
  let posBuffer = gl.createBuffer();
  let vao = gl.createVertexArray();

  
  if(!posBuffer)
  {
    console.log('Failed to create the buffer object');
    return;
  }

  gl.bindVertexArray(vao);

  //Bind the buffer object to target
  gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
  //Write date into the buffer object
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  //Connect the assignment to a_Position variable
  gl.vertexAttribPointer(loc_aPosition, 2, gl.FLOAT, false, 0, 0);

  //Enable the assignment to a_Position variable
  gl.enableVertexAttribArray(loc_aPosition);

  return {vao, n};
}

function animate() 
{
    // Calculate the elapsed time
    let now = Date.now();
    let elapsed = now - g_last;
    g_last = now;
    // Update the current rotation angle (adjusted by the elapsed time)
    for(let i = 0; i<g_angles.length; i++)
    {
        g_angles[i] = g_angles[i] + (ANGLE_STEP * elapsed) / 1000.0;
        g_angles[i] %= 360;
        g_scale[i] *= 0.99;
    }
  }


  function draw(gl, loc_uModelMatrix, loc_uOffSet, loc_uColor, loc_uScaleMatrix) 
  {
    // Clear <canvas>
    gl.clear(gl.COLOR_BUFFER_BIT);
    let rotMatrix = new Matrix4();
    let scaleMatrix = new Matrix4();
    
    // Draw the rectangle
    let len = g_vertices.length;
    for(let i = 0; i < len; i++) 
    {
        if((Date.now() - g_ages[i]) / 1000 > 3.5 )
          continue;
        let rgba = g_colors[i];

        rotMatrix.setRotate(g_angles[i], 0, 0, 1);
        scaleMatrix.setScale(g_scale[i], g_scale[i], 1);

        gl.uniformMatrix4fv(loc_uModelMatrix, false, rotMatrix.elements);
        gl.uniformMatrix4fv(loc_uScaleMatrix, false, scaleMatrix.elements);
        gl.uniform2f(loc_uOffSet, g_vertices[i][0], g_vertices[i][1]);
        gl.uniform4f(loc_uColor, rgba[0], rgba[1], rgba[2], rgba[3]);

        gl.drawArrays(gl.TRIANGLE_FAN, 0, 9);

        
        rotMatrix.setIdentity();
        scaleMatrix.setIdentity();
    }
  }

  function initUBO(gl, prog, matR, matS, offSetXY, colors)
  {
    let vertexBinding_Matrices = 1;
    let fragBinding_Colors = 2;

    let vubo = gl.createBuffer();
    let cubo = gl.createBuffer();
    gl.bindBufferBase(gl.UNIFORM_BUFFER, vertexBinding_Matrices, vubo);
    gl.bindBufferBase(gl.UNIFORM_BUFFER, fragBinding_Colors, cubo);

    let idx_uniform_block1 = gl.getUniformBlockIndex(prog, 'matrices');
    let idx_uniform_block2 = gl.getUniformBlockIndex(prog, 'colors');

    gl.uniformBlockBinding(prog, idx_uniform_block1, vertexBinding_Matrices);
    gl.uniformBlockBinding(prog, idx_uniform_block2, fragBinding_Colors);
    
    let FSIZE = 4;

    let matBuffer = new ArrayBuffer(FSIZE * 16 * 2  + FSIZE * 2);

    matR.elements = new Float32Array(matBuffer, 0, 16);
    matS.elements = new Float32Array(matBuffer, FSIZE * 16, 16);
    offSetXY = new Float32Array(matBuffer, FSIZE * 16 * 2, 2);

    gl.bindBuffer(gl.UNIFORM_BUFFER, vubo);
    gl.bufferData(gl.UNIFORM_BUFFER, FSIZE * 16 * 2  + FSIZE * 2, gl.DYNAMIC_DRAW);
    gl.bindBuffer(gl.UNIFORM_BUFFER, null);


    let colorBuffer = new ArrayBuffer(FSIZE * 4);

    colors = new Float32Array(colorBuffer, 0, 4);

    gl.bindBuffer(gl.UNIFORM_BUFFER, cubo);
    gl.bufferData(gl.UNIFORM_BUFFER, FSIZE * 4, gl.DYNAMIC_DRAW);
    gl.bindBuffer(gl.UNIFORM_BUFFER, null);


    return {vubo, cubo, matBuffer, colorBuffer};
  }


  function render(gl, prog, vao, n, g_stars, matBuffer, colorBuffer, matR, matS, offSetXY, colors, vubo, cubo)
  {

  }

仍然有很多事情可以解决这个问题...我只是想知道我是否走在正确的轨道上...

still there are lots of things to fix this... I just wonder whether I am going on a right track or not...

是否有使用ubos和实例渲染的示例?

is there any examples using ubos and instanced rendering?

p.s由于我不是英语母语人士,所以这个问题和代码可能都存在错别字...

p.s Since I am not a native English speaker, so there may exist typos both this question and the code...

推荐答案

统一缓冲区对象只是设置制服的另一种方法.他们对实例化没有帮助.与使用gl.uniformXXX相比,它们只是潜在"设置制服速度更快的一种方法.例如,如果您的着色器将ambientdiffusespecularshininess作为输入,则可以制作一个包含这4种制服的制服块.然后,您可以为每种材质创建一个统一的缓冲对象,所有这四种设置,都可以使用一个WebGL功能设置不同的材质

Uniform Buffer Objects are just a different way to set uniforms. They don't help with instancing. They are just a way to "potentially" set uniforms faster than using gl.uniformXXX. For example if you have a shader that takes ambient, diffuse, specular, shininess as inputs you could make a uniform block that contains those 4 uniforms. You could then create one uniform buffer object for each material, all 4 of those settings, you can set different material with one WebGL function

gl.bindBufferRange(gl.UNIFORM_BUFFER, blockIndex, materialBuffer, [offset], [length]);

代替4个WebGL调用

Instead of 4 WebGL calls

gl.uniform3fv(ambientLocation, ...);
gl.uniform3fv(diffuseLocation, ...);
gl.uniform3fv(specularLocation, ...);
gl.uniform1f(shininessLocation, ...);

因此,统一的缓冲对象无助于实例化.他们所做的只是可能更快地设置uinforms的帮助.我之所以说可能",是因为尽管我怀疑要执行上述操作,但在初始化时预先创建一个统一的缓冲区对象,并在渲染时将其绑定到uniforn块肯定比调用gl.uniform 4次要快.另一方面,如果制服的值每帧改变一次,那么您需要通过使用新值调用gl.bufferSubData来更新缓冲区.这可能或可能不会比仅调用gl.uniform更快.我没有描述它. WebGL中的开销比OpenGL多.您穿的制服越多,速度可能越快.

So, uniform buffer objects don't help with instancing. All they do is help with setting uinforms possibly quicker. I say "possibly" because while I suspect doing the above, pre-creating at init time a uniform buffer object and at render time binding it to a uniforn block is certainly faster than calling gl.uniform 4 times. On the other hand if the values of the uniforms change every frame then you need to update the buffer by calling gl.bufferSubData with the new values. That may or maybe not be faster than just calling gl.uniform. I haven't profiled it. There is more overhead in WebGL than OpenGL. It's probably faster the more uniforms you have.

此答案显示了使用统一缓冲区的示例对象.

This answer shows an example of using uniform buffer objects.

无论如何,重要的一点是它们只是设置制服的一种更快的方法.他们没有启用新功能.

In any case the important point is they are just a faster way of setting uniforms. They don't enable new functionality.

实例化图纸确实提供了新功能.只需1次draw调用就可以绘制多件事情,并且您的某些属性每个实例仅更新一次,而不是像通常那样每个顶点更新一次.

Instanced Drawing does provide new functionality. The ability to draw multiple things with 1 draw call and have some of your attributes only updated once per instance instead of once per vertex like they normally do.

因此,为了使用实例化绘图,通常必须设置一些属性,该属性的数据每个实例都会更改一次.最明显的是mat4属性,可以为每个实例提供不同的矩阵.当然,如果您希望每个实例使用不同的颜色,则还需要提供颜色的属性.

So, in order to use instanced drawing you generally have to set up some attribute with data that will change once per instance. The most obvious is a mat4 attrtibute to provide a different matrix per instance. Of course if you want different colors for each instance you'll need to provide attributes for colors as well.

您希望每个实例仅更改一次属性,而不是像通常调用的那样每个顶点更改一次

Any attribute you want to only change once per instance instead of once per vertex like normal you call

  gl.vertexAttribDivisor(attributeLocation, 1);

以上语句中的1表示仅每1个实例将属性提升到下一个值.在该位置放2表示该属性仅在2个实例等之后才会前进.0=做正常的事情并向每个顶点前进.

The 1 in the statement above means only advance the attribute to the next value every 1 instance. Putting a 2 there would mean the attribute only advances after 2 instances etc. 0 = do the normal thing and advance every vertex.

示例:

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) alert('need WebGL2');

const positionLoc = 0;
const matrixLoc = 1; // note: mat4 attributes take 4 indices
const colorLoc = 5;


const vs = `#version 300 es
layout(location = ${positionLoc}) in vec4 position;

layout(location = ${matrixLoc}) in mat4 modelMatrix;  // we'll make this per instance
layout(location = ${colorLoc}) in vec4 color;  // we'll make this per instance

uniform mat4 viewProjection;

out vec4 v_color;

void main () {
  gl_Position = viewProjection * modelMatrix * position;
  v_color = color;
}
`;

const fs = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 outColor;
void main() {
  outColor = v_color;
}
`;

const program = twgl.createProgram(gl, [vs, fs]);
const viewProjectionLoc = gl.getUniformLocation(program, 'viewProjection');

const vao = gl.createVertexArray();
gl.bindVertexArray(vao);

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

const deg = v => v * Math.PI / 180;

// setup matrixes, one per instance
const matrixBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([    
    ...m4.rotateZ(m4.translation([-0.5, 0, 0]), deg(10)),
    ...m4.rotateZ(m4.translation([-0.25, 0, 0]), deg(20)),
    ...m4.rotateZ(m4.translation([0, 0, 0]), deg(30)),
    ...m4.rotateZ(m4.translation([0.25, 0, 0]), deg(40)),
    ...m4.rotateZ(m4.translation([0.5, 0, 0]), deg(50)),
  ]), gl.DYNAMIC_DRAW);
 
// set all 4 attributes for matrix
for (let i = 0; i < 4; ++i) {
  gl.enableVertexAttribArray(matrixLoc + i);
  // note the stride and offset
  gl.vertexAttribPointer(matrixLoc + i, 4, gl.FLOAT, false, 64, i * 16);
  // this line says this attribute only changes for each 1 instance
  gl.vertexAttribDivisor(matrixLoc + i, 1);
}
 
// setup colors, one per instance
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([    
    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
    1, 0, 1, 1,
    0, 1, 1, 1,
  ]), gl.DYNAMIC_DRAW);
 
// set all attribute for color
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
// this line says this attribute only changes for each 1 instance
gl.vertexAttribDivisor(colorLoc, 1);

gl.useProgram(program);
gl.uniformMatrix4fv(viewProjectionLoc, false, m4.identity());
gl.drawArraysInstanced(
  gl.TRIANGLES,
  0,  // offset
  3,  // num vertices per instance
  5,  // num instances
);

canvas { border: 1px solid black; }

<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

要设置它们的动画,您需要通过调用gl.bufferSubData

To animate them you'd need to update the buffer data for the matrixBuffer each frame by calling gl.bufferSubData

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) alert('need WebGL2');

const positionLoc = 0;
const matrixLoc = 1; // note: mat4 attributes take 4 indices
const colorLoc = 5;


const vs = `#version 300 es
layout(location = ${positionLoc}) in vec4 position;

layout(location = ${matrixLoc}) in mat4 modelMatrix;  // we'll make this per instance
layout(location = ${colorLoc}) in vec4 color;  // we'll make this per instance

uniform mat4 viewProjection;

out vec4 v_color;

void main () {
  gl_Position = viewProjection * modelMatrix * position;
  v_color = color;
}
`;

const fs = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 outColor;
void main() {
  outColor = v_color;
}
`;

const program = twgl.createProgram(gl, [vs, fs]);
const viewProjectionLoc = gl.getUniformLocation(program, 'viewProjection');

const vao = gl.createVertexArray();
gl.bindVertexArray(vao);

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

const deg = v => v * Math.PI / 180;

// setup matrixes, one per instance
const numInstances = 5;
const matrixBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
// just allocate the buffer
gl.bufferData(gl.ARRAY_BUFFER, numInstances * 16 * 4, gl.DYNAMIC_DRAW);
 
// set all 4 attributes for matrix
for (let i = 0; i < 4; ++i) {
  gl.enableVertexAttribArray(matrixLoc + i);
  // note the stride and offset
  gl.vertexAttribPointer(matrixLoc + i, 4, gl.FLOAT, false, 64, i * 16);
  // this line says this attribute only changes for each 1 instance
  gl.vertexAttribDivisor(matrixLoc + i, 1);
}
 
// setup colors, one per instance
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([    
    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
    1, 0, 1, 1,
    0, 1, 1, 1,
  ]), gl.DYNAMIC_DRAW);
 
// set all attribute for color
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
// this line says this attribute only changes for each 1 instance
gl.vertexAttribDivisor(colorLoc, 1);

// make a typed array with one view per matrix
const matrixData = new Float32Array(numInstances * 16);
const matrices = [];
for (let i = 0; i < numInstances; ++i) {
  matrices.push(new Float32Array(matrixData.buffer, i * 16 * 4, 16));
}

function render(time) {
  time *= 0.001; // seconds
  
  // update all the matrices
  matrices.forEach((mat, ndx) => {
    m4.translation([-0.5 + ndx * 0.25, 0, 0], mat);
    m4.rotateZ(mat, time * (0.1 + 0.1 * ndx), mat);
  });
  
  // upload the new matrix data
  gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, matrixData);

  gl.bindVertexArray(vao);
  gl.useProgram(program);
  gl.uniformMatrix4fv(viewProjectionLoc, false, m4.identity());
  gl.drawArraysInstanced(
    gl.TRIANGLES,
    0,  // offset
    3,  // num vertices per instance
    numInstances,  // num instances
  );
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

canvas { border: 1px solid black; }

<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

这篇关于如何通过使用统一的缓冲区对象和实例渲染来绘制多个对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆