网络英语;实例化渲染——设置除数

WebGL; Instanced rendering - setting up divisors

我正在尝试使用实例化渲染在 webgl 中绘制很多立方体 (ANGLE_instanced_arrays)。

但是我似乎无法理解如何设置除数。我有以下缓冲区;

36 个顶点(6 个面由 2 个三角形组成,每个三角形使用 3 个顶点)。 每个立方体 6 种颜色(每个面 1 种)。 每个立方体 1 个翻译。

重用每个立方体的顶点;我已经将它的除数设置为 0。 对于颜色,我将除数设置为 2(即两个三角形使用相同的颜色 - 一张脸)。 对于平移,我将除数设置为 12(即 6 个面的相同平移 * 每个面 2 个三角形)。

为了渲染我打电话 ext_angle.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 36, num_cubes);

但这似乎并没有渲染我的立方体。

使用平移除数 1 可以,但颜色太差了,立方体是单一纯色。

我认为这是因为我的实例现在是完整的立方体,但是如果我限制 count(即每个实例的顶点),我似乎无法完全通过顶点缓冲区,实际上,我只是为每个立方体渲染一个三角形。

我将如何渲染大量这样的立方体;有不同颜色的面孔?

实例化是这样的:

你最终会打电话给

ext.drawArraysInstancedANGLE(mode, first, numVertices, numInstances);

假设您正在绘制立方体的实例。一个立方体有 36 个顶点(每个面 6 个顶点 * 6 个面)。所以

numVertices = 36

假设你想画 100 个立方体,所以

numInstances = 100

假设您有一个像这样的顶点着色器

假设您有以下着色器

attribute vec4 position;

uniform mat4 matrix;

void main() {
  gl_Position = matrix * position;
}

如果你什么都没做,只是打电话

var mode = gl.TRIANGLES;
var first = 0;
var numVertices = 36
var numInstances = 100

ext.drawArraysInstancedANGLE(mode, first, numVertices, numInstances);

它只会在同一个地方绘制同一个立方体 100 次

接下来你想给每个立方体一个不同的翻译,所以你更新你的着色器到这个

attribute vec4 position;
attribute vec3 translation;

uniform mat4 matrix;

void main() {
  gl_Position = matrix * (position + vec4(translation, 0));
}

您现在创建一个缓冲区并为每个多维数据集放置一个翻译,然后像正常一样设置属性

gl.vertexAttribPointer(translationLocation, 3, gl.FLOAT, false, 0, 0)

但是你还设置了一个除数

ext.vertexAttribDivisorANGLE(translationLocation, 1);

1 表示'每个实例只前进到翻译缓冲区中的下一个值一次'

现在您希望每个立方体的每个面都有不同的颜色,并且您只希望数据中的每个面有一种颜色(您不想重复颜色)。 没有这样的设置因为你的numVertices = 36你只能选择推进每个顶点(除数= 0)或每36个顶点的倍数一次(即numVertices) .

所以你说,如果实例面而不是立方体呢?那么现在你遇到了相反的问题。每张脸涂上一种颜色。 numVertices = 6numInstances = 600(100 个立方体 * 每个立方体 6 个面)。您将颜色的除数设置为 1 以将每个面的颜色提升一次。您可以将平移除数设置为 6,以便每 6 个面(每 6 个实例)仅推进一次平移。但是现在你不再有一个立方体,你只有一个面。换句话说,你要画 600 张面朝同一个方向的面孔,每 6 张面孔平移到同一个位置。

要恢复立方体,您必须添加一些东西以在 6 个方向上定位面部实例。

好的,你用 6 个方向填充缓冲区。那行不通的。您不能将除数设置为任何将使用这 6 个方向的任何东西,每个面只前进一次,然后在下一个立方体的 6 个面后重置。只有 1 个除数设置。将其设置为 6 以重复每个面或 36 以重复每个立方体,但您希望每个面前进 每个立方体重置。不存在这样的选项。

您可以使用 6 个绘制调用来绘制它,每个面方向一个。换句话说,你要画所有的左面,然后是所有的右面,所有的顶面,等等...

为此,我们只制作 1 个面,每个立方体 1 个平移,每个立方体每个面 1 种颜色。我们将翻译和颜色的除数设置为 1。

然后我们画6次,每个面一个方向。每次绘制之间的区别是我们传递了面部的方向,我们更改了颜色属性的属性偏移量并将其步幅设置为 6 * 4 浮点数 (6 * 4 * 4)。

