启动一些 vertices/triangles 供顶点着色器使用

initiate a number of vertices/triangles for vertex shader to use

我一直在研究 vertexshaderart.com,我想在一个单独的网站上使用我学到的东西。虽然我以前使用过着色器,但在网站上实现的某些效果取决于是否可以访问 vertices/lines/triangles。虽然传递顶点很容易(至少它是 THREE.js,虽然对于简单的着色器来说有点矫枉过正,但在某些情况下也需要着色器材质),创建三角形似乎有点复杂。

我从源头上搞不懂,这里切换模式的时候三角形到底是怎么创建的?

我想复制这种行为,但老实说我不知道​​如何处理它。我可以通过三个来创建多个三角形,但是由于有这么多单独的对象,性能会迅速受到影响。此处创建的三角形是单独的实体还是一个几何体的一部分?

vertexshaderart.com is more of a puzzle, toy, art box, creative coding experiment than an example of the good WebGL. The same is true of shadertoy.com. An example like this is beautiful but it runs at 20fps in it's tiny window and about 1fps fullscreen on my 2014 Macbook Pro and yet my MBP can play beautiful games with huge worlds rendered fullscreen at 60fps。换句话说,这些技术更多地是为了 art/fun/play/mental 锻炼和尝试在极限条件下让事情发生的乐趣,而不是真正的好技术。

我想表达的意思是 vertexshaderart 和 shadertoy 都很有趣但不实用。

vertexshaderart 的工作方式是它提供了一个计算顶点的计数 vertexId。 0 到 N,其中 N 是设置 UI 顶部的计数。对于每个计数,您输出 gl_Positionv_color(颜色)。

所以,如果你想画一些东西,你需要提供数学来根据计数生成顶点位置。例如,让我们先使用 Canvas 2D

这是一个用 JavaScript 编写的假 JavaScript 顶点着色器,除了 vertexId 之外什么都不给,将绘制一个 1 个单位高和 N 个单位长的网格,其中 N = 顶点数 ( vertexCount) / 6.

function ourPseudoVertexShader(vertexId, time) {
  // let's compute an infinite grid of points based off vertexId
  var x = Math.floor(vertexId / 6) + (vertexId % 2);
  var y = (Math.floor(vertexId / 2) + Math.floor(vertexId / 3)) % 2;

  // color every other triangle red or green
  var triangleId = Math.floor(vertexId / 3);
  var color = triangleId % 2 ? "#F00" : "#0F0";

  return {
    x: x * 0.2,
    y: y * 0.2,
    color: color,
  };
}

我们从提供 vertexId

的循环中调用它
  for (var count = 0; count < vertexCount; count += 3) {

    // get 3 points
    var position0 = ourPseudoVertexShader(count + 0, time);
    var position1 = ourPseudoVertexShader(count + 1, time);
    var position2 = ourPseudoVertexShader(count + 2, time);

    // draw triangle
    ctx.beginPath();
    ctx.moveTo(position0.x, position0.y);
    ctx.lineTo(position1.x, position1.y);
    ctx.lineTo(position2.x, position2.y);
    ctx.fillStyle = position0.color;
    ctx.fill();
  }

如果你 运行 它在这里你会看到一个 1 个单位高和 N 个单位长的网格。我已经设置了 canvas 原点,所以 0,0 就像 WebGL 一样位于中心,因此 canvas 被寻址为 +1 到 -1 横向和 +1 到 -1 向下

var vertexCount = 100;

function ourPseudoVertexShader(vertexId, time) {
  // let's compute an infinite grid of points based off vertexId
  var x = Math.floor(vertexId / 6) + (vertexId % 2);
  var y = (Math.floor(vertexId / 2) + Math.floor(vertexId / 3)) % 2;
  
  // color every other triangle red or green
  var triangleId = Math.floor(vertexId / 3);
  var color = triangleId % 2 ? "#F00" : "#0F0";
  
  return {
    x: x * 0.2,
    y: y * 0.2,
    color: color,
  };
}

