如何通过使用统一的缓冲区对象和实例渲染来绘制多个对象? [英] How to draw multiple objects by using uniform buffer objects and instanced rendering?
问题描述
我想通过使用统一缓冲区对象和实例化渲染来绘制带有动画的多个对象.
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
相比,它们只是潜在"设置制服速度更快的一种方法.例如,如果您的着色器将ambient
,diffuse
,specular
,shininess
作为输入,则可以制作一个包含这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屋!