【问题标题】:Write a function that will plot x/y for a sine wave to be drawn by WebGL?编写一个函数,为 WebGL 绘制的正弦波绘制 x/y?
【发布时间】:2018-05-15 04:01:10
【问题描述】:

我正在尝试学习 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>

【问题讨论】:

    标签: javascript webgl vertex-shader trigonometry


    【解决方案1】:

    由于您的代码要求每个顶点有 2 个点,因此您需要 makePoints 为偶数 (x) 和奇数 (y) 值返回不同的值。

    我发现理解冗长的代码要容易得多,所以这是我的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>

    【讨论】:

      【解决方案2】:

      您提供的顶点缓冲区不够大。它应该为 x AND 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;
      }
      &lt;canvas id="canvas" width="500" height="500"&gt;&lt;/canvas&gt;

      感谢 gman 关于如何进行 sn-ps

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多