gl readPixels坐标系:读取并检查多个点

时间:2017-12-04 11:19:06

标签: html5-canvas webgl coordinate-systems glreadpixels

我需要从拾取缓冲区中读取一个像素块,以检查周围的点是否有碰撞,如下图所示:

enter image description here

我点击canvas上的xy - 所以我定义了一个方块区域,比如11x11像素,以cX为中心,{{ 1}}然后我从cYx1开始一次性从拣选缓冲区中读取:

y1

然后,我从缓冲区的中心向外循环,沿着红色箭头循环,同时检查拾取缓冲区内的正响应(发现碰撞):

var w = 11, h = 11, cX = x, cY = cH - y, rc = ~~(0.5*w), x1 = cX - rc, y1 = cY + rc;

gl.readPixels(x1, y1, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pickBuf);

这段代码正在工作,我能够选择一个几何体,但现在我相信我没有正确理解整个坐标系,因为碰撞结果总是在它应该的点的上方和左侧。为什么会这样?

在我看来,function readAt(i, pickBuf) { return pickBuf[i] << 16 | pickBuf[i+1] << 8 | pickBuf[i+2]; } var l = rc; while(l--) { /* Top-Left sector */ var r = rc+(rc-l), c = l, p = r*w + c, i = 4*p; found = readAt(i, pickBuf) > 0; /* Top-Right sector */ var r = rc+(rc-l), c = rc+(rc-l), p = r*w + c, i = 4*p; found = readAt(i, pickBuf) > 0; /* Bottom-Left sector */ var r = l, c = l, p = r*w + c, i = 4*p; found = readAt(i, pickBuf) > 0; /* Bottom-Right sector */ var r = l, c = rc+(rc-l), p = r*w + c, i = 4*p; found = readAt(i, pickBuf) > 0; } 应该读取从readPixels左下角开始到右上角的字节。是真的,还是我错过了什么?

我知道canvas坐标系对我的gl是Y倒置的,所以我在canvas定义了拣选缓冲区的中心,并且是Y的起点。在cY = cH - y选择缓冲区,但无论如何,这在我从中心走到我的采摘方框外面时给出了错误的结果。我对坐标系的解释在哪里错了?

1 个答案:

答案 0 :(得分:1)

  

在我看来,readPixels应该从中读取字节   画布的左下角,右上角。就是它   是的,还是我错过了什么?

多数是真的

  

无论如何,这在我走路时给了我错误的结果

除了未检查中间像素之外,它对我有效,我怀疑问题在您的代码中的其他位置。

// picking
c.addEventListener('click',function (e) {
  var
    cX = e.offsetX, // x within canvas
    cY = ctx.drawingBufferHeight - e.offsetY // y within canvas inverted, drawingbufferheight is same as canvas height 
  ;

  var
    area = 11,
    halfArea = (area/2)|0,
    sX = cX-halfArea,
    sY = cY-halfArea
  ;
  console.log('Picking from ', sX, sY, area,area);
  // read data
  var data = new Uint8Array(area*area*4);
  ctx.readPixels(sX,sY,area,area,ctx.RGBA,ctx.UNSIGNED_BYTE,data);
  // draw cross
  (function (pickBuf,rc,w) {
  function readAt(i, pickBuf) {
      return pickBuf[i]=pickBuf[i+1]=pickBuf[i+2]=0;
  }
  // this is your unmodified loop
  var l = rc;
  while(l--) { 
      /* Top-Left sector */
      var r = rc+(rc-l), c = l, p = r*w + c, i = 4*p;
      found = readAt(i, pickBuf) > 0;
      /* Top-Right sector */
      var r = rc+(rc-l), c = rc+(rc-l), p = r*w + c, i = 4*p;
      found = readAt(i, pickBuf) > 0;
      /* Bottom-Left sector */
      var r = l, c = l, p = r*w + c, i = 4*p;
      found = readAt(i, pickBuf) > 0;
      /* Bottom-Right sector */
      var r = l, c = rc+(rc-l), p = r*w + c, i = 4*p;
      found = readAt(i, pickBuf) > 0;
  }
  })(data,halfArea,area);

  // create preview
  var octx = document.createElement('canvas').getContext('2d');
  octx.canvas.classList.add('preview');
  octx.canvas.width = octx.canvas.height = area;
  var idata = octx.createImageData(area,area);
  idata.data.set(data);
  octx.putImageData(idata,0,0);
  ctx.canvas.parentElement.insertBefore(octx.canvas,ctx.canvas);
});


// context setup
var ctx = c.getContext('webgl',{preserveDrawingBuffer:true});
ctx.canvas.width = ctx.canvas.height = 256;
ctx.viewport(0,0,256,256);
// shader setup
var vs=ctx.createShader(ctx.VERTEX_SHADER),fs=ctx.createShader(ctx.FRAGMENT_SHADER),prg=ctx.createProgram();
ctx.shaderSource(vs,`
  attribute vec2 vPos;
  void main(){gl_Position=vec4(vPos,0,1);}
`);
ctx.shaderSource(fs,`
  precision highp float;
  void main(){
  vec2 p=(gl_FragCoord.xy/256.)*2.-1.;
  gl_FragColor=vec4(sign(p),sign(-p.r),1);
  }
`);
ctx.compileShader(vs);
ctx.compileShader(fs);
ctx.attachShader(prg,vs);
ctx.attachShader(prg,fs);
ctx.linkProgram(prg);
// screenspace setup
var ssq = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER,ssq);
ctx.bufferData(ctx.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,1,1,-1,1]),ctx.STATIC_DRAW);
ctx.vertexAttribPointer(0,2,ctx.FLOAT,false,0,0);
ctx.enableVertexAttribArray(0);
// draw
ctx.useProgram(prg);
ctx.drawArrays(ctx.TRIANGLE_FAN,0,4);
h2{text-align:center;font-family: Arial,sans-serif;}
#c{display:block;margin:0 auto;outline:1px solid red;cursor:crosshair;}

.preview {
width: 64px;
height: 64px;
outline: none;
display:inline;
image-rendering: pixelated;
transform:scaleY(-1);
margin: 10px;
}
<h2>Click 2 Pick</h2>
<canvas id="c"></canvas>