2

我正在尝试使用纯 webgl 创建一条虚线。我知道已经有一个问题,也许我很愚蠢,但我不知道如何让它发挥作用。我理解这个概念,但我不知道如何在着色器中获得沿路径的距离。先前的答案有以下几行:

varying float LengthSoFar; // <-- passed in from the vertex shader

那我怎么得到LengthSoFar?如何在顶点着色器中计算它?

我完全错过了什么吗?有人可以给我一个工作的例子吗?或者至少有一些好的线索?这几天我一直在用头撞墙。

4

2 回答 2

6

我假设它是这样工作的。你有一个位置缓冲区。你做了一个相应的缓冲区lengthSoFar

function distance(array, ndx1, ndx2) 
{
  ndx1 *= 3;
  ndx2 *= 3;

  var dx = array[ndx1 + 0] - array[ndx2 + 0];
  var dy = array[ndx1 + 1] - array[ndx2 + 1];
  var dz = array[ndx1 + 2] - array[ndx2 + 2];

  return Math.sqrt(dx * dx + dy * dy + dz * dz);
}

var positions = 
[
  0.123, 0.010, 0.233,
  0.423, 0.312, 0.344,
  0.933, 1.332, 0.101,
];

var lengthSoFar = [0];  // the length so far starts at 0
for (var ii = 1; ii < positions.length / 3; ++ii) 
{
  lengthSoFar.push(lengthSoFar[ii - 1] + distance(positions, ii - 1, ii));
}

现在,您可以为两者创建缓冲区,positions并将其作为属性lengthSoFar传递到顶点着色器,然后从那里将其作为变量传递给片段着色器。lengthSoFar

不幸的是,它不适用于索引几何(最常见的类型?)。换句话说,它不能与 一起使用gl.drawElements,只能与gl.drawArrays. 此外,虚线将以 3D 而非 2D 形式虚线,因此进入屏幕(远离观察者)的线看起来与穿过屏幕的线不同。当然,如果您正在绘制 2D,那么没有问题。

如果这些限制对你有好处,这能回答你的问题吗?

const gl = document.querySelector("#c").getContext("webgl");

// Note: createProgramFromScripts will call bindAttribLocation
// based on the index of the attibute names we pass to it.
var program = twgl.createProgramFromScripts(
    gl, 
    ["vshader", "fshader"], 
    ["a_position", "a_lengthSoFar"]);
gl.useProgram(program);

function distance(array, ndx1, ndx2) 
{
  ndx1 *= 3;
  ndx2 *= 3;

  var dx = array[ndx1 + 0] - array[ndx2 + 0];
  var dy = array[ndx1 + 1] - array[ndx2 + 1];
  var dz = array[ndx1 + 2] - array[ndx2 + 2];

  return Math.sqrt(dx * dx + dy * dy + dz * dz);
}

// Used this line in the console to generate the positions
// var sub = 10; for (var ii = 0; ii <= sub; ++ii) { r = (ii & 1) ? 0.5 : 0.9; a = ii * Math.PI * 2 / sub; console.log((Math.cos(a) * r).toFixed(3) + ", " + (Math.sin(a) * r).toFixed(3) + ", "); }

var positions = [
  0.900, 0.000, 0,
  0.405, 0.294, 0,
  0.278, 0.856, 0,
  -0.155, 0.476, 0,
  -0.728, 0.529, 0,
  -0.500, 0.000, 0,
  -0.728, -0.529, 0,
  -0.155, -0.476, 0,
  0.278, -0.856, 0,
  0.405, -0.294, 0,
  0.900, -0.000, 0,
];
    
var lengthSoFar = [0];  // the length so far starts at 0
for (var ii = 1; ii < positions.length / 3; ++ii) 
{
  lengthSoFar.push(lengthSoFar[ii - 1] + distance(positions, ii - 1, ii));
}

var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);

var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(lengthSoFar), gl.STATIC_DRAW);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);

// Since uniforms default to '0' and since 
// the default active texture is '0' we don't
// have to setup the texture uniform. 
var pixels = [
    0, 0, 255, 255,
    0, 0, 255, 255,
    0, 0, 255, 255,
    0, 0, 255, 255,
    0, 0, 0, 0,
    0, 0, 0, 0,
    255, 0, 0, 255,
    0, 0, 0, 0,
    0, 0, 0, 0,
    255, 0, 0, 255,
    0, 0, 0, 0,
    0, 0, 0, 0,
];

var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
    gl.TEXTURE_2D, 0, gl.RGBA, pixels.length / 4, 1, 0,
    gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(pixels));
gl.texParameteri(
    gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(
    gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  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.drawArrays(gl.LINE_STRIP, 0, positions.length / 3);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vshader" type="whatever">
    attribute vec4 a_position;
    attribute float a_lengthSoFar;
    varying float v_lengthSoFar;
    void main() {
      gl_Position = a_position;
      v_lengthSoFar = a_lengthSoFar;
    }    
</script>
<script id="fshader" type="whatever">
precision mediump float;
varying float v_lengthSoFar;
uniform sampler2D u_pattern;
#define NumDashes 6.0
void main() {
    gl_FragColor = texture2D(
      u_pattern, 
      vec2(fract(v_lengthSoFar * NumDashes)), 0.5);
}
</script>
<canvas id="c" width="300" height="300"></canvas>

注意:这是一篇可能有助于解释变量如何工作的文章

另请注意,您无法更改线条的粗细。为此,您需要从三角形中画线

于 2013-10-14T14:07:02.923 回答
1

将“LengthSoFar”作为顶点属性传递给着色器。当您在 javascript 代码(js 部分,而不是着色器部分)中创建着色器程序时,您可以创建类似

program.lengthSoFar = gl.getAttribLocation(program, 'aLengthSoFar');

顶点着色器中属性中的 aLengthSoFar

我推荐这个关于 webgl 的教程,它有所有的基础知识:http ://www.youtube.com/watch?v=me3BviH3nZc

您将很快意识到在 webgl 中进行图形编程的问题。虽然着色器编程本身并不难,但在 js 和 webgl 中设置所有细节以开始使用是非常痛苦的。

提示:永远记住图形编程是单向的。你有你的模型,即javascript代码。该模型将向您的顶点着色器发送信息,该顶点着色器将向您的片段着色器发送信息,该着色器将在屏幕上显示像素。不幸的是,没有什么可以削减的。

这就是为什么他们想出了属性和变量这样的东西。您可以在 javascript 中设置顶点着色器中的属性(如函数中的参数)。但是属性是只读的。因此,您必须复制它们并将它们作为变量传递给片段着色器。

于 2013-10-13T04:26:10.647 回答