为什么要更改所有点而不是一个点的颜色?

Why changed the color for all points instead of one?

我学习 WebGL。我写了一个示例:为每次鼠标单击绘制点。每个点都有自己的颜色(这取决于点击的坐标)。我希望得到类似这样的结果:

但是所有我的点一起改变自己的颜色每次鼠标点击(下一次点击鼠标图片):

为什么要更改所有点而不是一个点的颜色?

我的JavaScript代码:

var VSHADER_SOURCE = null;
var FSHADER_SOURCE = null;
var points = []; // coordinates of each mouse click (WebGL coordinate system)

// entry point
function main(){
  var canvas = document.getElementById('webgl');
  if(!canvas){
    console.log('Element with the "webgl" id wasn\'t found.');
    return;
  }
  var gl = getWebGLContext(canvas);
  if(!gl){
    console.log('Can\'t to get the WebGL context.');
    return;
  }
  loadShaderFromFile(gl,'./shaders/vertex.shader', gl.VERTEX_SHADER);
  loadShaderFromFile(gl,'./shaders/fragment.shader', gl.FRAGMENT_SHADER);
}

// loading vertex or fragment shader
function loadShaderFromFile(context, fileName, shaderType){
  var request = new XMLHttpRequest();
  request.onreadystatechange = function(){
    if(request.readyState == 4 && request.status != 404){
      onLoadShader(context, request.responseText, shaderType);
    }    
  }
  request.open('GET', fileName, true);
  request.send();
}

function onLoadShader(context, shader_source_code, shaderType){
  var gl = context;
  if(shaderType == gl.VERTEX_SHADER){
    VSHADER_SOURCE = shader_source_code;
  }
  else if(shaderType == gl.FRAGMENT_SHADER){
    FSHADER_SOURCE = shader_source_code;
  }
  else{
    console.log('Unexpected shader type.');
    return;
  }

  // only when code sources of both shaders are gotten then continue...
  if(VSHADER_SOURCE && FSHADER_SOURCE){
      start(context);
  }
}

// initializing of the shaders and registering of the 'mousedown' event 
function start(gl){
  if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)){
    console.log('Can\'t initialize the shaders.');
    return;
  }

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
  var canvas = document.getElementById('webgl');
  canvas.onmousedown = function(ev){
    // Attantion: I put the gl and canvas too.
    onMouseDown(ev, gl, canvas, u_FragColor);
  }  
}

// it redraws all points again for each mouse click
function onMouseDown(ev, gl, canvas, u_FragColor){  
  var x = ev.clientX;
  var y = ev.clientY;
  var rect = ev.target.getBoundingClientRect();
  var gl_x = (x - rect.left - canvas.width / 2) / (canvas.width / 2);
  var gl_y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
  points.push(gl_x);
  points.push(gl_y);
  console.log('x = ' + gl_x + '; y = ' + gl_y);  

  var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
  if(a_PointSize < 0){
    console.log('Can\'t to get a pointer for the "a_PointSize" variable.');
    return;
  }  
  gl.vertexAttrib1f(a_PointSize, 10.0);

  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if(a_Position < 0){
    console.log('Can\'t to get a pointer for the "a_Position" variable.');
    return;
  }

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  for(i = 0; i < points.length; i += 2){
    var _x = points[i];
    var _y = points[i+1];

    gl.vertexAttrib2f(a_Position, _x, _y);

    // Set color of point
    if (gl_x <= 0 && gl_y >= 0){
      gl.uniform4f(u_FragColor, 1.0, 0.0, 0.0, 1.0);
    }
    else if (gl_x > 0 && gl_y >= 0){
      gl.uniform4f(u_FragColor, 0.0, 1.0, 0.0, 1.0);
    }
    else if (gl_x <= 0 && gl_y <= 0){
      gl.uniform4f(u_FragColor, 0.0, 0.0, 1.0, 1.0);
    }
    else {
      gl.uniform4f(u_FragColor, 0.0, 1.0, 1.0, 1.0);
    }

    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

我还看到了其他示例(来自 this book效果相同并且工作正常(!!!):

// ColoredPoint.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = 10.0;\n' +
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  'precision mediump float;\n' +
  'uniform vec4 u_FragColor;\n' +  // uniform変数
  'void main() {\n' +
  '  gl_FragColor = u_FragColor;\n' +
  '}\n';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // // Get the storage location of a_Position
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
  }

  // Get the storage location of u_FragColor
  var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
  if (!u_FragColor) {
    console.log('Failed to get the storage location of u_FragColor');
    return;
  }

  // Register function (event handler) to be called on a mouse press
  canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position, u_FragColor) };

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);
}

var g_points = [];  // The array for the position of a mouse press
var g_colors = [];  // The array to store the color of a point
function click(ev, gl, canvas, a_Position, u_FragColor) {
  var x = ev.clientX; // x coordinate of a mouse pointer
  var y = ev.clientY; // y coordinate of a mouse pointer
  var rect = ev.target.getBoundingClientRect();

  x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
  y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);

  // Store the coordinates to g_points array
  g_points.push([x, y]);
  // Store the coordinates to g_points array
  if (x >= 0.0 && y >= 0.0) {      // First quadrant
    g_colors.push([1.0, 0.0, 0.0, 1.0]);  // Red
  } else if (x < 0.0 && y < 0.0) { // Third quadrant
    g_colors.push([0.0, 1.0, 0.0, 1.0]);  // Green
  } else {                         // Others
    g_colors.push([1.0, 1.0, 1.0, 1.0]);  // White
  }

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);

  var len = g_points.length;
  for(var i = 0; i < len; i++) {
    var xy = g_points[i];
    var rgba = g_colors[i];

    // Pass the position of a point to a_Position variable
    gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0);
    // Pass the color of a point to u_FragColor variable
    gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
    // Draw
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

效果不错!我不明白为什么 我的代码版本 不能像我预期的那样工作...

评论中的人说正确的方法是使用顶点属性并使用一次绘制调用绘制所有点是正确的,但由于您正在阅读一本书,我认为它会介绍如何以后做事更有效率。

现在,让我们看看您的问题。麻烦来了:

for(i = 0; i < points.length; i += 2){
   var _x = points[i];
   var _y = points[i+1];

   gl.vertexAttrib2f(a_Position, _x, _y);

   // Set color of point
   if (gl_x <= 0 && gl_y >= 0){
     ...
   ...
 }

您没有使用各个点(_x_y)的位置来决定颜色。您正在使用变量 gl_xgl_y,它们是在您的函数顶部通过最后一次单击点设置的。而这正是您所看到的!

避免出现此问题的一些一般性建议:将事件处理与绘图分开。也就是说,在点击处理程序的末尾,调用一个单独的函数(我们称之为 redraw()),它遍历数据并绘制所有点。这在复杂的程序(具有不止一种输入事件的程序)中是必不可少的,在这种特殊情况下,它会导致您因使用错误的变量而出错,因为 gl_x 是在点击中定义的处理函数,但不会在绘图函数中。