【问题标题】:Why is the WebGL instancing extension only drawing one triangle instead of two?为什么 WebGL 实例化扩展只绘制一个三角形而不是两个?
【发布时间】:2020-06-01 21:31:13
【问题描述】:

我正在尝试使用基于 this guide 的 WebGL 实例化。我试图创建一个简单的示例,在左侧绘制一个红色三角形,在右侧绘制一个蓝色三角形。当我运行它时,它在左侧绘制了一个蓝色三角形,我不明白为什么。

我尝试将参数更改为drawArraysInstancedANGLE,但这并没有帮助。我尝试更改绑定缓冲区和设置属性指针等的行顺序。我做错了什么?谢谢

const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
const ext = gl.getExtension("ANGLE_instanced_arrays");
const vert = gl.createShader(gl.VERTEX_SHADER);
const frag = gl.createShader(gl.FRAGMENT_SHADER);
const program = gl.createProgram();

gl.shaderSource(vert, `
  attribute vec4 a_position;
  attribute vec4 a_color;
  varying vec4 v_color;

  void main() {
    gl_Position = a_position;
    v_color = a_color;
  }
`);

gl.shaderSource(frag, `
  precision mediump float;
  varying vec4 v_color;

  void main() {
    gl_FragColor = v_color;
  }
`);

gl.compileShader(vert);
gl.compileShader(frag);
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);

const a_position = gl.getAttribLocation(program, "a_position");
const buffer1 = gl.createBuffer();
const positions = [-0.5, 0, -0.5, 0.2, -0.3, 0, 0.5, 0, 0.5, 0.2, 0.3, 0];
gl.bindBuffer(gl.ARRAY_BUFFER, buffer1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

const a_color = gl.getAttribLocation(program, "a_color");
const buffer2 = gl.createBuffer();
const colors = [1, 0, 0, 1, 0, 0, 1, 1];
gl.bindBuffer(gl.ARRAY_BUFFER, buffer2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

gl.viewport(0, 0, 300, 300);
gl.useProgram(program);

gl.bindBuffer(gl.ARRAY_BUFFER, buffer1);
gl.enableVertexAttribArray(a_position);
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, buffer2);
gl.enableVertexAttribArray(a_color);
gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(a_color, 1);

ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 3, 2);
<canvas id="canvas" width="300" height="300"></canvas>

【问题讨论】:

  • 请阅读教程直到最后。你错过了attribute mat4 matrix;。三角形的 2 个实例完全重叠,它们共享相同的位置。
  • 在这个例子中我没有使用矩阵。我明确设置了三角形角的位置。我不认为他们有相同的立场。一个出现在屏幕左侧,另一个出现在右侧。当我不使用实例化并在单个 drawArrays 调用中绘制两个三角形时,它可以正常工作(前提是我使颜色缓冲区数据足够大)。
  • 你误解了Instancing。再次阅读教程。您有一个三角形的 2 个实例。顶点 4 到 6 根本没有使用。
  • 啊,是的,对不起,你是对的。我误解了实例化。我认为它会读取第一个实例的第一个 3x2 值,然后读取第二个实例的第二个 3x2 值,但这不是它的工作原理。相反,它会重复前 3x2 值。为了实现我想要的,我想我需要为顶点数据索引到一个统一的数组中,或者将它放入一个纹理中,如下所示:stackoverflow.com/questions/55319323/… 感谢您的帮助。

标签: javascript webgl


【解决方案1】:

所以我看到你大多已经在 cmets 中得到了答案。

代码将第一个三角形绘制两次,一次是红色,一次是蓝色。

但是,...您提到了对统一数组进行索引。这将是不寻常的

通常您会将每个实例的数据放在缓冲区中。链接站点上的示例使用矩阵只是为了通用,因为矩阵可以让您进行任何数学运算(平移、旋转、缩放、3d 投影……),但作为示例,您可以传入单个 x 偏移量。

const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
const ext = gl.getExtension("ANGLE_instanced_arrays");
const vert = gl.createShader(gl.VERTEX_SHADER);
const frag = gl.createShader(gl.FRAGMENT_SHADER);
const program = gl.createProgram();

gl.shaderSource(vert, `
  attribute vec4 a_position;
  attribute float a_xoffset;
  attribute vec4 a_color;
  varying vec4 v_color;

  void main() {
    gl_Position = a_position;
    gl_Position.x += a_xoffset;
    v_color = a_color;
  }
`);

gl.shaderSource(frag, `
  precision mediump float;
  varying vec4 v_color;

  void main() {
    gl_FragColor = v_color;
  }
`);

gl.compileShader(vert);
gl.compileShader(frag);
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);

const a_position = gl.getAttribLocation(program, "a_position");
const buffer1 = gl.createBuffer();
const positions = [-0.5, 0, -0.5, 0.2, -0.3, 0];
gl.bindBuffer(gl.ARRAY_BUFFER, buffer1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

const a_color = gl.getAttribLocation(program, "a_color");
const buffer2 = gl.createBuffer();
const colors = [1, 0, 0, 1, 0, 0, 1, 1];
gl.bindBuffer(gl.ARRAY_BUFFER, buffer2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

const a_xoffset = gl.getAttribLocation(program, "a_xoffset");
const buffer3 = gl.createBuffer();
const xoffsets = [0, 1];
gl.bindBuffer(gl.ARRAY_BUFFER, buffer3);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(xoffsets), gl.STATIC_DRAW);

gl.viewport(0, 0, 300, 300);
gl.useProgram(program);

gl.bindBuffer(gl.ARRAY_BUFFER, buffer1);
gl.enableVertexAttribArray(a_position);
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, buffer2);
gl.enableVertexAttribArray(a_color);
gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(a_color, 1);

gl.bindBuffer(gl.ARRAY_BUFFER, buffer3);
gl.enableVertexAttribArray(a_xoffset);
gl.vertexAttribPointer(a_xoffset, 1, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(a_xoffset, 1);

ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 3, 2);
<canvas id="canvas" width="300" height="300"></canvas>

我只是指出这一点,因为您在缓冲区中有数百万个值,但制服受到的限制要多得多,因此索引制服进行实例化并不常见。

如果您实际上希望每个实例有不同的数据,那么是的,您必须做其他事情。

一个例子是here

【讨论】:

  • 谢谢!为了提供更多上下文,我正在尝试渲染几个包含光几何顶点的三角形风扇(基于ncase.me/sight-and-light)。每盏灯都有不同的属性,如颜色、亮度等。我最初的计划是使用实例化(每个风扇一个)并使用 1 的除数作为属性,但这不起作用。第一个扇子的形状正在重复。我认为,相反,我将尝试将几何数据写入浮点纹理并使用属性(instanceId 和 vertexId)对其进行索引。不过看起来很复杂!
  • 将数据放入纹理中并不复杂。答案底部的链接就是这样做的。
  • 谢谢,我设法让它工作了!我很高兴。在我的用例中,它的运行速度快了大约 10 倍。非常感谢您的教程和对 SO 问题的丰富回答。
猜你喜欢
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2014-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多