在WebGL中-实例化几何体时,是否可以为每个实例传递每个顶点的属性信息? [英] In WebGL - when instancing geometry, is it possible to pass per-vertex attribute information for each instance?

查看:62
本文介绍了在WebGL中-实例化几何体时,是否可以为每个实例传递每个顶点的属性信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正尝试通过原始WebGL重新创建以下three.js示例具有实例化的几何体;不过,经过测试,看起来我可能只需要像示例中那样绘制多个网格,但是我想我首先要在这里进行仔细检查.

I am trying to recreate through raw WebGL the following three.js example with instanced geometry; though, after testing it looks like I may just have to draw multiple meshes like in the sample but I thought I would ask here first to double check.

现在的问题本质上是-是否可以为要渲染的几何的每个实例传递每个顶点的数据?

The question now is essentially - is it possible to pass per-vertex data for each instance of the geometry that gets rendered?

希望这是有道理的,但如果没有,则-

Hopefully that makes sense but if it doesn't -

  • 我正在渲染的几何具有4个顶点.
  • 对于几何的每个实例-我需要将uv坐标分配给几何的每个实例的每个顶点
  • 似乎设置步幅或偏移似乎无效.

基本上,现在发生的事情是给定当前数据,类似于

Basically what is happening now is given the current data which looks something like

[s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4] // enough uvs for 2 instances, each s and t value makes up 1 uv coord for 1 vertex

而不是像这样的块中读取和分配数据

Instead of data being read and assigned in blocks like this

      // instance 1            // instance 2
[[s1,t1,s2,t2,s3,t3,s4,t4], [s1,t1,s2,t2,s3,t3,s4,t4]]

// Each s and t forms 1 uv coord for 1 vertex 

数据看起来像这样被读取

Data appears to be instead being read like this

  [s1,t1,s2,t2,s3,t3,s4,t4,s1,t1,s2,t2,s3,t3,s4,t4]
  // Each s and t pair is assigned to every vertex of each instance

是否有可能像第二个步骤那样使事情正常进行?如果不是这样,那很好,但是我认为应该问.

Is it at all possible to get things working like in the second block? If not that's fine but thought I ought to ask.

推荐答案

这取决于您的意思

是否可以为要渲染的几何的每个实例传递每个顶点的数据?

is it possible to pass per-vertex data for each instance of the geometry that gets rendered?

如果通过属性来表示,那么答案是否定的.您可以获取每个顶点的数据或每个实例的数据,但不能获取每个实例的每个顶点的数据.

If you mean via attributes then the answer is no. You can get data per vertex or data per instance but not data per vertex per instance.

但是您还会获得2个额外的输入,gl_VertexIDgl_InstanceID(在WebGL2中),或者您可以添加自己的输入,可以用于计算UV或用于查找纹理中的数据以有效地实现所需的目标结果.

But you also get 2 extra inputs, gl_VertexID and gl_InstanceID (in WebGL2) or you can add your own which you could use to either compute UVs or use to look up data in a texture to effectively achieve your desired result.

例如,假设您有20x10的多维数据集.你可以做类似的事情

For example let's say you have 20x10 cubes. You could do something like

attribute vec4 position;    // 36 cube positions
attribute vec2 uv;          // 36 cube UVs
attribute float instanceId; // 1 per instance

uniform float cubesAcross;  // set to 20
uniform float cubesDown;    // set to 10

varying v_uv;

void main() {
  float cubeX = mod(instanceId, cubesAcross);
  float cubeY = floor(instanceId / cubesAcross);

  v_vu = (vec2(cubeX, cubeY) + uv) / vec2(cubesAcross, cubesDown);

  gl_Position = ...whatever you were doing for position before...
}

示例:

"use strict";