var ctx = document.querySelector("canvas").getContext("2d");
requestAnimationFrame(render);

function render(time) {
  time *= 0.001;

  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.save();
  ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
  ctx.scale(ctx.canvas.width / 2, -ctx.canvas.height / 2);
  
  // lets assume triangles
  for (var count = 0; count < vertexCount; count += 3) {

    // get 3 points
    var position0 = ourPseudoVertexShader(count + 0, time);
    var position1 = ourPseudoVertexShader(count + 1, time);
    var position2 = ourPseudoVertexShader(count + 2, time);
    
    // draw triangle
    ctx.beginPath();
    ctx.moveTo(position0.x, position0.y);
    ctx.lineTo(position1.x, position1.y);
    ctx.lineTo(position2.x, position2.y);
    ctx.fillStyle = position0.color;
    ctx.fill();
  }
  
  ctx.restore();
  
  requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas width="500" height="200"></canvas>

在 WebGL 中做同样的事情意味着用计数创建一个缓冲区

var count = [];
for (var i = 0; i < vertexCount; ++i) {
  count.push(i);
}

然后将该计数放入缓冲区并将其用作着色器的属性。

这是与上面的假着色器等效的着色器

attribute float vertexId;

uniform float time;

varying vec4 v_color;

void main() {
  // let's compute an infinite grid of points based off vertexId
  float x = floor(vertexId / 6.) + mod(vertexId, 2.);
  float y = mod(floor(vertexId / 2.) + floor(vertexId / 3.), 2.);

  // color every other triangle red or green
  float triangleId = floor(vertexId / 3.);
  v_color = mix(vec4(0, 1, 0, 1), vec4(1, 0, 0, 1), mod(triangleId, 2.));

  gl_Position = vec4(x * 0.2, y * 0.2, 0, 1);
}

如果我们运行我们会得到相同的结果

var vs = `
attribute float vertexId;

uniform float vertexCount;
uniform float time;

varying vec4 v_color;

void main() {
  // let's compute an infinite grid of points based off vertexId
  float x = floor(vertexId / 6.) + mod(vertexId, 2.);
  float y = mod(floor(vertexId / 2.) + floor(vertexId / 3.), 2.);

  // color every other triangle red or green
  float triangleId = floor(vertexId / 3.);
  v_color = mix(vec4(0, 1, 0, 1), vec4(1, 0, 0, 1), mod(triangleId, 2.));

  gl_Position = vec4(x * 0.2, y * 0.2, 0, 1);
}
`;

var fs = `
precision mediump float;

varying vec4 v_color;

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

var vertexCount = 100;
var gl = document.querySelector("canvas").getContext("webgl");
var count = [];
for (var i = 0; i < vertexCount; ++i) {
  count.push(i);
}

var bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  vertexId: { numComponents: 1, data: count, },
});

var programInfo = twgl.createProgramInfo(gl, [vs, fs]);

var uniforms = {
  time: 0,
  vertexCount: vertexCount,
};

requestAnimationFrame(render);

function render(time) {
  uniforms.time = time * 0.001;
  
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.setUniforms(programInfo, uniforms);
  twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
  
  requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<canvas width="500" height="200"></canvas>

vertexshartart 上的所有其他内容都只是用来制作有趣图案的创造性数学。您可以使用 time 来制作动画。还提供了带有声音数据的纹理。

There are some tutorials here

因此,在回答您的问题时,当您在 vertexshaderart.com 上切换模式 (triangles/lines/points) 时,所做的只是更改传递给 gl.drawArrays 的内容(gl.POINTSgl.LINESgl.TRIANGLES)。点本身是在顶点着色器中生成的,就像上面的例子一样。

那么问题就来了,你想要达到什么样的具体效果。然后我们就可以知道要实现它的建议是什么。你可能想问一个新问题(这样这个答案仍然与上面的问题相匹配)