简体   繁体   English

WebGL 绘制三角形以一次筛选一个点

[英]WebGL draw triangles to screen one point at a time

I am really new to WebGL and have been trying to create a program that will draw points of a color specified in color sliders on the canvas with a mouse click and after three points are drawn, they will be connected into a triangle.我真的是 WebGL 的新手,并且一直在尝试创建一个程序,该程序将通过单击鼠标在画布上的颜色滑块中绘制指定颜色的点,在绘制三个点后,它们将连接成一个三角形。 I created an array called "points" to pass in the data of the mouse position on click and then I flatten that into "vertexData" to be sent to the buffer and shaders.我创建了一个名为“points”的数组,以在单击时传入鼠标位置的数据,然后将其展平为“vertexData”以发送到缓冲区和着色器。 I cant get anything to come up currently however.但是,我目前无法提出任何问题。 Any help would be greatly appreciated.任何帮助将不胜感激。 Thanks in advance.提前致谢。

"use strict"; “严格使用”;

// Constructor
//
// @param canvasID - string containing name of canvas to render.
//          Buttons and sliders should be prefixed with this string.
//
function Lab2(canvasID /* name of canvas to render */) {
  this.canvasID = canvasID;
  this.canvas = document.getElementById(canvasID);
  if (!this.canvas) {
    alert("Canvas ID '" + canvasID + "' not found.");
    return;
  }
  this.gl = WebGLUtils.setupWebGL(this.canvas);
  if (!this.gl) {
    alert("WebGL isn't available in this browser");
    return;
  }

  this.init();
}

// Define prototype values common to all Lab2 objects
Lab2.prototype.gl = null;

Lab2.prototype.toString = function () {
  return JSON.stringify(this);
};