function main() {
  const m4 = twgl.m4;
  const gl = document.querySelector("canvas").getContext("webgl");
  const ext = gl.getExtension('ANGLE_instanced_arrays');
  if (!ext) {
    return alert('need ANGLE_instanced_arrays');
  }
  twgl.addExtensionsToContext(gl);
  const vs = `
  attribute vec4 position;
  attribute vec2 texcoord;
  attribute mat4 matrix;
  attribute float instanceId;

  uniform float cubesAcross;  // set to 20
  uniform float cubesDown;    // set to 10

  varying vec2 v_texcoord;

  void main() {
    gl_Position = matrix * position;
    
    float cubeX = mod(instanceId, cubesAcross);
    float cubeY = floor(instanceId / cubesAcross);

    v_texcoord = (vec2(cubeX, cubeY) + texcoord) / vec2(cubesAcross, cubesDown);
  }
  `;

  const fs = `
  precision mediump float;

  varying vec2 v_texcoord;

  void main() {
    gl_FragColor = vec4(v_texcoord, 0, 1);
  }
  `;

  // compile shaders, link, look up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

  const cubesAcross = 20;
  const cubesDown = 10;
  const numCubes = cubesAcross * cubesDown;
  // matrix per instance
  const matrixData = new Float32Array(16 * numCubes);
  const matrices = [];
  const instanceIds = new Float32Array(numCubes);
  for (let i = 0; i < numCubes; ++i) {
    instanceIds[i] = i;
    // make a typedarray view for each matrix
    matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));
  }

  const arrays = {
    position: [
       1,  1, -1, 
       1,  1,  1, 
       1, -1,  1,
       1, -1, -1,
       
      -1,  1,  1,
      -1,  1, -1,
      -1, -1, -1,
      -1, -1,  1, 
      
      -1,  1,  1, 
       1,  1,  1, 
       1,  1, -1,
      -1,  1, -1,
      
      -1, -1, -1,
       1, -1, -1,
       1, -1,  1,
      -1, -1,  1,
      
       1,  1,  1,
      -1,  1,  1,
      -1, -1,  1,
       1, -1,  1,
       
      -1,  1, -1,
       1,  1, -1,
       1, -1, -1,
      -1, -1, -1,
    ],
    texcoord: [
      1, 0,
      0, 0,
      0, 1,
      1, 1,
      
      1, 0,
      0, 0,
      0, 1,
      1, 1,
      
      0, 1,
      1, 1,
      1, 0,
      0, 0,
      
      0, 1,
      1, 1,
      1, 0,
      0, 0,
      
      1, 1,
      0, 1,
      0, 0,
      1, 0,
      
      1, 0,
      0, 0,
      0, 1,
      1, 1,
    ],
    indices: [
       0,  1,  2,  0,  2,  3,
       4,  5,  6,  4,  6,  7,
       8,  9, 10,  8, 10, 11,
      12, 13, 14, 12, 14, 15,
      16, 17, 18, 16, 18, 19,
      20, 21, 22, 20, 22, 23,
    ],
    instanceId: { numComponents: 1, data: instanceIds, divisor: 1 },
    matrix: { numComponents: 16, data: matrices, divisor: 1 },
  };
  // create buffers, upload data (gl.bufferData)
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

  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.enable(gl.CULL_FACE);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    const fov = 30 * Math.PI / 180;
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.5;
    const zFar = 100;
    const projection = m4.perspective(fov, aspect, zNear, zFar);
    const eye = [1, 24, 76];
    const target = [18, 10, 0];
    const up = [0, 1, 0];

    const camera = m4.lookAt(eye, target, up);
    const view = m4.inverse(camera);
    const viewProjection = m4.multiply(projection, view);
    
    // update the instance for each matrix
    const spacing = 2.5 + Math.sin(time) * .5;
    let i = 0;
    for (let y = 0; y < cubesDown; ++y) {
      for (let x = 0; x < cubesAcross; ++x) {
        const matrix = matrices[i++];
        m4.translate(viewProjection, [
          x * spacing,
          y * spacing,
          0,
        ], matrix);
      }
    }

    gl.useProgram(programInfo.program);
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    twgl.setUniforms(programInfo, {
      cubesAcross,
      cubesDown,
    });
    
    // upload instance matrices to buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
    gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

    ext.drawElementsInstancedANGLE(
      gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0,  numCubes);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

body {
  margin: 0;
}

canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

如果您实际上想要每个实例的每个顶点数据唯一,则将其放入纹理中并同时传递一个vertexId.然后同时使用vertexId和instanceId来计算纹理坐标

