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

时间:2015-02-17 13:38:04

标签: javascript opengl-es webgl shader

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

enter image description here

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

enter image description here

enter image description here

enter image description here

enter image description here

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

我的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);
  }
}

工作正常!我不明白为什么我的代码没有像我预期的那样工作......

1 个答案:

答案 0 :(得分:0)

评论中的人说正确的方法是使用顶点属性并使用一次绘制调用绘制所有点是正确的,但是因为你按照一本书我会假设它会介绍如何以后有效地做事。

现在,让我们来看看你的问题。这就是麻烦:

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处理函数但不在绘图函数中。