Lab2.prototype.init = function () {
  var canvas = this.canvas;
  var gl = this.gl;
  var t = this;  // make available to event handlers

  // WebGL setup
  gl.viewport(0, 0, canvas.width, canvas.height);

  // Compile and link shaders
  this.shaderProgram = initShaders(gl, "vShader.glsl", "fShader.glsl");
  if (this.shaderProgram === null)
    return;
  gl.useProgram(this.shaderProgram);

  // Define names for colors
  var white = vec3(1.0, 1.0, 1.0);
  var red = vec3(1.0, 0.0, 0.0);
  var green = vec3(0.0, 1.0, 0.0);
  var blue = vec3(0.0, 0.0, 1.0);
  var yellow = vec3(1.0, 1.0, 1.0);

  // Array of alternating initial vertex coordinates and colors for each vertex
  var points = [];

  this.vertexData = flatten(points);

  // Count of points in vertexData
  this.pointCount = points.size();

  var floatSize = 4;  // size of gl.FLOAT in bytes
  // Load vertex data into WebGL buffer
  this.vertexCoordBuffer = gl.createBuffer();  // get unique buffer ID
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexCoordBuffer);  // make this the active buffer
  gl.bufferData(gl.ARRAY_BUFFER, this.vertexData, gl.STATIC_DRAW);  // write data to buffer

  // Define data layout in buffer for position.  Postions are 3 floats,
  // interleaved with 3 floats for colors, starting at beginning of buffer.
  this.vPosition = gl.getAttribLocation(this.shaderProgram, "vPosition");
  gl.vertexAttribPointer(this.vPosition, 3, gl.FLOAT, false, 6 * floatSize, 0);
  gl.enableVertexAttribArray(this.vPosition);

  // Define data layout in buffer for colors.  Colors are 3 floats,
  // interleaved with 3 floats for positions, starting after first position in buffer.
  this.vColor = gl.getAttribLocation(this.shaderProgram, "vColor");
  gl.vertexAttribPointer(this.vColor, 3, gl.FLOAT, false, 6 * floatSize, 3 * floatSize);
  gl.enableVertexAttribArray(this.vColor);

  // Define callback for change of slider value
  var sliderCallback = function (e) {
    // Update text display for slider
    var color = e.target.value;
    e.target.valueDisplay.textContent = color;

    // Re-render canvas
    requestAnimationFrame(render);
  };

  // Set up HTML user interface
  this.colors = ["r", "g", "b"];
  var rgbSliders = [];         // array of slider HTML elements
  var rgbSliderValues = [];    // array of slider value HTML elements

  // Set up an object with sliders for the three colors. The sliders are
  // accessed using "indices" of "r", "g", and "b".
  for (var i in this.colors) {
    var color = this.colors[i];
    var sliderID = this.canvasID + "-" + color + "-slider";
    rgbSliders[color] = document.getElementById(sliderID);
    if (rgbSliders[color] === null) {
      alert("Slider ID not found: " + sliderID);
      return;
    }
    var valueID = this.canvasID + "-" + color + "-value";
    rgbSliderValues[color] = document.getElementById(valueID);
    if (rgbSliders[color] === null) {
      alert("Slider value ID not found: " + sliderID);
      return;
    }
    rgbSliders[color].valueDisplay = rgbSliderValues[color];  // attach to slider

    // Set callback on slider input
    rgbSliders[color].addEventListener("input", sliderCallback);
  }
  this.rgbSliders = rgbSliders;

  var resetButton = document.getElementById(this.canvasID + "-reset-button");
  if (resetButton === null) {
    alert("Reset button ID not found: " + this.canvasID + "-reset-button");
    return;
  }

  // Set up callback to render a frame
  var render = function () {
    t.Render();
  };

  // Set up the callback for the reset button
  resetButton.addEventListener("click", function () {
    // Reset all the sliders to the middle value
    for (var i in rgbSliders) {
      rgbSliders[i].value = rgbSliders[i].max / 2.0;
      rgbSliders[i].valueDisplay.textContent =
              rgbSliders[i].valueAsNumber / rgbSliders[i].max;
    }
    requestAnimationFrame(render);
  });

  // Set up mouse tracking
  var mouseX = document.getElementById(this.canvasID + "-mousex");
  var mouseY = document.getElementById(this.canvasID + "-mousey");
  var mouseButton = document.getElementById(this.canvasID + "-mousebutton");
  this.mouseDown = [ false, false, false ];  // track mouse button state
  mouseButton.textContent = this.mouseDown;
  if (mouseX === null || mouseY === null || mouseButton === null) {
    alert("Mouse output HTML IDs not found");
    return;
  }

  // Add mouse event handlers
  canvas.addEventListener("mousedown", function (e) {
    t.mouseDown[e.button] = true;
    mouseButton.textContent = t.mouseDown;
    getMouseClickPosition();
  });
  canvas.addEventListener("mouseup", function (e) {
    t.mouseDown[e.button] = false;
    mouseButton.textContent = t.mouseDown;
  });
  canvas.addEventListener("mousemove", function (e) {
    mouseX.textContent = e.pageX - e.target.offsetLeft;
    mouseY.textContent = e.pageY - e.target.offsetTop;
  });

  // Kick things off with an initial rendering
  requestAnimationFrame(render);
};

/**
 * GetSliderColors - get the current RGB color represented by the sliders
 *   as a vec3.
 *   
 * @returns {vec3} current slider color
 */
Lab2.prototype.getSliderColor = function () {
  // Build an array of color values based on the current slider colors
  var colorValues = [];
  for (var i in this.colors) {
    var color = this.colors[i];
    var colorValue = this.rgbSliders[color].valueAsNumber;
    colorValues[i] = colorValue;
  }

  return vec3(colorValues);
};

Lab2.prototype.getMouseClickPosition = function (){
    var point = vec2(this.mouseX, this.mouseY);
    this.points.push(point);
};

/**
 * Render - draw the frame
 *
 */
Lab2.prototype.Render = function () {
  var gl = this.gl;
  gl.clearColor(0.0, 0.0, 0.25, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.POINTS, 0, this.pointCount);
};

There are many issues with the code above上面的代码有很多问题

As a start you create an empty array called points and then flatten it, it's empty so the flattened version is empty, you then make a WebGL buffer that's empty as a result.首先,您创建一个名为points的空数组,然后将其展平,它是空的,因此展平后的版本是空的,然后您创建了一个空的 WebGL 缓冲区。 Pushing points into the javascript array won't update the buffer in WebGL so at a minimum you need to update the buffer in WebGL each time you add points.将点推入 javascript 数组不会更新 WebGL 中的缓冲区,因此至少每次添加点时都需要更新 WebGL 中的缓冲区。

