Напишите функцию, которая будет строить график x / y для синусоидальной волны, которую рисует WebGL? - PullRequest
0 голосов
/ 15 мая 2018

Я пытаюсь выучить WebGL (и немного математики из codingmath ).Цель сегодня - нарисовать синусоидальную волну в любом направлении начала и конца.Примерно так: wave

Я просто что-то упустил в своем методе makePoints().Мои пункты странным образом излагаются, и я как-то не понимаю, куда идти дальше.

line

ВОПРОС:

Как исправить мою функцию 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>

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Поскольку ваш код ожидает 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>
0 голосов
/ 15 мая 2018

Буфер вершин, который вы указали, недостаточно велик. Следует хранить 2 числа с плавающей запятой для x и y (вместо 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>

Спасибо, Гман, как делать фрагменты

...