javascript - 如何在绑定顶点数组对象和缓冲区数据以在渲染时动态绘制时编写通用 webgl 渲染循环?
问题描述
我想使用本文中提到的方法显示文本。同时我关心代码是通用的。
在文章中提到手动创建缓冲区信息,我称之为第一种方法:
// Maunally create a bufferInfo
var textBufferInfo = {
attribs: {
a_position: { buffer: gl.createBuffer(), numComponents: 2, },
a_texcoord: { buffer: gl.createBuffer(), numComponents: 2, },
},
numElements: 0,
};
var textVAO = twgl.createVAOFromBufferInfo(
gl, textProgramInfo, textBufferInfo);
并使用以下设置渲染:
// update the buffers
textBufferInfo.attribs.a_position.numComponents = 2;
gl.bindBuffer(gl.ARRAY_BUFFER, textBufferInfo.attribs.a_position.buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices.arrays.position, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, textBufferInfo.attribs.a_texcoord.buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices.arrays.texcoord, gl.DYNAMIC_DRAW);
反对第二种方法:
// Create data for 'F'
var fBufferInfo = twgl.primitives.create3DFBufferInfo(gl);
var fVAO = twgl.createVAOFromBufferInfo(
gl, fProgramInfo, fBufferInfo);
并设置渲染:
// setup the attributes and buffers for the F
gl.bindVertexArray(fVAO);
所以我认为这意味着,在初始化时,我可以像这样设置 VAO:
const makeVao = (bufferInfos) => {
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
bufferInfos.forEach(({
array,
size,
index
}) => {
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW);
gl.enableVertexAttribArray(index);
gl.vertexAttribPointer(index,
size,
gl.FLOAT,
false,
0,
0);
});
gl.bindVertexArray(null);
return vao;
};
随着bufferInfos
用法:
let bufferInfos = [{
array: [vertices],
size: 2,
index: gl.getAttribLocation(program, name)
}];
这将设置属性并为我提供一个可以在渲染时使用的 VAO,例如:
gl.bindVertexArray(vao);
然后我就完成了。
除了,我想要第一种方法,我可以在每个渲染上设置顶点属性。那么如何设置通用代码以便能够在渲染时设置着色器属性呢?
解决方案
由于您使用的是顶点数组对象,因此您只需要在初始化时设置属性。属性保留一个指向当前vertexAttribPointer
被调用时的缓冲区的指针。请参阅有关属性状态或此问题或此问题的这篇文章
换句话说,如果你这样做
// assume positionLoc = 0, normalLoc = 1, texcoordLoc = 2
gl.bindVertexArray(someVAO);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLoc, ...);
gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(normalLoc, ...);
gl.enableVertexAttribArray(texcoordLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.vertexAttribPointer(texcoordLoc, ...);
gl.enableVertexAttribArray(texcoordLoc);
然后someVAO
保持以下状态
// pseudo code
someVAO = {
attributes: [
{ enabled: true, buffer: positionBuffer, ... }, // loc = 0
{ enabled: true, buffer: normalBuffer, ... }, // loc = 1
{ enabled: true, buffer: texcoordBuffer, ... }, // loc = 2
{ enabled: false, ... }, // loc = 3
...
]
elementArrayBuffer: null,
}
因此,只要您想更新缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, bufferToUpdate);
gl.bindData(gl.ARRAY_BUFFER, newData, gl.???_DRAW);
或者
gl.bindBuffer(gl.ARRAY_BUFFER, bufferToUpdate);
gl.bindSubData(gl.ARRAY_BUFFER, offset, newData);
任何时候你想渲染你
gl.useProgram(someProgram);
gl.bindVertexArray(someVAO)
gl.uniform... // for each uniform
gl.drawXXX
唯一的复杂情况是,如果您尝试对 2 个或更多程序使用相同的顶点数组,则需要确保两个程序的属性位置匹配。您可以通过在顶点着色器 GLSL 中手动分配位置来做到这一点
#version 300 es
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texcoord;
或者在打电话之前gl.linkProgram
你可以打电话gl.bindAttribLocation
gl.bindAttribLocation(someProgram, 0, "position");
gl.bindAttribLocation(someProgram, 1, "normal");
gl.bindAttribLocation(someProgram, 2, "texcoord");
gl.linkProgram(someProgram);
我更喜欢第二种方法,因为它更DRY,但第一种方法更常见我只是猜测,因为 DRY 风格的编程也比非 DRY 少
如果你使用 twgl 编译你的程序,你可以传入位置让它bindAttribLocation
为你调用
const programOptions = {
attribLocations: {
'position': 0,
'normal': 1,
'texcoord': 2,
'color': 3,
},
};
const programInfo1 = twgl.createProgramInfo(gl, [vs1, fs1], programOptions);
const programInfo2 = twgl.createProgramInfo(gl, [vs2, fs2], programOptions);
至于您的代码,我在您的makeVAO
函数中看到的唯一问题是您没有在任何地方存储每个属性,因此当您想尝试更新缓冲区时buffer
没有简单的方法可以调用。gl.bindBuffer(gl.ARRAY_BUFFER, theBufferToUpdate)
否则,乍一看,您的makeVAO
功能看起来不错。
例如,您可以这样做
const makeVao = (bufferInfos) => {
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
bufferInfos.forEach((bufferInfo) => {
const {
array,
size,
index
} = bufferInfo;
const buffer = gl.createBuffer();
bufferInfo.buffer = buffer; // remember the buffer
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW);
gl.enableVertexAttribArray(index);
gl.vertexAttribPointer(index,
size,
gl.FLOAT,
false,
0,
0);
});
gl.bindVertexArray(null);
return vao;
};
现在你可以使用
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfos[0].buffer);
gl.bufferSubData(gl.ARRAY_BUFFER, offset, newData);
更新第一个缓冲区。
推荐阅读
- pypy - ../pypy3/bin/pip3 install 给出错误
- sql - SQL (where in) 不返回任何结果
- javascript - 如何在鼠标滚轮滚动时触发点击事件(5 部分)
- html - 如果它们位于不同的文件夹中,则 HTML 不会链接到 CSS
- c# - 具有自定义登录页面和数据库的 ASP.NET MVC 角色授权
- java - Hibernate 如何创建查询?如何避免达到 61 表的限制?
- jquery - 如果 span 没有文本,则隐藏 span 元素
- awk - AWK 需要读取一个文件并在另一个文件中搜索
- javascript - Javascript 最初跳过嵌套函数,然后返回它?
- c# - TextDecorations Underline 属性 — System.NullReferenceException