Lab2.prototype.getMouseClickPosition = function (){
    var point = vec2(this.mouseX, this.mouseY);
    this.points.push(point);

    this.vertexData = flatten(this.points);

    // Count of points in vertexData
    this.pointCount = this.points.size();

    var floatSize = 4;  // size of gl.FLOAT in bytes
    // Load vertex data into WebGL buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexCoordBuffer);  // make this the active buffer
    gl.bufferData(gl.ARRAY_BUFFER, this.vertexData, gl.STATIC_DRAW);  // write data to buffer

Further you add 2d values to points in getMouseClickPosition but according to your code where you create the WebGL buffers you've set it up so it expects points with 3 values each, not 2, and then expects 3 color values.此外,您将 2d 值添加到getMouseClickPosition points ,但根据您创建 WebGL 缓冲区的代码,您已经设置了它,因此它期望每个点具有 3 个值,而不是 2,然后期望 3 个颜色值。 In other words you're putting [x, y, x, y, x, y] in points but based on your setup code it needs to be [x, y, z, r, g, b, x, y, z, r, g, b, x, y, z, r, g, b]换句话说,您将[x, y, x, y, x, y]放在points但根据您的设置代码,它需要是[x, y, z, r, g, b, x, y, z, r, g, b, x, y, z, r, g, b]

Lab2.prototype.getMouseClickPosition = function (){
    var point = vec3(this.mouseX, this.mouseY, 0);  // no idea what Z value you want
    this.points.push(point);
    this.points.push(vec3(red, green, blue));   // you need to figure out red, green, and blue
     ...

further you're calling points is a javascript array so this code您进一步调用points是一个 javascript 数组,因此此代码

this.pointCount = points.size();

makes no sense and would likely crash since there is no size function for javascript arrays.没有意义并且可能会崩溃,因为 javascript 数组没有size函数。 The length of an array is array.length as in points.length and further, if you're putting 2 vec3 values per point, one for position, one for color, then the pointCount is points.length / 2数组的长度是array.lengthpoints.length ,此外,如果您为每个点放置2个vec3值,一个用于位置,一个用于颜色,那么pointCount是points.length / 2

I also see no where where this.points is assigned even though it's referenced.即使引用了this.points我也看不到它在哪里分配。

And that's just some of the issues.这只是部分问题。

  • If you want to draw both points and triangles you'll need 2 calls to gl.drawArrays .如果您想同时绘制点和三角形,则需要调用 2 次gl.drawArrays One for points and one for triangles.一种用于点,一种用于三角形。

  • this.mouseX and this.mouseY are not defined. this.mouseXthis.mouseY没有定义。

  • getSliderColor returns an array of vec3 instead of vec3 getSliderColor 返回一个数组 vec3 而不是 vec3

  • not calling render after adding a point添加点后不调用渲染

  • other...其他...

And you didn't show your shaders.而且你没有展示你的着色器。 It seems unlikely the code would work as is as you're passing in pixel coordinates but shaders work with clip space coordinates.当您传入像素坐标时,代码似乎不太可能按原样工作,但着色器使用剪辑空间坐标。 There is no setup in your code to deal with that conversion.您的代码中没有设置来处理该转换。 It's possible it's hard coded in your shaders but that's not normal.它可能在您的着色器中进行了硬编码,但这不正常。 It's more common to pass in the canvas resolution in one form or another, a projection matrix, or to convert to clip space in JavaScript (less common) but nothing like any one of those appears in your code.更常见的是以一种或另一种形式传入画布分辨率、投影矩阵,或者在 JavaScript 中转换为剪辑空间(不太常见),但不会出现在您的代码中。

I'd suggest you learn how to use a debugger as it would show errors for some of the issues with the code.我建议您学习如何使用调试器,因为它会显示代码中某些问题的错误。

I'd also suggest these articles for more WebGL help.我还建议阅读这些文章以获得更多 WebGL 帮助。

 // Constructor // // @param canvasID - string containing name of canvas to render. // Buttons and sliders should be prefixed with this string. // function Lab2(canvasID /* name of canvas to render */) { this.canvasID = canvasID; this.canvas = document.getElementById(canvasID); if (!this.canvas) { alert("Canvas ID '" + canvasID + "' not found."); return; } this.gl = WebGLUtils.setupWebGL(this.canvas); if (!this.gl) { alert("WebGL isn't available in this browser"); return; } this.init(); } // Define prototype values common to all Lab2 objects Lab2.prototype.gl = null; Lab2.prototype.toString = function () { return JSON.stringify(this); }; Lab2.prototype.init = function () { var canvas = this.canvas; var gl = this.gl; var t = this; // make available to event handlers // WebGL setup gl.viewport(0, 0, canvas.width, canvas.height); // Compile and link shaders this.shaderProgram = initShaders(gl, "vShader.glsl", "fShader.glsl"); if (this.shaderProgram === null) return; gl.useProgram(this.shaderProgram); this.resolutionLoc = gl.getUniformLocation(this.shaderProgram, 'resolution'); // Define names for colors var white = vec3(1.0, 1.0, 1.0); var red = vec3(1.0, 0.0, 0.0); var green = vec3(0.0, 1.0, 0.0); var blue = vec3(0.0, 0.0, 1.0); var yellow = vec3(1.0, 1.0, 1.0); // Array of alternating initial vertex coordinates and colors for each vertex var points = []; this.points = points; this.vertexData = flatten(points); // Count of points in vertexData this.pointCount = 0; var floatSize = 4; // size of gl.FLOAT in bytes // Load vertex data into WebGL buffer this.vertexCoordBuffer = gl.createBuffer(); // get unique buffer ID gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexCoordBuffer); // make this the active buffer gl.bufferData(gl.ARRAY_BUFFER, this.vertexData, gl.STATIC_DRAW); // write data to buffer // Define data layout in buffer for position. Postions are 3 floats, // interleaved with 3 floats for colors, starting at beginning of buffer. this.vPosition = gl.getAttribLocation(this.shaderProgram, "vPosition"); gl.vertexAttribPointer(this.vPosition, 3, gl.FLOAT, false, 6 * floatSize, 0); gl.enableVertexAttribArray(this.vPosition); // Define data layout in buffer for colors. Colors are 3 floats, // interleaved with 3 floats for positions, starting after first position in buffer. this.vColor = gl.getAttribLocation(this.shaderProgram, "vColor"); gl.vertexAttribPointer(this.vColor, 3, gl.FLOAT, false, 6 * floatSize, 3 * floatSize); gl.enableVertexAttribArray(this.vColor); // Define callback for change of slider value var sliderCallback = function (e) { // Update text display for slider var color = e.target.value; e.target.valueDisplay.textContent = color; // Re-render canvas requestAnimationFrame(render); }; // Set up HTML user interface this.colors = ["r", "g", "b"]; var rgbSliders = []; // array of slider HTML elements var rgbSliderValues = []; // array of slider value HTML elements // Set up an object with sliders for the three colors. The sliders are // accessed using "indices" of "r", "g", and "b". for (var i in this.colors) { var color = this.colors[i]; var sliderID = this.canvasID + "-" + color + "-slider"; rgbSliders[color] = document.getElementById(sliderID); if (rgbSliders[color] === null) { alert("Slider ID not found: " + sliderID); return; } var valueID = this.canvasID + "-" + color + "-value"; rgbSliderValues[color] = document.getElementById(valueID); if (rgbSliders[color] === null) { alert("Slider value ID not found: " + sliderID); return; } rgbSliders[color].valueDisplay = rgbSliderValues[color]; // attach to slider // Set callback on slider input rgbSliders[color].addEventListener("input", sliderCallback); } this.rgbSliders = rgbSliders; var resetButton = document.getElementById(this.canvasID + "-reset-button"); if (resetButton === null) { alert("Reset button ID not found: " + this.canvasID + "-reset-button"); return; } // Set up callback to render a frame var render = function () { t.Render(); }; // Set up the callback for the reset button resetButton.addEventListener("click", function () { // Reset all the sliders to the middle value for (var i in rgbSliders) { rgbSliders[i].value = rgbSliders[i].max / 2.0; rgbSliders[i].valueDisplay.textContent = rgbSliders[i].valueAsNumber / rgbSliders[i].max; } requestAnimationFrame(render); }); // Set up mouse tracking var mouseX = document.getElementById(this.canvasID + "-mousex"); var mouseY = document.getElementById(this.canvasID + "-mousey"); var mouseButton = document.getElementById(this.canvasID + "-mousebutton"); this.mouseDown = [ false, false, false ]; // track mouse button state mouseButton.textContent = this.mouseDown; if (mouseX === null || mouseY === null || mouseButton === null) { alert("Mouse output HTML IDs not found"); return; } // Add mouse event handlers canvas.addEventListener("mousedown", function (e) { t.mouseDown[e.button] = true; mouseButton.textContent = t.mouseDown; t.mouseX = e.pageX - e.target.offsetLeft; t.mouseY = e.pageY - e.target.offsetTop; t.getMouseClickPosition(); requestAnimationFrame(render); }); canvas.addEventListener("mouseup", function (e) { t.mouseDown[e.button] = false; mouseButton.textContent = t.mouseDown; }); canvas.addEventListener("mousemove", function (e) { mouseX.textContent = e.pageX - e.target.offsetLeft; mouseY.textContent = e.pageY - e.target.offsetTop; }); // Kick things off with an initial rendering requestAnimationFrame(render); }; /** * GetSliderColors - get the current RGB color represented by the sliders * as a vec3. * * @returns {vec3} current slider color */ Lab2.prototype.getSliderColor = function () { // Build an array of color values based on the current slider colors var colorValues = []; for (var i in this.colors) { var color = this.colors[i]; var colorValue = this.rgbSliders[color].valueAsNumber; colorValues[i] = colorValue; } return vec3(...colorValues); }; Lab2.prototype.getMouseClickPosition = function (){ var point = vec3(this.mouseX, this.mouseY, 0); this.points.push(point, this.getSliderColor()); this.vertexData = flatten(this.points); // Count of points in vertexData this.pointCount = this.points.length / 2; var floatSize = 4; // size of gl.FLOAT in bytes var gl = this.gl; // Load vertex data into WebGL buffer gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexCoordBuffer); // make this the active buffer gl.bufferData(gl.ARRAY_BUFFER, this.vertexData, gl.STATIC_DRAW); // write data to buffer }; /** * Render - draw the frame * */ Lab2.prototype.Render = function () { var gl = this.gl; gl.clearColor(0.0, 0.0, 0.25, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); if (!this.pointCount) return; gl.useProgram(this.shaderProgram); gl.uniform2f(this.resolutionLoc, gl.canvas.width, gl.canvas.height); gl.drawArrays(gl.POINTS, 0, this.pointCount); gl.drawArrays(gl.TRIANGLES, 0, this.pointCount); }; const WebGLUtils = { setupWebGL(elem) { return elem.getContext('webgl'); }, }; function initShaders(gl, vsHref, fsHref) { // ignore vsHref and fsHref and guess what the shaders are return twgl.createProgram(gl, [` attribute vec4 vPosition; attribute vec3 vColor; uniform vec2 resolution; varying vec3 v_color; void main() { gl_PointSize = 10.0; gl_Position = vec4(vPosition.xy / resolution * vec2(2, -2) + vec2(-1, 1), 0, 1); v_color = vColor; } `, ` precision mediump float; varying vec3 v_color; void main() { gl_FragColor = vec4(v_color, 1); } `]); } const vec3 = (x, y, z) => [x, y, z]; const flatten = a => new Float32Array(a.flat()); const lab = new Lab2('c');
 #outer { display: flex; }
 <div id="outer"><canvas id='c'></canvas><div> <input type="range" value="1" min="0" max="1" step="0.01" id="cr-slider"><span id="cr-value"></span><br> <input type="range" min="0" max="1" step="0.01" id="cg-slider"><span id="cg-value"></span><br> <input type="range" min="0" max="1" step="0.01" id="cb-slider"><span id="cb-value"></span><br> <button type="button" id="c-reset-button">reset</button> <div id="c-mousex"></div> <div id="c-mousey"></div> <div id="c-mousebutton"></div> </div> </div> <script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM