WebGL;实例渲染-设置除数 [英] WebGL; Instanced rendering - setting up divisors

查看:102
本文介绍了WebGL;实例渲染-设置除数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to draw a lot of cubes in webgl using instanced rendering (ANGLE_instanced_arrays).

但是,我似乎无法全神贯注于如何设置除数。我有以下缓冲区;

However I can't seem to wrap my head around how to setup the divisors. I have the following buffers;

36个顶点(由2个三角形组成的6个面,每个三角形使用3个顶点)。
每个立方体6种颜色(每个面1种)。
每个多维数据集1个转换。

36 vertices (6 faces made from 2 triangles using 3 vertices each). 6 colors per cube (1 for each face). 1 translate per cube.

重用每个多维数据集的顶点;我将其除数设置为0。
对于颜色,我将其除数设置为2(即,将相同的颜色用于两个三角形-一个面)。
对于平移,我将除数设置为12(即相同平移为6个面*每个面2个三角形)。

To reuse the vertices for each cube; I've set it's divisor to 0. For color I've set the divisor to 2 (i.e. use same color for two triangles - a face)). For translate I've set the divisor to 12 (i.e. same translate for 6 faces * 2 triangles per face).

对于渲染,我称

ext_angle.drawArraysInstancedANGLE(gl.TRIANGLES,0,36,num_cubes);

但这似乎无法渲染我的立方体。

This however does not seem to render my cubes.

使用翻译除数1可以,但是颜色远不及,立方体是单一纯色。

Using translate divisor 1 does but the colors are way off then, with cubes being a single solid color.

我认为这是因为我的实例现在是完整的多维数据集,但是如果我限制 count (即每个实例的顶点),我似乎并不会一路走来通过顶点缓冲区,实际上我只是每个多维数据集渲染一个三角形。

I'm thinking it's because my instances are now the full cube, but if I limit the count (i.e. vertices per instance), I do not seem to get all the way through the vertices buffer, effectively I'm just rendering one triangle per cube then.

我将如何渲染很多这样的多维数据集;

How would I go about rendering a lot of cubes like this; with varying colored faces?

推荐答案

实例化如下:

最终,您将调用

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

因此,假设您正在绘制多维数据集的实例。一个立方体具有36个顶点(每面6个* 6面)。因此

So let's say you're drawing instances of a cube. One cube has 36 vertices (6 per face * 6 faces). So

numVertices = 36

并且假设您要绘制100个立方体,所以

And lets say you want to draw 100 cubes so

numInstances = 100

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

Let's say you have a vertex shader like this

假设您有具有以下着色器

Let's say you have the following shader

attribute vec4 position;

uniform mat4 matrix;

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

如果您什么也没做,只是打电话给

If you did nothing else and just called

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

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

它将在相同的确切位置绘制相同的立方体100次

It would just draw the same cube in the same exact place 100 times

接下来,您想给每个立方体一个不同的翻译,以便将着色器更新为此

Next up you want to give each cube a different translation so you update your shader to this

attribute vec4 position;
attribute vec3 translation;

uniform mat4 matrix;

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

您现在创建一个缓冲区并为每个多维数据集放置一个转换,然后设置属性像普通

You now make a buffer and put one translation per cube then you setup the attribute like normal

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

但是您还设置了除数

ext.vertexAttribDivisorANGLE(translationLocation, 1);

1表示 每个实例仅前进到转换缓冲区中的下一个值'

现在,您希望每个多维数据集的每个面具有不同的颜色,并且只希望数据中每个面具有一种颜色(您不想重复颜色) )。 没有任何设置,因为您的 numVertices = 36 ,因此您只能选择每个顶点(除数= 0)或每个整数倍前进总共36个顶点(即numVertices)。

Now you want have a different color per face per cube and you only want one color per face in the data (you don't want to repeat colors). There is no setting that would to that Since your numVertices = 36 you can only choose to advance every vertex (divisor = 0) or once every multiple of 36 vertices (ie, numVertices).

所以你说,如果实例面对而不是立方体怎么办?现在,您遇到了相反的问题。每张脸只放一种颜色。 numVertices = 6 numInstances = 600 (100个立方体*每个立方体6个面)。您可以将颜色的除数设置为1,以使每张脸颜色一次。您可以将翻译除数设置为6,以每6个面(每6个实例)仅进行一次翻译。但是现在您不再拥有一个多维数据集,您只能拥有一个面孔。换句话说,您要绘制600张面朝相同方向的面,每6张面都转换到相同的位置。

So you say, what if instance faces instead of cubes? Well now you've got the opposite problem. Put one color per face. numVertices = 6, numInstances = 600 (100 cubes * 6 faces per cube). You set color's divisor to 1 to advance the color once per face. You can set translation divisor to 6 to advance the translation only once every 6 faces (every 6 instances). But now you no longer have a cube you only have a single face. In other words you're going to draw 600 faces all facing the same way, every 6 of them translated to the same spot.

要得到一个立方体,您需要必须添加一些东西来使面部实例沿6个方向定向。

To get a cube back you'd have to add something to orient the face instances in 6 direction.

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

Ok, you fill a buffer with 6 orientations. That won't work. You can't set divisor to anything that will use those 6 orientations advance only once every face but then resetting after 6 faces for the next cube. There's only 1 divisor setting. Setting it to 6 to repeat per face or 36 to repeat per cube but you want advance per face and reset back per cube. No such option exists.

您可以做的是用6个绘制调用对其绘制,每个面朝一个方向绘制。换句话说,您将绘制所有左侧的面,然后绘制所有右侧的面,所有顶部的面,等等。

What you can do is draw it with 6 draw calls, one per face direction. In other words you're going to draw all the left faces, then all the right faces, the all the top faces, etc...

仅1张面,每个立方体1个平移,每个立方体每面1种颜色。我们将平移和颜色的除数设置为1。

To do that we make just 1 face, 1 translation per cube, 1 color per face per cube. We set the divisor on the translation and the color to 1.

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

Then we draw 6 times, one for each face direction. The difference between each draw is we pass in an orientation for the face and we change the attribute offset for the color attribute and set it's stride to 6 * 4 floats (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>

这篇关于WebGL;实例渲染-设置除数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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