vertexshaderart.com 更像是一个拼图、玩具、艺术盒、创意编码实验,而不是一个好的 WebGL 示例。 shadertoy.com 也是如此。一个例子like this 很漂亮,但它在它的小窗口中以 20fps 的速度运行,在我的 2014 Macbook Pro 上以大约 1fps 的全屏速度运行,而我的 MBP 可以播放beautiful games with huge worlds rendered fullscreen at 60fps。换句话说,这些技术更多地是为了艺术/娱乐/游戏/心理锻炼,以及试图以极端限制使事情发生的乐趣,而不是真正的好技术。
我想说的是,vertexshaderart 和 shadertoy 都很有趣但不切实际。
vertexshaderart 的工作方式是提供一个计数vertexId 来计算顶点数。 0 到 N,其中 N 是设置 UI 顶部的计数。对于每个计数,您输出 gl_Position 和 v_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 个单位的网格。我已经设置了画布原点,所以 0,0 就像 WebGL 一样位于中心,因此画布的地址是 +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 上切换模式(三角形/线/点)时,所做的就是更改传递给 gl.drawArrays 的内容(gl.POINTS、gl.LINES、gl.TRIANGLES) .点本身是在顶点着色器中生成的,如上例。
所以剩下的问题是,您要达到什么具体效果。然后我们可以知道要建议什么来实现它。你可能想为此提出一个新问题(这样这个答案仍然与上面的问题相匹配)