If you actually want unique per vertex per instance data then put it in a texture and pass in a vertexId as well. Then use both vertexId and instanceId to compute a texture coordinate

  attribute float instanceId;
  attribute float vertexId;

  uniform sampler2D dataTexture;
  uniform vec2 dataTextureSize;

  varying vec2 v_texcoord;

  void main() {

    // each row is for an instance, each texel
    // per vertex. Of course if you want more data
    // per vertex then multiply vertexId by the number of
    // vec4s of data you need. If you need more instances
    // then compute a more complex offset based off instanceId
    vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
    vec4 data = texture2D(dataTexture, uv);

    v_texcoord = data.xy;

"use strict";

function main() {
  const m4 = twgl.m4;
  const gl = document.querySelector("canvas").getContext("webgl");
  const ext1 = gl.getExtension('OES_texture_float');
  if (!ext1) {
    return alert('need OES_texture_float');
  }
  const ext = gl.getExtension('ANGLE_instanced_arrays');
  if (!ext) {
    return alert('need ANGLE_instanced_arrays');
  }
  twgl.addExtensionsToContext(gl);
  const vs = `
  attribute vec4 position;
  attribute mat4 matrix;
  attribute float instanceId;
  attribute float vertexId;
  
  uniform sampler2D dataTexture;
  uniform vec2 dataTextureSize;

  varying vec2 v_texcoord;

  void main() {
    gl_Position = matrix * position;

    // each row is for an instance, each texel
    // per vertex. Of course if you want more data
    // per vertex then multiply vertexId by the number of
    // vec4s of data you need. If you need more instances
    // then compute a more complex offset based off instanceId
    vec2 uv = (vec2(vertexId, instanceId) + .5) / dataTextureSize;
    vec4 data = texture2D(dataTexture, uv);

    v_texcoord = data.xy;
  }
  `;

  const fs = `
  precision mediump float;

  varying vec2 v_texcoord;

  void main() {
    gl_FragColor = vec4(v_texcoord, 0, 1);
  }
  `;

  // compile shaders, link, look up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

  const cubesAcross = 20;
  const cubesDown = 10;
  const numCubes = cubesAcross * cubesDown;
  // matrix per instance
  const matrixData = new Float32Array(16 * numCubes);
  const matrices = [];
  const instanceIds = new Float32Array(numCubes);
  for (let i = 0; i < numCubes; ++i) {
    instanceIds[i] = i;
    // make a typedarray view for each matrix
    matrices.push(matrixData.subarray(i * 16, (i + 1) * 16));
  }

  const arrays = {
    position: [
       1,  1, -1, 
       1,  1,  1, 
       1, -1,  1,
       1, -1, -1,
       
      -1,  1,  1,
      -1,  1, -1,
      -1, -1, -1,
      -1, -1,  1, 
      
      -1,  1,  1, 
       1,  1,  1, 
       1,  1, -1,
      -1,  1, -1,
      
      -1, -1, -1,
       1, -1, -1,
       1, -1,  1,
      -1, -1,  1,
      
       1,  1,  1,
      -1,  1,  1,
      -1, -1,  1,
       1, -1,  1,
       
      -1,  1, -1,
       1,  1, -1,
       1, -1, -1,
      -1, -1, -1,
    ],
    vertexId: {
      numComponents: 1,
      data: [
         0,  1,  2,  3,
         4,  5,  6,  7,
         8,  9, 10, 11,
        12, 13, 14, 15,
        16, 17, 18, 19,
        20, 21, 21, 23,
      ],
    },
    indices: [
       0,  1,  2,  0,  2,  3,
       4,  5,  6,  4,  6,  7,
       8,  9, 10,  8, 10, 11,
      12, 13, 14, 12, 14, 15,
      16, 17, 18, 16, 18, 19,
      20, 21, 22, 20, 22, 23,
    ],
    instanceId: { numComponents: 1, data: instanceIds, divisor: 1 },
    matrix: { numComponents: 16, data: matrices, divisor: 1 },
  };
  // create buffers, upload data (gl.bufferData)
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

  // put UV data in texture
  const uvs = [];
  for (let y = 0; y < cubesDown; ++y) {
    const v0 = (y    ) / cubesDown;
    const v1 = (y + 1) / cubesDown;
    for (let x = 0; x < cubesAcross; ++x) {
      const u0 = (x    ) / cubesAcross;
      const u1 = (x + 1) / cubesAcross;
      uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
      uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
      uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
      uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
      uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
      uvs.push(u0, v0, 0, 0, u1, v0, 0, 0, u0, v1, 0, 0, u1, v1, 0, 0);
    }
  }
  
  const texWidth = 24; // width = 24 vertices * 1 texel per
  const texHeight = numCubes; // height = numInstances
  const tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texImage2D(
    gl.TEXTURE_2D,
    0,  // level
    gl.RGBA,
    texWidth,
    texHeight,
    0,  // border
    gl.RGBA,
    gl.FLOAT,
    new Float32Array(uvs));


  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.enable(gl.CULL_FACE);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    const fov = 30 * Math.PI / 180;
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.5;
    const zFar = 100;
    const projection = m4.perspective(fov, aspect, zNear, zFar);
    const eye = [1, 24, 76];
    const target = [18, 10, 0];
    const up = [0, 1, 0];

    const camera = m4.lookAt(eye, target, up);
    const view = m4.inverse(camera);
    const viewProjection = m4.multiply(projection, view);
    
    // update the instance for each matrix
    const spacing = 2.5 + Math.sin(time) * .5;
    let i = 0;
    for (let y = 0; y < cubesDown; ++y) {
      for (let x = 0; x < cubesAcross; ++x) {
        const matrix = matrices[i++];
        m4.translate(viewProjection, [
          x * spacing,
          y * spacing,
          0,
        ], matrix);
      }
    }

    gl.useProgram(programInfo.program);
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer, ext.vertexAttribDivisorANGLE
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    twgl.setUniforms(programInfo, {
      dataTexture: tex,
      dataTextureSize: [texWidth, texHeight],
    });
    
    // upload instance matrices to buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.matrix.buffer);
    gl.bufferData(gl.ARRAY_BUFFER, matrixData, gl.DYNAMIC_DRAW);

    ext.drawElementsInstancedANGLE(
      gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0,  numCubes);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

body {
  margin: 0;
}

canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}

<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

另请参见此答案

这篇关于在WebGL中-实例化几何体时,是否可以为每个实例传递每个顶点的属性信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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