deblocker
deblocker

Reputation: 7687

gl readPixels coordinate system: read and check more than one point

I need to read a block of pixels from the picking buffer to check the surrounding points for collisions, like in the picture below:

enter image description here

I am clicking on my canvas at x, y - so I define a square region of, let say, 11x11 pixels, centered at cX, cY and then I read from the picking buffer just in one shot starting from x1, 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);

Then, i am looping from the center of my buffer walking to the outside, following the red arrows, while checking for a positive response inside the picking buffer (collision found):

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;
}

The piece of code is working and I am able to pick a geometry, but now I believe I haven't understand correctly the whole coordinate system, because the collision result is always above and left of the point where it should be. Why this happens?

It seems to me that readPixels should read the bytes starting from the bottom-left corner of the canvas, to the top-right corner. Is that true, or I am missing something?

I am aware that the glcoordinate system is Y-inverted against my canvas, so I am defining the center of the picking buffer at cY = cH - y and the Y starting point for the picking buffer at y1 = cY + rc, but anyway this is giving me wrong results during my walk from the center to the outside of my picking square box. Where goes my interpretation of the coordinate system wrong?

Upvotes: 1

Views: 481

Answers (1)

LJᛃ
LJᛃ

Reputation: 8123

It seems to me that readPixels should read the bytes starting from the bottom-left corner of the canvas, to the top-right corner. Is that true, or i am missing something?

Thats true

anyway this is giving me wrong results during my walk

With the exception of the middle pixel not being checked, it works for me, I suspect the issue to be elsewhere in your code.

// 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>

Upvotes: 1

Related Questions