编写一个函数来绘制 x/y 用于 WebGL 绘制的正弦波?

Write a function that will plot x/y for a sine wave to be drawn by WebGL?

我正在尝试学习 WebGL(以及来自 codingmath 的一些数学知识)。今天的目标是在任何开始和结束方向绘制正弦波。 是这样的:

我只是在 makePoints() 方法中遗漏了一些东西。我的观点很奇怪,我对下一步该去哪里有点傻眼了。

问题:

如何修复我的 makePoints() 函数,以便它绘制出正弦波的 x 和 y 坐标。

let gl,
    shaderProgram,
    vertices,
    canvas;

const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
  gl_Position = coords;
  gl_PointSize = pointSize;
}
`;

const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
  gl_FragColor = color;
}
`;

initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);

function setCanvasSize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}

function initGL() {
    canvas = document.querySelector('#canvas');
    gl = canvas.getContext('webgl');
    setCanvasSize();
    console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.clearColor(0, 0, 0, 1);
}

function makePoints(points) {
    const diff = (Math.PI * 2) / (points - 1);
    const len = {length: points};
    return Array.from(len, (_, i) => Math.sin(i * diff));
}

function createVertices() {
    vertices = makePoints(VERTEX_LENGTH);
   
    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);

    const coords = gl.getAttribLocation(shaderProgram, 'coords');
    gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(coords);
    // gl.bindBuffer(gl.ARRAY_BUFFER, null);

    const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
    gl.vertexAttrib1f(pointSize, 2);

    const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
    gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}

function createShader() {
    const vs = VERTEX_SHADER;

    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vs);
    gl.compileShader(vertexShader);

    const fs = FRAGMENT_SHADER;

    fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fs);
    gl.compileShader(fragmentShader);

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);

    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);
}


function draw() {
    console.log(vertices)
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH/2);
    requestAnimationFrame(draw);
}

function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
    display: block;
    position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>

您提供的顶点缓冲区不够大。它应该为 x 和 y 存储 2 个浮点数(而不是 1 个)

我重写了它:(检查 makePoints2)

let gl,
 shaderProgram,
 vertices,
 canvas;

const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;

void main(void) {
  gl_Position = coords;
  gl_PointSize = pointSize;
}
`;

const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;

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

initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);

function setCanvasSize() {
 canvas.width = window.innerWidth;
 canvas.height = window.innerHeight;
 gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}

function initGL() {
 canvas = document.querySelector('#canvas');
 gl = canvas.getContext('webgl');
 setCanvasSize();
 console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
 gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
 gl.clearColor(0, 0, 0, 1);
}

function makePoints(points) {
 const diff = (Math.PI * 2) / (points - 1);
 const len = {length: points};
 return Array.from(len, (_, i) => Math.sin(i * diff));
}

function makePoints2(points) {
    let arr = Array(points * 2);
    let index = 0;
 
    for(var i=0;i<points;i++) {

        let val = (i/points) * (Math.PI * 2);  // lerp 0..points => 0..2PI
        arr[index] =  ((i/points)*2)-1; // x, lerp 0..points => -1..1 range
        arr[index+1] = Math.sin(val);   // y, the sinus function...
        
        index += 2; // next vertex
    }
    return arr;
}


function createVertices() {
 // Feel like my function is close but I'm missing something
 vertices = makePoints2(VERTEX_LENGTH);

    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);

    const coords = gl.getAttribLocation(shaderProgram, 'coords');
    gl.enableVertexAttribArray(coords);
    gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0); 
    const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
    gl.vertexAttrib1f(pointSize, 2);

    const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
    gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}

function createShader() {
 const vs = VERTEX_SHADER;

 const vertexShader = gl.createShader(gl.VERTEX_SHADER);
 gl.shaderSource(vertexShader, vs);
 gl.compileShader(vertexShader);

 const fs = FRAGMENT_SHADER;

 fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
 gl.shaderSource(fragmentShader, fs);
 gl.compileShader(fragmentShader);

 shaderProgram = gl.createProgram();
 gl.attachShader(shaderProgram, vertexShader);
 gl.attachShader(shaderProgram, fragmentShader);

 gl.linkProgram(shaderProgram);
 gl.useProgram(shaderProgram);
}


function draw() {
 console.log(vertices)
 //gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
 gl.clear(gl.COLOR_BUFFER_BIT);
 gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH);
 //requestAnimationFrame(draw);
}

function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
    display: block;
    position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>

感谢 gman 如何制作片段

由于您的代码期望每个顶点有 2 个点,因此您需要为偶数 (x) 和奇数 (y) 值设置 return 不同的值。

我发现理解冗长的代码要容易得多,所以这是我的 makePoints。请注意,我发现像这样在循环中始终计算 lerp0to1 值很有用。然后我可以使用该值轻松转换为我想要的几乎任何类型的数据。

function makePoints(points) {
  const highestPointNdx = points / 2 - 1;
  return Array.from({length: points}, (_, i) => {
    const pointId = i / 2 | 0;
    const lerp0To1 = pointId / highestPointNdx;
    const odd = i % 2;
    return odd
      ? Math.sin(lerp0To1 * Math.PI * 2) // Y
      : (lerp0To1 * 2 - 1);              // X
  });
}

let gl,
    shaderProgram,
    vertices,
    canvas;

const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
  gl_Position = coords;
  gl_PointSize = pointSize;
}
`;