var vs = `
attribute vec4 position;
attribute vec3 translation;
attribute vec4 color;

uniform mat4 viewProjectionMatrix;
uniform mat4 localMatrix;

varying vec4 v_color;

void main() {
  vec4 localPosition = localMatrix * position + vec4(translation, 0);
  gl_Position = viewProjectionMatrix * localPosition;
  v_color = color;
}
`;

var fs = `
precision mediump float;

varying vec4 v_color;

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

var m4 = twgl.m4;
var gl = document.querySelector("canvas").getContext("webgl");
var ext = gl.getExtension("ANGLE_instanced_arrays");
if (!ext) {
  alert("need ANGLE_instanced_arrays");
}
var program = twgl.createProgramFromSources(gl, [vs, fs]);

var positionLocation = gl.getAttribLocation(program, "position");
var translationLocation = gl.getAttribLocation(program, "translation");
var colorLocation = gl.getAttribLocation(program, "color");

var localMatrixLocation = gl.getUniformLocation(program, "localMatrix");
var viewProjectionMatrixLocation = gl.getUniformLocation(
    program, 
    "viewProjectionMatrix");

function r(min, max) {
  if (max === undefined) {
    max = min;
    min = 0;
  }
  return Math.random() * (max - min) + min;
}

function rp() {
  return r(-20, 20);
}

// make translations and colors, colors are separated by face
var numCubes = 1000;
var colors = [];
var translations = [];

for (var cube = 0; cube < numCubes; ++cube) {
  translations.push(rp(), rp(), rp());

  // pick a random color;
  var color = [r(1), r(1), r(1), 1];

  // now pick 4 similar colors for the faces of the cube
  // that way we can tell if the colors are correctly assigned
  // to each cube's faces.
  var channel = r(3) | 0;  // pick a channel 0 - 2 to randomly modify
  for (var face = 0; face < 6; ++face) {
    color[channel] = r(.7, 1);
    colors.push.apply(colors, color);
  }
}

var buffers = twgl.createBuffersFromArrays(gl, {
  position: [  // one face
    -1, -1, -1,
    -1,  1, -1,
     1, -1, -1,
     1, -1, -1,
    -1,  1, -1,
     1,  1, -1,
  ],
  color: colors, 
  translation: translations,
});

var faceMatrices = [
  m4.identity(),
  m4.rotationX(Math.PI /  2),
  m4.rotationX(Math.PI / -2),
  m4.rotationY(Math.PI /  2),
  m4.rotationY(Math.PI / -2),
  m4.rotationY(Math.PI),
];

function render(time) {
  time *= 0.001;

  twgl.resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
  gl.enable(gl.DEPTH_TEST);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
  gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

  gl.bindBuffer(gl.ARRAY_BUFFER, buffers.translation);
  gl.enableVertexAttribArray(translationLocation);
  gl.vertexAttribPointer(translationLocation, 3, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);
  gl.enableVertexAttribArray(colorLocation);
  
  ext.vertexAttribDivisorANGLE(positionLocation, 0);
  ext.vertexAttribDivisorANGLE(translationLocation, 1);
  ext.vertexAttribDivisorANGLE(colorLocation, 1);

  gl.useProgram(program);
  
  var fov = 60;
  var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  var projection = m4.perspective(fov * Math.PI / 180, aspect, 0.5, 100);
  
  var radius = 30;
  var eye = [
    Math.cos(time) * radius, 
    Math.sin(time * 0.3) * radius, 
    Math.sin(time) * radius,
  ];
  var target = [0, 0, 0];
  var up = [0, 1, 0];

  var camera = m4.lookAt(eye, target, up);
  var view = m4.inverse(camera);
  var viewProjection = m4.multiply(projection, view); 
  
  gl.uniformMatrix4fv(viewProjectionMatrixLocation, false, viewProjection);

  // 6 faces * 4 floats per color * 4 bytes per float
  var stride = 6 * 4 * 4;  
  var numVertices = 6; 
  faceMatrices.forEach(function(faceMatrix, ndx) {
    var offset = ndx * 4 * 4;  // 4 floats per color * 4 floats
    gl.vertexAttribPointer(
       colorLocation, 4, gl.FLOAT, false, stride, offset);
    gl.uniformMatrix4fv(localMatrixLocation, false, faceMatrix);
    ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, numVertices, numCubes);
  });
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>