const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
  gl_FragColor = color;
}
`;

initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);

function setCanvasSize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}

function initGL() {
    canvas = document.querySelector('#canvas');
    gl = canvas.getContext('webgl');
    setCanvasSize();
    console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.clearColor(0, 0, 0, 1);
}

function makePoints(points) {
  const highestPointNdx = points / 2 - 1;
  return Array.from({length: points}, (_, i) => {
    const pointId = i / 2 | 0;
    const lerp0To1 = pointId / highestPointNdx;
    const odd = i % 2;
    return odd
      ? Math.sin(lerp0To1 * Math.PI * 2) // Y
      : (lerp0To1 * 2 - 1);              // X
  });
}


function createVertices() {
    vertices = makePoints(VERTEX_LENGTH);
console.log(vertices);   
    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);

    const coords = gl.getAttribLocation(shaderProgram, 'coords');
    gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(coords);
    // gl.bindBuffer(gl.ARRAY_BUFFER, null);

    const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
    gl.vertexAttrib1f(pointSize, 2);

    const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
    gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}

function createShader() {
    const vs = VERTEX_SHADER;

    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vs);
    gl.compileShader(vertexShader);

    const fs = FRAGMENT_SHADER;

    fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fs);
    gl.compileShader(fragmentShader);

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);

    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);
}


function draw() {
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH/2);
    requestAnimationFrame(draw);
}

function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
    display: block;
    position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>

让我补充一下,我认为 makePoints 目前有点令人困惑。我会更改它以获取您想要的点数,而不是顶点缓冲区中的值数(这是现在所需要的),这与点数不同。如果你想要 N 个点,你需要 2*N 个值。所以,我将其更改为

function makePoints(numPoints) {
  const highestPointNdx = numPoints - 1;
  return Array.from({length: numPoints * 2}, (_, i) => {
    const pointId = i / 2 | 0;
    const lerp0To1 = pointId / highestPointNdx;
    const isY = i % 2;
    return isY
      ? Math.sin(lerp0To1 * Math.PI * 2) // Y
      : (lerp0To1 * 2 - 1);              // X
  });
}

然后我传入 VERTEX_LENGTH 并为 gl.drawArrays 使用相同的值,如果我使用 3D 点而不是 2D 点,则两者都不必更改。

let gl,
    shaderProgram,
    vertices,
    canvas;

const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
  gl_Position = coords;
  gl_PointSize = pointSize;
}
`;

const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
  gl_FragColor = color;
}
`;

initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);

function setCanvasSize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}

function initGL() {
    canvas = document.querySelector('#canvas');
    gl = canvas.getContext('webgl');
    setCanvasSize();
    console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    gl.clearColor(0, 0, 0, 1);
}

function makePoints(numPoints) {
  const highestPointNdx = numPoints - 1;
  return Array.from({length: numPoints * 2}, (_, i) => {
    const pointId = i / 2 | 0;
    const lerp0To1 = pointId / highestPointNdx;
    const isY = i % 2;
    return isY
      ? Math.sin(lerp0To1 * Math.PI * 2) // Y
      : (lerp0To1 * 2 - 1);              // X
  });
}

function createVertices() {
    vertices = makePoints(VERTEX_LENGTH);
    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);

    const coords = gl.getAttribLocation(shaderProgram, 'coords');
    gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(coords);
    // gl.bindBuffer(gl.ARRAY_BUFFER, null);

    const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
    gl.vertexAttrib1f(pointSize, 2);

    const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
    gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}

function createShader() {
    const vs = VERTEX_SHADER;

    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vs);
    gl.compileShader(vertexShader);

    const fs = FRAGMENT_SHADER;

    fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fs);
    gl.compileShader(fragmentShader);

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);

    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);
}


function draw() {
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH);
    requestAnimationFrame(draw);
}

function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
    display: block;
    position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>